mirror of
https://github.com/cagnulein/qdomyos-zwift.git
synced 2026-02-18 00:17:41 +01:00
Compare commits
3 Commits
build-1206
...
qthttpserv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1bf9c227d | ||
|
|
73e5e8164e | ||
|
|
35230d3d42 |
@@ -0,0 +1,8 @@
|
||||
/* This file was generated by qmake with the info from <root>/src/httpserver/httpserver.pro. */
|
||||
#ifdef __cplusplus /* create empty PCH in C mode */
|
||||
#include <QtNetwork/QtNetwork>
|
||||
#include <QtCore/QtCore>
|
||||
#include <QtWebSockets/QtWebSockets>
|
||||
#include <QtSslServer/QtSslServer>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
/* This file was generated by qmake with the info from <root>/src/sslserver/sslserver.pro. */
|
||||
#ifdef __cplusplus /* create empty PCH in C mode */
|
||||
#include <QtNetwork/QtNetwork>
|
||||
#include <QtCore/QtCore>
|
||||
#endif
|
||||
@@ -0,0 +1,252 @@
|
||||
if (CMAKE_VERSION VERSION_LESS 3.1.0)
|
||||
message(FATAL_ERROR "Qt 5 SslServer module requires at least CMake version 3.1.0")
|
||||
endif()
|
||||
|
||||
get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||
# Use original install prefix when loaded through a
|
||||
# cross-prefix symbolic link such as /lib -> /usr/lib.
|
||||
get_filename_component(_realCurr "${_IMPORT_PREFIX}" REALPATH)
|
||||
get_filename_component(_realOrig "/usr/lib/arm-linux-gnueabihf/cmake/Qt5SslServer" REALPATH)
|
||||
if(_realCurr STREQUAL _realOrig)
|
||||
get_filename_component(_qt5SslServer_install_prefix "/usr/lib/arm-linux-gnueabihf/../../" ABSOLUTE)
|
||||
else()
|
||||
get_filename_component(_qt5SslServer_install_prefix "${CMAKE_CURRENT_LIST_DIR}/../../../../" ABSOLUTE)
|
||||
endif()
|
||||
unset(_realOrig)
|
||||
unset(_realCurr)
|
||||
unset(_IMPORT_PREFIX)
|
||||
|
||||
# For backwards compatibility only. Use Qt5SslServer_VERSION instead.
|
||||
set(Qt5SslServer_VERSION_STRING 5.12.0)
|
||||
|
||||
set(Qt5SslServer_LIBRARIES Qt5::SslServer)
|
||||
|
||||
macro(_qt5_SslServer_check_file_exists file)
|
||||
if(NOT EXISTS "${file}" )
|
||||
message(FATAL_ERROR "The imported target \"Qt5::SslServer\" references the file
|
||||
\"${file}\"
|
||||
but this file does not exist. Possible reasons include:
|
||||
* The file was deleted, renamed, or moved to another location.
|
||||
* An install or uninstall procedure did not complete successfully.
|
||||
* The installation package was faulty and contained
|
||||
\"${CMAKE_CURRENT_LIST_FILE}\"
|
||||
but not all the files it references.
|
||||
")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
|
||||
macro(_populate_SslServer_target_properties Configuration LIB_LOCATION IMPLIB_LOCATION
|
||||
IsDebugAndRelease)
|
||||
set_property(TARGET Qt5::SslServer APPEND PROPERTY IMPORTED_CONFIGURATIONS ${Configuration})
|
||||
|
||||
set(imported_location "${_qt5SslServer_install_prefix}/lib/arm-linux-gnueabihf/${LIB_LOCATION}")
|
||||
_qt5_SslServer_check_file_exists(${imported_location})
|
||||
set(_deps
|
||||
${_Qt5SslServer_LIB_DEPENDENCIES}
|
||||
)
|
||||
set(_static_deps
|
||||
)
|
||||
|
||||
set_target_properties(Qt5::SslServer PROPERTIES
|
||||
"IMPORTED_LOCATION_${Configuration}" ${imported_location}
|
||||
"IMPORTED_SONAME_${Configuration}" "libQt5SslServer.so.5"
|
||||
# For backward compatibility with CMake < 2.8.12
|
||||
"IMPORTED_LINK_INTERFACE_LIBRARIES_${Configuration}" "${_deps};${_static_deps}"
|
||||
)
|
||||
set_property(TARGET Qt5::SslServer APPEND PROPERTY INTERFACE_LINK_LIBRARIES
|
||||
"${_deps}"
|
||||
)
|
||||
|
||||
|
||||
endmacro()
|
||||
|
||||
if (NOT TARGET Qt5::SslServer)
|
||||
|
||||
set(_Qt5SslServer_OWN_INCLUDE_DIRS "${_qt5SslServer_install_prefix}/include/arm-linux-gnueabihf/qt5/" "${_qt5SslServer_install_prefix}/include/arm-linux-gnueabihf/qt5/QtSslServer")
|
||||
set(Qt5SslServer_PRIVATE_INCLUDE_DIRS "")
|
||||
|
||||
foreach(_dir ${_Qt5SslServer_OWN_INCLUDE_DIRS})
|
||||
_qt5_SslServer_check_file_exists(${_dir})
|
||||
endforeach()
|
||||
|
||||
# Only check existence of private includes if the Private component is
|
||||
# specified.
|
||||
list(FIND Qt5SslServer_FIND_COMPONENTS Private _check_private)
|
||||
if (NOT _check_private STREQUAL -1)
|
||||
foreach(_dir ${Qt5SslServer_PRIVATE_INCLUDE_DIRS})
|
||||
_qt5_SslServer_check_file_exists(${_dir})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
set(Qt5SslServer_INCLUDE_DIRS ${_Qt5SslServer_OWN_INCLUDE_DIRS})
|
||||
|
||||
set(Qt5SslServer_DEFINITIONS -DQT_SSLSERVER_LIB)
|
||||
set(Qt5SslServer_COMPILE_DEFINITIONS QT_SSLSERVER_LIB)
|
||||
set(_Qt5SslServer_MODULE_DEPENDENCIES "Network;Core")
|
||||
|
||||
|
||||
set(Qt5SslServer_OWN_PRIVATE_INCLUDE_DIRS ${Qt5SslServer_PRIVATE_INCLUDE_DIRS})
|
||||
|
||||
set(_Qt5SslServer_FIND_DEPENDENCIES_REQUIRED)
|
||||
if (Qt5SslServer_FIND_REQUIRED)
|
||||
set(_Qt5SslServer_FIND_DEPENDENCIES_REQUIRED REQUIRED)
|
||||
endif()
|
||||
set(_Qt5SslServer_FIND_DEPENDENCIES_QUIET)
|
||||
if (Qt5SslServer_FIND_QUIETLY)
|
||||
set(_Qt5SslServer_DEPENDENCIES_FIND_QUIET QUIET)
|
||||
endif()
|
||||
set(_Qt5SslServer_FIND_VERSION_EXACT)
|
||||
if (Qt5SslServer_FIND_VERSION_EXACT)
|
||||
set(_Qt5SslServer_FIND_VERSION_EXACT EXACT)
|
||||
endif()
|
||||
|
||||
set(Qt5SslServer_EXECUTABLE_COMPILE_FLAGS "")
|
||||
|
||||
foreach(_module_dep ${_Qt5SslServer_MODULE_DEPENDENCIES})
|
||||
if (NOT Qt5${_module_dep}_FOUND)
|
||||
find_package(Qt5${_module_dep}
|
||||
5.12.0 ${_Qt5SslServer_FIND_VERSION_EXACT}
|
||||
${_Qt5SslServer_DEPENDENCIES_FIND_QUIET}
|
||||
${_Qt5SslServer_FIND_DEPENDENCIES_REQUIRED}
|
||||
PATHS "${CMAKE_CURRENT_LIST_DIR}/.." NO_DEFAULT_PATH
|
||||
)
|
||||
endif()
|
||||
|
||||
if (NOT Qt5${_module_dep}_FOUND)
|
||||
set(Qt5SslServer_FOUND False)
|
||||
return()
|
||||
endif()
|
||||
|
||||
list(APPEND Qt5SslServer_INCLUDE_DIRS "${Qt5${_module_dep}_INCLUDE_DIRS}")
|
||||
list(APPEND Qt5SslServer_PRIVATE_INCLUDE_DIRS "${Qt5${_module_dep}_PRIVATE_INCLUDE_DIRS}")
|
||||
list(APPEND Qt5SslServer_DEFINITIONS ${Qt5${_module_dep}_DEFINITIONS})
|
||||
list(APPEND Qt5SslServer_COMPILE_DEFINITIONS ${Qt5${_module_dep}_COMPILE_DEFINITIONS})
|
||||
list(APPEND Qt5SslServer_EXECUTABLE_COMPILE_FLAGS ${Qt5${_module_dep}_EXECUTABLE_COMPILE_FLAGS})
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES Qt5SslServer_INCLUDE_DIRS)
|
||||
list(REMOVE_DUPLICATES Qt5SslServer_PRIVATE_INCLUDE_DIRS)
|
||||
list(REMOVE_DUPLICATES Qt5SslServer_DEFINITIONS)
|
||||
list(REMOVE_DUPLICATES Qt5SslServer_COMPILE_DEFINITIONS)
|
||||
list(REMOVE_DUPLICATES Qt5SslServer_EXECUTABLE_COMPILE_FLAGS)
|
||||
|
||||
# It can happen that the same FooConfig.cmake file is included when calling find_package()
|
||||
# on some Qt component. An example of that is when using a Qt static build with auto inclusion
|
||||
# of plugins:
|
||||
#
|
||||
# Qt5WidgetsConfig.cmake -> Qt5GuiConfig.cmake -> Qt5Gui_QSvgIconPlugin.cmake ->
|
||||
# Qt5SvgConfig.cmake -> Qt5WidgetsConfig.cmake ->
|
||||
# finish processing of second Qt5WidgetsConfig.cmake ->
|
||||
# return to first Qt5WidgetsConfig.cmake ->
|
||||
# add_library cannot create imported target Qt5::Widgets.
|
||||
#
|
||||
# Make sure to return early in the original Config inclusion, because the target has already
|
||||
# been defined as part of the second inclusion.
|
||||
if(TARGET Qt5::SslServer)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(_Qt5SslServer_LIB_DEPENDENCIES "Qt5::Network;Qt5::Core")
|
||||
|
||||
|
||||
add_library(Qt5::SslServer SHARED IMPORTED)
|
||||
|
||||
|
||||
set_property(TARGET Qt5::SslServer PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${_Qt5SslServer_OWN_INCLUDE_DIRS})
|
||||
set_property(TARGET Qt5::SslServer PROPERTY
|
||||
INTERFACE_COMPILE_DEFINITIONS QT_SSLSERVER_LIB)
|
||||
|
||||
set_property(TARGET Qt5::SslServer PROPERTY INTERFACE_QT_ENABLED_FEATURES )
|
||||
set_property(TARGET Qt5::SslServer PROPERTY INTERFACE_QT_DISABLED_FEATURES )
|
||||
|
||||
# Qt 6 forward compatible properties.
|
||||
set_property(TARGET Qt5::SslServer
|
||||
PROPERTY QT_ENABLED_PUBLIC_FEATURES
|
||||
)
|
||||
set_property(TARGET Qt5::SslServer
|
||||
PROPERTY QT_DISABLED_PUBLIC_FEATURES
|
||||
)
|
||||
set_property(TARGET Qt5::SslServer
|
||||
PROPERTY QT_ENABLED_PRIVATE_FEATURES
|
||||
)
|
||||
set_property(TARGET Qt5::SslServer
|
||||
PROPERTY QT_DISABLED_PRIVATE_FEATURES
|
||||
)
|
||||
|
||||
set_property(TARGET Qt5::SslServer PROPERTY INTERFACE_QT_PLUGIN_TYPES "")
|
||||
|
||||
set(_Qt5SslServer_PRIVATE_DIRS_EXIST TRUE)
|
||||
foreach (_Qt5SslServer_PRIVATE_DIR ${Qt5SslServer_OWN_PRIVATE_INCLUDE_DIRS})
|
||||
if (NOT EXISTS ${_Qt5SslServer_PRIVATE_DIR})
|
||||
set(_Qt5SslServer_PRIVATE_DIRS_EXIST FALSE)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if (_Qt5SslServer_PRIVATE_DIRS_EXIST)
|
||||
add_library(Qt5::SslServerPrivate INTERFACE IMPORTED)
|
||||
set_property(TARGET Qt5::SslServerPrivate PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${Qt5SslServer_OWN_PRIVATE_INCLUDE_DIRS}
|
||||
)
|
||||
set(_Qt5SslServer_PRIVATEDEPS)
|
||||
foreach(dep ${_Qt5SslServer_LIB_DEPENDENCIES})
|
||||
if (TARGET ${dep}Private)
|
||||
list(APPEND _Qt5SslServer_PRIVATEDEPS ${dep}Private)
|
||||
endif()
|
||||
endforeach()
|
||||
set_property(TARGET Qt5::SslServerPrivate PROPERTY
|
||||
INTERFACE_LINK_LIBRARIES Qt5::SslServer ${_Qt5SslServer_PRIVATEDEPS}
|
||||
)
|
||||
|
||||
# Add a versionless target, for compatibility with Qt6.
|
||||
if(NOT "${QT_NO_CREATE_VERSIONLESS_TARGETS}" AND NOT TARGET Qt::SslServerPrivate)
|
||||
add_library(Qt::SslServerPrivate INTERFACE IMPORTED)
|
||||
set_target_properties(Qt::SslServerPrivate PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES "Qt5::SslServerPrivate"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
_populate_SslServer_target_properties(RELEASE "libQt5SslServer.so.5.12.0" "" FALSE)
|
||||
|
||||
|
||||
|
||||
|
||||
# In Qt 5.15 the glob pattern was relaxed to also catch plugins not literally named Plugin.
|
||||
# Define QT5_STRICT_PLUGIN_GLOB or ModuleName_STRICT_PLUGIN_GLOB to revert to old behavior.
|
||||
if (QT5_STRICT_PLUGIN_GLOB OR Qt5SslServer_STRICT_PLUGIN_GLOB)
|
||||
file(GLOB pluginTargets "${CMAKE_CURRENT_LIST_DIR}/Qt5SslServer_*Plugin.cmake")
|
||||
else()
|
||||
file(GLOB pluginTargets "${CMAKE_CURRENT_LIST_DIR}/Qt5SslServer_*.cmake")
|
||||
endif()
|
||||
|
||||
macro(_populate_SslServer_plugin_properties Plugin Configuration PLUGIN_LOCATION
|
||||
IsDebugAndRelease)
|
||||
set_property(TARGET Qt5::${Plugin} APPEND PROPERTY IMPORTED_CONFIGURATIONS ${Configuration})
|
||||
|
||||
set(imported_location "${_qt5SslServer_install_prefix}/lib/arm-linux-gnueabihf/qt5/plugins/${PLUGIN_LOCATION}")
|
||||
_qt5_SslServer_check_file_exists(${imported_location})
|
||||
set_target_properties(Qt5::${Plugin} PROPERTIES
|
||||
"IMPORTED_LOCATION_${Configuration}" ${imported_location}
|
||||
)
|
||||
|
||||
endmacro()
|
||||
|
||||
if (pluginTargets)
|
||||
foreach(pluginTarget ${pluginTargets})
|
||||
include(${pluginTarget})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
_qt5_SslServer_check_file_exists("${CMAKE_CURRENT_LIST_DIR}/Qt5SslServerConfigVersion.cmake")
|
||||
endif()
|
||||
|
||||
# Add a versionless target, for compatibility with Qt6.
|
||||
if(NOT "${QT_NO_CREATE_VERSIONLESS_TARGETS}" AND TARGET Qt5::SslServer AND NOT TARGET Qt::SslServer)
|
||||
add_library(Qt::SslServer INTERFACE IMPORTED)
|
||||
set_target_properties(Qt::SslServer PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES "Qt5::SslServer"
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
set(PACKAGE_VERSION 5.12.0)
|
||||
|
||||
if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
|
||||
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
||||
else()
|
||||
set(PACKAGE_VERSION_COMPATIBLE TRUE)
|
||||
if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
|
||||
set(PACKAGE_VERSION_EXACT TRUE)
|
||||
endif()
|
||||
endif()
|
||||
28
qHttpServerBin/5.15.2/raspberry/lib/libQt5SslServer.la
Normal file
28
qHttpServerBin/5.15.2/raspberry/lib/libQt5SslServer.la
Normal file
@@ -0,0 +1,28 @@
|
||||
# libQt5SslServer.la - a libtool library file
|
||||
# Generated by qmake/libtool (3.1) (Qt 5.15.2)
|
||||
# The name that we can dlopen(3).
|
||||
dlname='libQt5SslServer.so.5'
|
||||
|
||||
# Names of this library.
|
||||
library_names='libQt5SslServer.so.5.12.0 libQt5SslServer.so.5 libQt5SslServer.so'
|
||||
|
||||
# The name of the static archive.
|
||||
old_library='libQt5SslServer.a'
|
||||
|
||||
# Libraries that this one depends upon.
|
||||
dependency_libs='-L/usr/lib/arm-linux-gnueabihf -lQt5Network -lQt5Core -lpthread -latomic'
|
||||
|
||||
# Version information for libQt5SslServer.la
|
||||
current=62
|
||||
age=0
|
||||
revision=0
|
||||
|
||||
# Is this an already installed library.
|
||||
installed=yes
|
||||
|
||||
# Files to dlopen/dlpreopen.
|
||||
dlopen=''
|
||||
dlpreopen=''
|
||||
|
||||
# Directory that this library needs to be installed in:
|
||||
libdir='=/usr/lib/arm-linux-gnueabihf'
|
||||
7
qHttpServerBin/5.15.2/raspberry/lib/libQt5SslServer.prl
Normal file
7
qHttpServerBin/5.15.2/raspberry/lib/libQt5SslServer.prl
Normal file
@@ -0,0 +1,7 @@
|
||||
QMAKE_PRL_BUILD_DIR = /home/pi/qdomyos-zwift/src/qthttpserver/src/sslserver
|
||||
QMAKE_PRO_INPUT = sslserver.pro
|
||||
QMAKE_PRL_TARGET = libQt5SslServer.so.5.12.0
|
||||
QMAKE_PRL_CONFIG = lex yacc depend_includepath testcase_targets import_plugins import_qpa_plugin prepare_docs qt_docs_targets qt_build_extra file_copies qmake_use qt warn_on release link_prl incremental shared shared release linux unix posix gcc compile_examples enable_new_dtags largefile nostrip prefix_build force_independent utf8_source create_prl link_prl no_private_qt_headers_warning QTDIR_build qt_example_installs exceptions_off testcase_exceptions explicitlib relative_qt_rpath target_qt c++11 strict_c++ c++14 c++1z c99 c11 hide_symbols qt_install_headers need_fwd_pri qt_install_module create_cmake compiler_supports_fpmath create_pc create_libtool have_target dll thread moc resources
|
||||
QMAKE_PRL_VERSION = 5.12.0
|
||||
QMAKE_PRL_LIBS = /usr/lib/arm-linux-gnueabihf/libQt5Network.so /usr/lib/arm-linux-gnueabihf/libQt5Core.so -lpthread -latomic
|
||||
QMAKE_PRL_LIBS_FOR_CMAKE = /usr/lib/arm-linux-gnueabihf/libQt5Network.so;/usr/lib/arm-linux-gnueabihf/libQt5Core.so;-lpthread;-latomic;
|
||||
1
qHttpServerBin/5.15.2/raspberry/lib/libqhttpserver.so
Symbolic link
1
qHttpServerBin/5.15.2/raspberry/lib/libqhttpserver.so
Symbolic link
@@ -0,0 +1 @@
|
||||
libqhttpserver.so.0.1.0
|
||||
1
qHttpServerBin/5.15.2/raspberry/lib/libqhttpserver.so.0
Symbolic link
1
qHttpServerBin/5.15.2/raspberry/lib/libqhttpserver.so.0
Symbolic link
@@ -0,0 +1 @@
|
||||
libqhttpserver.so.0.1.0
|
||||
1
qHttpServerBin/5.15.2/raspberry/lib/libqhttpserver.so.0.1
Symbolic link
1
qHttpServerBin/5.15.2/raspberry/lib/libqhttpserver.so.0.1
Symbolic link
@@ -0,0 +1 @@
|
||||
libqhttpserver.so.0.1.0
|
||||
BIN
qHttpServerBin/5.15.2/raspberry/lib/libqhttpserver.so.0.1.0
Normal file
BIN
qHttpServerBin/5.15.2/raspberry/lib/libqhttpserver.so.0.1.0
Normal file
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
prefix=/usr
|
||||
exec_prefix=${prefix}
|
||||
libdir=${prefix}/lib/arm-linux-gnueabihf
|
||||
includedir=${prefix}/include/arm-linux-gnueabihf/qt5
|
||||
|
||||
|
||||
Name: Qt5 SslServer
|
||||
Description: Qt SslServer module
|
||||
Version: 5.12.0
|
||||
Libs: -lQt5SslServer
|
||||
Cflags: -DQT_SSLSERVER_LIB -I${includedir}/QtSslServer -I${includedir}
|
||||
Requires: Qt5Network Qt5Core
|
||||
|
||||
3
src/qthttpserver/.qmake.conf
Normal file
3
src/qthttpserver/.qmake.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
load(qt_build_config)
|
||||
|
||||
MODULE_VERSION = 5.12.0
|
||||
15
src/qthttpserver/CMakeLists.txt
Normal file
15
src/qthttpserver/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
# Generated from qthttpserver.pro.
|
||||
|
||||
cmake_minimum_required(VERSION 3.15.0)
|
||||
|
||||
project(QtHttpServer
|
||||
VERSION 6.0.0
|
||||
DESCRIPTION "Qt HTTP Server"
|
||||
HOMEPAGE_URL "https://qt.io/"
|
||||
LANGUAGES CXX C
|
||||
)
|
||||
|
||||
find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core Network)
|
||||
find_package(Qt6 ${PROJECT_VERSION} CONFIG OPTIONAL_COMPONENTS Concurrent)
|
||||
|
||||
qt_build_repo()
|
||||
674
src/qthttpserver/LICENSE.GPL3
Normal file
674
src/qthttpserver/LICENSE.GPL3
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
3
src/qthttpserver/coin/product_dependencies.yaml
Normal file
3
src/qthttpserver/coin/product_dependencies.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
../../qt/qt5.git:
|
||||
ref: "5.12"
|
||||
4
src/qthttpserver/dependencies.yaml
Normal file
4
src/qthttpserver/dependencies.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
../../qt/qtbase:
|
||||
ref: "7d7afe1d2b1d85ea5221d1d2883c8bf80aa0e927"
|
||||
required: true
|
||||
7
src/qthttpserver/examples/CMakeLists.txt
Normal file
7
src/qthttpserver/examples/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
# Generated from examples.pro.
|
||||
|
||||
qt_examples_build_begin()
|
||||
|
||||
add_subdirectory(httpserver)
|
||||
|
||||
qt_examples_build_end()
|
||||
5
src/qthttpserver/examples/examples.pro
Normal file
5
src/qthttpserver/examples/examples.pro
Normal file
@@ -0,0 +1,5 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS += \
|
||||
helloworld\
|
||||
greeting\
|
||||
bodydata\
|
||||
4
src/qthttpserver/examples/httpserver/CMakeLists.txt
Normal file
4
src/qthttpserver/examples/httpserver/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
# Generated from httpserver.pro.
|
||||
|
||||
add_subdirectory(afterrequest)
|
||||
add_subdirectory(simple)
|
||||
@@ -0,0 +1,27 @@
|
||||
# Generated from afterrequest.pro.
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(afterrequest LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "examples/httpserver/afterrequest")
|
||||
|
||||
find_package(Qt6 COMPONENTS HttpServer)
|
||||
|
||||
add_executable(afterrequest
|
||||
main.cpp
|
||||
)
|
||||
target_link_libraries(afterrequest PUBLIC
|
||||
Qt::HttpServer
|
||||
)
|
||||
|
||||
install(TARGETS afterrequest
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
requires(qtHaveModule(httpserver))
|
||||
|
||||
TEMPLATE = app
|
||||
|
||||
QT = httpserver
|
||||
|
||||
SOURCES += \
|
||||
main.cpp
|
||||
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/httpserver/afterrequest
|
||||
INSTALLS += target
|
||||
|
||||
CONFIG += cmdline
|
||||
80
src/qthttpserver/examples/httpserver/afterrequest/main.cpp
Normal file
80
src/qthttpserver/examples/httpserver/afterrequest/main.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Mikhail Svetkin <mikhail.svetkin@gmail.com>
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** BSD License Usage
|
||||
** Alternatively, you may use this file under the terms of the BSD license
|
||||
** as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of The Qt Company Ltd nor the names of its
|
||||
** contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtCore>
|
||||
#include <QtHttpServer>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
QHttpServer httpServer;
|
||||
httpServer.route("/", []() {
|
||||
return "Hello world";
|
||||
});
|
||||
|
||||
httpServer.afterRequest([](QHttpServerResponse &&resp) {
|
||||
resp.setHeader("Server", "Super server!");
|
||||
return std::move(resp);
|
||||
});
|
||||
|
||||
const auto port = httpServer.listen(QHostAddress::Any);
|
||||
if (!port) {
|
||||
qDebug() << QCoreApplication::translate(
|
||||
"QHttpServerExample", "Server failed to listen on a port.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
qDebug() << QCoreApplication::translate(
|
||||
"QHttpServerExample", "Running on http://127.0.0.1:%1/ (Press CTRL+C to quit)").arg(port);
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
6
src/qthttpserver/examples/httpserver/httpserver.pro
Normal file
6
src/qthttpserver/examples/httpserver/httpserver.pro
Normal file
@@ -0,0 +1,6 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = \
|
||||
afterrequest \
|
||||
simple
|
||||
|
||||
40
src/qthttpserver/examples/httpserver/simple/CMakeLists.txt
Normal file
40
src/qthttpserver/examples/httpserver/simple/CMakeLists.txt
Normal file
@@ -0,0 +1,40 @@
|
||||
# Generated from simple.pro.
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(simple LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "examples/httpserver/simple")
|
||||
|
||||
find_package(Qt6 COMPONENTS HttpServer)
|
||||
|
||||
add_executable(simple
|
||||
main.cpp
|
||||
)
|
||||
target_link_libraries(simple PUBLIC
|
||||
Qt::HttpServer
|
||||
)
|
||||
|
||||
|
||||
# Resources:
|
||||
set(assets_resource_files
|
||||
"assets/qt-logo.png"
|
||||
)
|
||||
|
||||
qt6_add_resources(simple "assets"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
${assets_resource_files}
|
||||
)
|
||||
|
||||
install(TARGETS simple
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
||||
5
src/qthttpserver/examples/httpserver/simple/assets.qrc
Normal file
5
src/qthttpserver/examples/httpserver/simple/assets.qrc
Normal file
@@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>assets/qt-logo.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
BIN
src/qthttpserver/examples/httpserver/simple/assets/qt-logo.png
Normal file
BIN
src/qthttpserver/examples/httpserver/simple/assets/qt-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
126
src/qthttpserver/examples/httpserver/simple/main.cpp
Normal file
126
src/qthttpserver/examples/httpserver/simple/main.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** BSD License Usage
|
||||
** Alternatively, you may use this file under the terms of the BSD license
|
||||
** as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of The Qt Company Ltd nor the names of its
|
||||
** contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtCore>
|
||||
#include <QtHttpServer>
|
||||
|
||||
static inline QString host(const QHttpServerRequest &request)
|
||||
{
|
||||
return request.headers()[QStringLiteral("Host")].toString();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
QHttpServer httpServer;
|
||||
httpServer.route("/", []() {
|
||||
return "Hello world";
|
||||
});
|
||||
|
||||
httpServer.route("/query", [] (const QHttpServerRequest &request) {
|
||||
return QString("%1/query/").arg(host(request));
|
||||
});
|
||||
|
||||
httpServer.route("/query/", [] (qint32 id, const QHttpServerRequest &request) {
|
||||
return QString("%1/query/%2").arg(host(request)).arg(id);
|
||||
});
|
||||
|
||||
httpServer.route("/query/<arg>/log", [] (qint32 id, const QHttpServerRequest &request) {
|
||||
return QString("%1/query/%2/log").arg(host(request)).arg(id);
|
||||
});
|
||||
|
||||
httpServer.route("/query/<arg>/log/", [] (qint32 id, float threshold,
|
||||
const QHttpServerRequest &request) {
|
||||
return QString("%1/query/%2/log/%3").arg(host(request)).arg(id).arg(threshold);
|
||||
});
|
||||
|
||||
httpServer.route("/user/", [] (const qint32 id) {
|
||||
return QString("User %1").arg(id);
|
||||
});
|
||||
|
||||
httpServer.route("/user/<arg>/detail", [] (const qint32 id) {
|
||||
return QString("User %1 detail").arg(id);
|
||||
});
|
||||
|
||||
httpServer.route("/user/<arg>/detail/", [] (const qint32 id, const qint32 year) {
|
||||
return QString("User %1 detail year - %2").arg(id).arg(year);
|
||||
});
|
||||
|
||||
httpServer.route("/json/", [] {
|
||||
return QJsonObject{
|
||||
{
|
||||
{"key1", "1"},
|
||||
{"key2", "2"},
|
||||
{"key3", "3"}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
httpServer.route("/assets/<arg>", [] (const QUrl &url) {
|
||||
return QHttpServerResponse::fromFile(QStringLiteral(":/assets/%1").arg(url.path()));
|
||||
});
|
||||
|
||||
httpServer.route("/remote_address", [](const QHttpServerRequest &request) {
|
||||
return request.remoteAddress().toString();
|
||||
});
|
||||
|
||||
const auto port = httpServer.listen(QHostAddress::Any);
|
||||
if (!port) {
|
||||
qDebug() << QCoreApplication::translate(
|
||||
"QHttpServerExample", "Server failed to listen on a port.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
qDebug() << QCoreApplication::translate(
|
||||
"QHttpServerExample", "Running on http://127.0.0.1:%1/ (Press CTRL+C to quit)").arg(port);
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
16
src/qthttpserver/examples/httpserver/simple/simple.pro
Normal file
16
src/qthttpserver/examples/httpserver/simple/simple.pro
Normal file
@@ -0,0 +1,16 @@
|
||||
requires(qtHaveModule(httpserver))
|
||||
|
||||
TEMPLATE = app
|
||||
|
||||
QT = httpserver
|
||||
|
||||
SOURCES += \
|
||||
main.cpp
|
||||
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/httpserver/simple
|
||||
INSTALLS += target
|
||||
|
||||
RESOURCES += \
|
||||
assets.qrc
|
||||
|
||||
CONFIG += cmdline
|
||||
1
src/qthttpserver/qthttpserver.pro
Normal file
1
src/qthttpserver/qthttpserver.pro
Normal file
@@ -0,0 +1 @@
|
||||
load(qt_parts)
|
||||
6
src/qthttpserver/src/3rdparty/http-parser.pri
vendored
Normal file
6
src/qthttpserver/src/3rdparty/http-parser.pri
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
NODEJS_HTTP_PARSER_PATH = $$PWD/http-parser
|
||||
|
||||
INCLUDEPATH += $$NODEJS_HTTP_PARSER_PATH
|
||||
|
||||
HEADERS += $$NODEJS_HTTP_PARSER_PATH/http_parser.h
|
||||
SOURCES += $$NODEJS_HTTP_PARSER_PATH/http_parser.c
|
||||
2575
src/qthttpserver/src/3rdparty/http-parser/http_parser.c
vendored
Normal file
2575
src/qthttpserver/src/3rdparty/http-parser/http_parser.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
449
src/qthttpserver/src/3rdparty/http-parser/http_parser.h
vendored
Normal file
449
src/qthttpserver/src/3rdparty/http-parser/http_parser.h
vendored
Normal file
@@ -0,0 +1,449 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef http_parser_h
|
||||
#define http_parser_h
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Also update SONAME in the Makefile whenever you change these. */
|
||||
#define HTTP_PARSER_VERSION_MAJOR 2
|
||||
#define HTTP_PARSER_VERSION_MINOR 9
|
||||
#define HTTP_PARSER_VERSION_PATCH 4
|
||||
|
||||
#include <stddef.h>
|
||||
#if defined(_WIN32) && !defined(__MINGW32__) && \
|
||||
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
|
||||
#include <BaseTsd.h>
|
||||
typedef __int8 int8_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#elif (defined(__sun) || defined(__sun__)) && defined(__SunOS_5_9)
|
||||
#include <sys/inttypes.h>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
|
||||
* faster
|
||||
*/
|
||||
#ifndef HTTP_PARSER_STRICT
|
||||
# define HTTP_PARSER_STRICT 1
|
||||
#endif
|
||||
|
||||
/* Maximium header size allowed. If the macro is not defined
|
||||
* before including this header then the default is used. To
|
||||
* change the maximum header size, define the macro in the build
|
||||
* environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
|
||||
* the effective limit on the size of the header, define the macro
|
||||
* to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
|
||||
*/
|
||||
#ifndef HTTP_MAX_HEADER_SIZE
|
||||
# define HTTP_MAX_HEADER_SIZE (80*1024)
|
||||
#endif
|
||||
|
||||
typedef struct http_parser http_parser;
|
||||
typedef struct http_parser_settings http_parser_settings;
|
||||
|
||||
|
||||
/* Callbacks should return non-zero to indicate an error. The parser will
|
||||
* then halt execution.
|
||||
*
|
||||
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
|
||||
* returning '1' from on_headers_complete will tell the parser that it
|
||||
* should not expect a body. This is used when receiving a response to a
|
||||
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
|
||||
* chunked' headers that indicate the presence of a body.
|
||||
*
|
||||
* Returning `2` from on_headers_complete will tell parser that it should not
|
||||
* expect neither a body nor any futher responses on this connection. This is
|
||||
* useful for handling responses to a CONNECT request which may not contain
|
||||
* `Upgrade` or `Connection: upgrade` headers.
|
||||
*
|
||||
* http_data_cb does not return data chunks. It will be called arbitrarily
|
||||
* many times for each string. E.G. you might get 10 callbacks for "on_url"
|
||||
* each providing just a few characters more data.
|
||||
*/
|
||||
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
|
||||
typedef int (*http_cb) (http_parser*);
|
||||
|
||||
|
||||
/* Status Codes */
|
||||
#define HTTP_STATUS_MAP(XX) \
|
||||
XX(100, CONTINUE, Continue) \
|
||||
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
|
||||
XX(102, PROCESSING, Processing) \
|
||||
XX(200, OK, OK) \
|
||||
XX(201, CREATED, Created) \
|
||||
XX(202, ACCEPTED, Accepted) \
|
||||
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
|
||||
XX(204, NO_CONTENT, No Content) \
|
||||
XX(205, RESET_CONTENT, Reset Content) \
|
||||
XX(206, PARTIAL_CONTENT, Partial Content) \
|
||||
XX(207, MULTI_STATUS, Multi-Status) \
|
||||
XX(208, ALREADY_REPORTED, Already Reported) \
|
||||
XX(226, IM_USED, IM Used) \
|
||||
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
|
||||
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
|
||||
XX(302, FOUND, Found) \
|
||||
XX(303, SEE_OTHER, See Other) \
|
||||
XX(304, NOT_MODIFIED, Not Modified) \
|
||||
XX(305, USE_PROXY, Use Proxy) \
|
||||
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
|
||||
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
|
||||
XX(400, BAD_REQUEST, Bad Request) \
|
||||
XX(401, UNAUTHORIZED, Unauthorized) \
|
||||
XX(402, PAYMENT_REQUIRED, Payment Required) \
|
||||
XX(403, FORBIDDEN, Forbidden) \
|
||||
XX(404, NOT_FOUND, Not Found) \
|
||||
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
|
||||
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
|
||||
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
|
||||
XX(408, REQUEST_TIMEOUT, Request Timeout) \
|
||||
XX(409, CONFLICT, Conflict) \
|
||||
XX(410, GONE, Gone) \
|
||||
XX(411, LENGTH_REQUIRED, Length Required) \
|
||||
XX(412, PRECONDITION_FAILED, Precondition Failed) \
|
||||
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
|
||||
XX(414, URI_TOO_LONG, URI Too Long) \
|
||||
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
|
||||
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
|
||||
XX(417, EXPECTATION_FAILED, Expectation Failed) \
|
||||
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
|
||||
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
|
||||
XX(423, LOCKED, Locked) \
|
||||
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
|
||||
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
|
||||
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
|
||||
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
|
||||
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
|
||||
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
|
||||
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
|
||||
XX(501, NOT_IMPLEMENTED, Not Implemented) \
|
||||
XX(502, BAD_GATEWAY, Bad Gateway) \
|
||||
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
|
||||
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
|
||||
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
|
||||
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
|
||||
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
|
||||
XX(508, LOOP_DETECTED, Loop Detected) \
|
||||
XX(510, NOT_EXTENDED, Not Extended) \
|
||||
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
|
||||
|
||||
enum http_status
|
||||
{
|
||||
#define XX(num, name, string) HTTP_STATUS_##name = num,
|
||||
HTTP_STATUS_MAP(XX)
|
||||
#undef XX
|
||||
};
|
||||
|
||||
|
||||
/* Request Methods */
|
||||
#define HTTP_METHOD_MAP(XX) \
|
||||
XX(0, DELETE, DELETE) \
|
||||
XX(1, GET, GET) \
|
||||
XX(2, HEAD, HEAD) \
|
||||
XX(3, POST, POST) \
|
||||
XX(4, PUT, PUT) \
|
||||
/* pathological */ \
|
||||
XX(5, CONNECT, CONNECT) \
|
||||
XX(6, OPTIONS, OPTIONS) \
|
||||
XX(7, TRACE, TRACE) \
|
||||
/* WebDAV */ \
|
||||
XX(8, COPY, COPY) \
|
||||
XX(9, LOCK, LOCK) \
|
||||
XX(10, MKCOL, MKCOL) \
|
||||
XX(11, MOVE, MOVE) \
|
||||
XX(12, PROPFIND, PROPFIND) \
|
||||
XX(13, PROPPATCH, PROPPATCH) \
|
||||
XX(14, SEARCH, SEARCH) \
|
||||
XX(15, UNLOCK, UNLOCK) \
|
||||
XX(16, BIND, BIND) \
|
||||
XX(17, REBIND, REBIND) \
|
||||
XX(18, UNBIND, UNBIND) \
|
||||
XX(19, ACL, ACL) \
|
||||
/* subversion */ \
|
||||
XX(20, REPORT, REPORT) \
|
||||
XX(21, MKACTIVITY, MKACTIVITY) \
|
||||
XX(22, CHECKOUT, CHECKOUT) \
|
||||
XX(23, MERGE, MERGE) \
|
||||
/* upnp */ \
|
||||
XX(24, MSEARCH, M-SEARCH) \
|
||||
XX(25, NOTIFY, NOTIFY) \
|
||||
XX(26, SUBSCRIBE, SUBSCRIBE) \
|
||||
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||
/* RFC-5789 */ \
|
||||
XX(28, PATCH, PATCH) \
|
||||
XX(29, PURGE, PURGE) \
|
||||
/* CalDAV */ \
|
||||
XX(30, MKCALENDAR, MKCALENDAR) \
|
||||
/* RFC-2068, section 19.6.1.2 */ \
|
||||
XX(31, LINK, LINK) \
|
||||
XX(32, UNLINK, UNLINK) \
|
||||
/* icecast */ \
|
||||
XX(33, SOURCE, SOURCE) \
|
||||
|
||||
enum http_method
|
||||
{
|
||||
#define XX(num, name, string) HTTP_##name = num,
|
||||
HTTP_METHOD_MAP(XX)
|
||||
#undef XX
|
||||
};
|
||||
|
||||
|
||||
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
|
||||
|
||||
|
||||
/* Flag values for http_parser.flags field */
|
||||
enum flags
|
||||
{ F_CHUNKED = 1 << 0
|
||||
, F_CONNECTION_KEEP_ALIVE = 1 << 1
|
||||
, F_CONNECTION_CLOSE = 1 << 2
|
||||
, F_CONNECTION_UPGRADE = 1 << 3
|
||||
, F_TRAILING = 1 << 4
|
||||
, F_UPGRADE = 1 << 5
|
||||
, F_SKIPBODY = 1 << 6
|
||||
, F_CONTENTLENGTH = 1 << 7
|
||||
};
|
||||
|
||||
|
||||
/* Map for errno-related constants
|
||||
*
|
||||
* The provided argument should be a macro that takes 2 arguments.
|
||||
*/
|
||||
#define HTTP_ERRNO_MAP(XX) \
|
||||
/* No error */ \
|
||||
XX(OK, "success") \
|
||||
\
|
||||
/* Callback-related errors */ \
|
||||
XX(CB_message_begin, "the on_message_begin callback failed") \
|
||||
XX(CB_url, "the on_url callback failed") \
|
||||
XX(CB_header_field, "the on_header_field callback failed") \
|
||||
XX(CB_header_value, "the on_header_value callback failed") \
|
||||
XX(CB_headers_complete, "the on_headers_complete callback failed") \
|
||||
XX(CB_body, "the on_body callback failed") \
|
||||
XX(CB_message_complete, "the on_message_complete callback failed") \
|
||||
XX(CB_status, "the on_status callback failed") \
|
||||
XX(CB_chunk_header, "the on_chunk_header callback failed") \
|
||||
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
|
||||
\
|
||||
/* Parsing-related errors */ \
|
||||
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
|
||||
XX(HEADER_OVERFLOW, \
|
||||
"too many header bytes seen; overflow detected") \
|
||||
XX(CLOSED_CONNECTION, \
|
||||
"data received after completed connection: close message") \
|
||||
XX(INVALID_VERSION, "invalid HTTP version") \
|
||||
XX(INVALID_STATUS, "invalid HTTP status code") \
|
||||
XX(INVALID_METHOD, "invalid HTTP method") \
|
||||
XX(INVALID_URL, "invalid URL") \
|
||||
XX(INVALID_HOST, "invalid host") \
|
||||
XX(INVALID_PORT, "invalid port") \
|
||||
XX(INVALID_PATH, "invalid path") \
|
||||
XX(INVALID_QUERY_STRING, "invalid query string") \
|
||||
XX(INVALID_FRAGMENT, "invalid fragment") \
|
||||
XX(LF_EXPECTED, "LF character expected") \
|
||||
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
|
||||
XX(INVALID_CONTENT_LENGTH, \
|
||||
"invalid character in content-length header") \
|
||||
XX(UNEXPECTED_CONTENT_LENGTH, \
|
||||
"unexpected content-length header") \
|
||||
XX(INVALID_CHUNK_SIZE, \
|
||||
"invalid character in chunk size header") \
|
||||
XX(INVALID_CONSTANT, "invalid constant string") \
|
||||
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
|
||||
XX(STRICT, "strict mode assertion failed") \
|
||||
XX(PAUSED, "parser is paused") \
|
||||
XX(UNKNOWN, "an unknown error occurred") \
|
||||
XX(INVALID_TRANSFER_ENCODING, \
|
||||
"request has invalid transfer-encoding") \
|
||||
|
||||
|
||||
/* Define HPE_* values for each errno value above */
|
||||
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
|
||||
enum http_errno {
|
||||
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
|
||||
};
|
||||
#undef HTTP_ERRNO_GEN
|
||||
|
||||
|
||||
/* Get an http_errno value from an http_parser */
|
||||
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
|
||||
|
||||
|
||||
struct http_parser {
|
||||
/** PRIVATE **/
|
||||
unsigned int type : 2; /* enum http_parser_type */
|
||||
unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
|
||||
unsigned int state : 7; /* enum state from http_parser.c */
|
||||
unsigned int header_state : 7; /* enum header_state from http_parser.c */
|
||||
unsigned int index : 5; /* index into current matcher */
|
||||
unsigned int uses_transfer_encoding : 1; /* Transfer-Encoding header is present */
|
||||
unsigned int allow_chunked_length : 1; /* Allow headers with both
|
||||
* `Content-Length` and
|
||||
* `Transfer-Encoding: chunked` set */
|
||||
unsigned int lenient_http_headers : 1;
|
||||
|
||||
uint32_t nread; /* # bytes read in various scenarios */
|
||||
uint64_t content_length; /* # bytes in body. `(uint64_t) -1` (all bits one)
|
||||
* if no Content-Length header.
|
||||
*/
|
||||
|
||||
/** READ-ONLY **/
|
||||
unsigned short http_major;
|
||||
unsigned short http_minor;
|
||||
unsigned int status_code : 16; /* responses only */
|
||||
unsigned int method : 8; /* requests only */
|
||||
unsigned int http_errno : 7;
|
||||
|
||||
/* 1 = Upgrade header was present and the parser has exited because of that.
|
||||
* 0 = No upgrade header present.
|
||||
* Should be checked when http_parser_execute() returns in addition to
|
||||
* error checking.
|
||||
*/
|
||||
unsigned int upgrade : 1;
|
||||
|
||||
/** PUBLIC **/
|
||||
void *data; /* A pointer to get hook to the "connection" or "socket" object */
|
||||
};
|
||||
|
||||
|
||||
struct http_parser_settings {
|
||||
http_cb on_message_begin;
|
||||
http_data_cb on_url;
|
||||
http_data_cb on_status;
|
||||
http_data_cb on_header_field;
|
||||
http_data_cb on_header_value;
|
||||
http_cb on_headers_complete;
|
||||
http_data_cb on_body;
|
||||
http_cb on_message_complete;
|
||||
/* When on_chunk_header is called, the current chunk length is stored
|
||||
* in parser->content_length.
|
||||
*/
|
||||
http_cb on_chunk_header;
|
||||
http_cb on_chunk_complete;
|
||||
};
|
||||
|
||||
|
||||
enum http_parser_url_fields
|
||||
{ UF_SCHEMA = 0
|
||||
, UF_HOST = 1
|
||||
, UF_PORT = 2
|
||||
, UF_PATH = 3
|
||||
, UF_QUERY = 4
|
||||
, UF_FRAGMENT = 5
|
||||
, UF_USERINFO = 6
|
||||
, UF_MAX = 7
|
||||
};
|
||||
|
||||
|
||||
/* Result structure for http_parser_parse_url().
|
||||
*
|
||||
* Callers should index into field_data[] with UF_* values iff field_set
|
||||
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
|
||||
* because we probably have padding left over), we convert any port to
|
||||
* a uint16_t.
|
||||
*/
|
||||
struct http_parser_url {
|
||||
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
|
||||
uint16_t port; /* Converted UF_PORT string */
|
||||
|
||||
struct {
|
||||
uint16_t off; /* Offset into buffer in which field starts */
|
||||
uint16_t len; /* Length of run in buffer */
|
||||
} field_data[UF_MAX];
|
||||
};
|
||||
|
||||
|
||||
/* Returns the library version. Bits 16-23 contain the major version number,
|
||||
* bits 8-15 the minor version number and bits 0-7 the patch level.
|
||||
* Usage example:
|
||||
*
|
||||
* unsigned long version = http_parser_version();
|
||||
* unsigned major = (version >> 16) & 255;
|
||||
* unsigned minor = (version >> 8) & 255;
|
||||
* unsigned patch = version & 255;
|
||||
* printf("http_parser v%u.%u.%u\n", major, minor, patch);
|
||||
*/
|
||||
unsigned long http_parser_version(void);
|
||||
|
||||
void http_parser_init(http_parser *parser, enum http_parser_type type);
|
||||
|
||||
|
||||
/* Initialize http_parser_settings members to 0
|
||||
*/
|
||||
void http_parser_settings_init(http_parser_settings *settings);
|
||||
|
||||
|
||||
/* Executes the parser. Returns number of parsed bytes. Sets
|
||||
* `parser->http_errno` on error. */
|
||||
size_t http_parser_execute(http_parser *parser,
|
||||
const http_parser_settings *settings,
|
||||
const char *data,
|
||||
size_t len);
|
||||
|
||||
|
||||
/* If http_should_keep_alive() in the on_headers_complete or
|
||||
* on_message_complete callback returns 0, then this should be
|
||||
* the last message on the connection.
|
||||
* If you are the server, respond with the "Connection: close" header.
|
||||
* If you are the client, close the connection.
|
||||
*/
|
||||
int http_should_keep_alive(const http_parser *parser);
|
||||
|
||||
/* Returns a string version of the HTTP method. */
|
||||
const char *http_method_str(enum http_method m);
|
||||
|
||||
/* Returns a string version of the HTTP status code. */
|
||||
const char *http_status_str(enum http_status s);
|
||||
|
||||
/* Return a string name of the given error */
|
||||
const char *http_errno_name(enum http_errno err);
|
||||
|
||||
/* Return a string description of the given error */
|
||||
const char *http_errno_description(enum http_errno err);
|
||||
|
||||
/* Initialize all http_parser_url members to 0 */
|
||||
void http_parser_url_init(struct http_parser_url *u);
|
||||
|
||||
/* Parse a URL; return nonzero on failure */
|
||||
int http_parser_parse_url(const char *buf, size_t buflen,
|
||||
int is_connect,
|
||||
struct http_parser_url *u);
|
||||
|
||||
/* Pause or un-pause the parser; a nonzero value pauses */
|
||||
void http_parser_pause(http_parser *parser, int paused);
|
||||
|
||||
/* Checks if this is the final chunk of the body. */
|
||||
int http_body_is_final(const http_parser *parser);
|
||||
|
||||
/* Change the maximum header size provided at compile time. */
|
||||
void http_parser_set_max_header_size(uint32_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
12
src/qthttpserver/src/3rdparty/qt_attribution.json
vendored
Normal file
12
src/qthttpserver/src/3rdparty/qt_attribution.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"Id": "http-parser",
|
||||
"Name": "HTTP request/response parser for C",
|
||||
"QDocModule": "qthttpserver",
|
||||
"QtUsage": "Used in Qt HttpServer.",
|
||||
"Version": "2.9.0"
|
||||
|
||||
"License": "MIT License",
|
||||
"LicenseId": "MIT",
|
||||
"LicenseFile": "http-parser/LICENSE-MIT",
|
||||
"Copyright": "Copyright Joyent, Inc. and other Node contributors. All rights reserved."
|
||||
}
|
||||
6
src/qthttpserver/src/CMakeLists.txt
Normal file
6
src/qthttpserver/src/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# Generated from src.pro.
|
||||
|
||||
if(QT_FEATURE_ssl)
|
||||
add_subdirectory(sslserver)
|
||||
endif()
|
||||
add_subdirectory(httpserver)
|
||||
56
src/qthttpserver/src/httpserver/CMakeLists.txt
Normal file
56
src/qthttpserver/src/httpserver/CMakeLists.txt
Normal file
@@ -0,0 +1,56 @@
|
||||
# Generated from httpserver.pro.
|
||||
|
||||
#####################################################################
|
||||
## HttpServer Module:
|
||||
#####################################################################
|
||||
|
||||
qt_add_module(HttpServer
|
||||
SOURCES
|
||||
../3rdparty/http-parser/http_parser.c ../3rdparty/http-parser/http_parser.h
|
||||
qabstracthttpserver.cpp qabstracthttpserver.h qabstracthttpserver_p.h
|
||||
qhttpserver.cpp qhttpserver.h qhttpserver_p.h
|
||||
qhttpserverliterals.cpp qhttpserverliterals_p.h
|
||||
qhttpserverrequest.cpp qhttpserverrequest.h qhttpserverrequest_p.h
|
||||
qhttpserverresponder.cpp qhttpserverresponder.h qhttpserverresponder_p.h
|
||||
qhttpserverresponse.cpp qhttpserverresponse.h qhttpserverresponse_p.h
|
||||
qhttpserverrouter.cpp qhttpserverrouter.h qhttpserverrouter_p.h
|
||||
qhttpserverrouterrule.cpp qhttpserverrouterrule.h qhttpserverrouterrule_p.h
|
||||
qhttpserverrouterviewtraits.h
|
||||
qhttpserverviewtraits.h
|
||||
qhttpserverviewtraits_impl.h
|
||||
qthttpserverglobal.h
|
||||
INCLUDE_DIRECTORIES
|
||||
.
|
||||
../3rdparty/http-parser
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
PUBLIC_LIBRARIES
|
||||
Qt::Core
|
||||
Qt::Network
|
||||
PRIVATE_MODULE_INTERFACE
|
||||
Qt::CorePrivate
|
||||
)
|
||||
|
||||
## Scopes:
|
||||
#####################################################################
|
||||
|
||||
qt_extend_target(HttpServer CONDITION TARGET Qt::WebSockets
|
||||
LIBRARIES
|
||||
Qt::WebSocketsPrivate
|
||||
PUBLIC_LIBRARIES
|
||||
Qt::WebSockets
|
||||
PRIVATE_MODULE_INTERFACE
|
||||
Qt::WebSocketsPrivate
|
||||
)
|
||||
|
||||
qt_extend_target(HttpServer CONDITION QT_FEATURE_ssl
|
||||
PUBLIC_LIBRARIES
|
||||
Qt::SslServer
|
||||
)
|
||||
|
||||
qt_extend_target(HttpServer CONDITION TARGET Qt::Concurrent
|
||||
SOURCES
|
||||
qhttpserverfutureresponse.cpp qhttpserverfutureresponse.h
|
||||
PUBLIC_LIBRARIES
|
||||
Qt::Concurrent
|
||||
)
|
||||
48
src/qthttpserver/src/httpserver/httpserver.pro
Normal file
48
src/qthttpserver/src/httpserver/httpserver.pro
Normal file
@@ -0,0 +1,48 @@
|
||||
TARGET = QtHttpServer
|
||||
INCLUDEPATH += .
|
||||
|
||||
QT = network core-private
|
||||
|
||||
qtHaveModule(websockets): QT += websockets-private
|
||||
qtConfig(ssl): QT += sslserver
|
||||
|
||||
HEADERS += \
|
||||
qthttpserverglobal.h \
|
||||
qabstracthttpserver.h \
|
||||
qabstracthttpserver_p.h \
|
||||
qhttpserver.h \
|
||||
qhttpserver_p.h \
|
||||
qhttpserverliterals_p.h \
|
||||
qhttpserverrequest.h \
|
||||
qhttpserverrequest_p.h \
|
||||
qhttpserverresponder.h \
|
||||
qhttpserverresponder_p.h \
|
||||
qhttpserverresponse.h \
|
||||
qhttpserverresponse_p.h \
|
||||
qhttpserverrouter.h \
|
||||
qhttpserverrouter_p.h \
|
||||
qhttpserverrouterrule.h \
|
||||
qhttpserverrouterrule_p.h \
|
||||
qhttpserverrouterviewtraits.h \
|
||||
qhttpserverviewtraits.h \
|
||||
qhttpserverviewtraits_impl.h
|
||||
|
||||
SOURCES += \
|
||||
qabstracthttpserver.cpp \
|
||||
qhttpserver.cpp \
|
||||
qhttpserverliterals.cpp \
|
||||
qhttpserverrequest.cpp \
|
||||
qhttpserverresponder.cpp \
|
||||
qhttpserverresponse.cpp \
|
||||
qhttpserverrouter.cpp \
|
||||
qhttpserverrouterrule.cpp
|
||||
|
||||
qtHaveModule(concurrent) {
|
||||
QT += concurrent
|
||||
HEADERS += qhttpserverfutureresponse.h
|
||||
SOURCES += qhttpserverfutureresponse.cpp
|
||||
}
|
||||
|
||||
include(../3rdparty/http-parser.pri)
|
||||
|
||||
load(qt_module)
|
||||
310
src/qthttpserver/src/httpserver/qabstracthttpserver.cpp
Normal file
310
src/qthttpserver/src/httpserver/qabstracthttpserver.cpp
Normal file
@@ -0,0 +1,310 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtHttpServer/qabstracthttpserver.h>
|
||||
|
||||
#include <QtHttpServer/qhttpserverrequest.h>
|
||||
#include <QtHttpServer/qhttpserverresponder.h>
|
||||
#include <private/qabstracthttpserver_p.h>
|
||||
#include <private/qhttpserverrequest_p.h>
|
||||
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
#include <QtCore/qmetaobject.h>
|
||||
#include <QtNetwork/qtcpserver.h>
|
||||
#include <QtNetwork/qtcpsocket.h>
|
||||
|
||||
#include "http_parser.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_LOGGING_CATEGORY(lcHttpServer, "qt.httpserver")
|
||||
|
||||
QAbstractHttpServerPrivate::QAbstractHttpServerPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
void QAbstractHttpServerPrivate::handleNewConnections()
|
||||
{
|
||||
Q_Q(QAbstractHttpServer);
|
||||
auto tcpServer = qobject_cast<QTcpServer *>(q->sender());
|
||||
Q_ASSERT(tcpServer);
|
||||
while (auto socket = tcpServer->nextPendingConnection()) {
|
||||
auto request = new QHttpServerRequest(socket->peerAddress()); // TODO own tcp server could pre-allocate it
|
||||
QObject::connect(socket, &QTcpSocket::readyRead, q_ptr,
|
||||
[this, request, socket] () {
|
||||
handleReadyRead(socket, request);
|
||||
});
|
||||
|
||||
QObject::connect(socket, &QTcpSocket::disconnected, socket, [request, socket] () {
|
||||
if (!request->d->handling)
|
||||
socket->deleteLater();
|
||||
});
|
||||
|
||||
QObject::connect(socket, &QObject::destroyed, socket, [request] () {
|
||||
delete request;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void QAbstractHttpServerPrivate::handleReadyRead(QTcpSocket *socket,
|
||||
QHttpServerRequest *request)
|
||||
{
|
||||
Q_Q(QAbstractHttpServer);
|
||||
Q_ASSERT(socket);
|
||||
Q_ASSERT(request);
|
||||
|
||||
if (!socket->isTransactionStarted())
|
||||
socket->startTransaction();
|
||||
|
||||
if (request->d->state == QHttpServerRequestPrivate::State::OnMessageComplete)
|
||||
request->d->clear();
|
||||
|
||||
if (!request->d->parse(socket)) {
|
||||
socket->disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!request->d->httpParser.upgrade &&
|
||||
request->d->state != QHttpServerRequestPrivate::State::OnMessageComplete)
|
||||
return; // Partial read
|
||||
|
||||
if (request->d->httpParser.upgrade &&
|
||||
request->d->httpParser.method != HTTP_CONNECT) { // Upgrade
|
||||
const auto &upgradeValue = request->value(QByteArrayLiteral("upgrade"));
|
||||
#if defined(QT_WEBSOCKETS_LIB)
|
||||
if (upgradeValue.compare(QByteArrayLiteral("websocket"), Qt::CaseInsensitive) == 0) {
|
||||
static const auto signal = QMetaMethod::fromSignal(
|
||||
&QAbstractHttpServer::newWebSocketConnection);
|
||||
if (q->isSignalConnected(signal)) {
|
||||
QObject::disconnect(socket, &QTcpSocket::readyRead, nullptr, nullptr);
|
||||
socket->rollbackTransaction();
|
||||
websocketServer.handleConnection(socket);
|
||||
Q_EMIT socket->readyRead();
|
||||
} else {
|
||||
qWarning(lcHttpServer, "WebSocket received but no slots connected to "
|
||||
"QWebSocketServer::newConnection");
|
||||
socket->disconnectFromHost();
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
qCWarning(lcHttpServer, "Upgrade to %s not supported", upgradeValue.constData());
|
||||
socket->disconnectFromHost();
|
||||
return;
|
||||
}
|
||||
|
||||
socket->commitTransaction();
|
||||
request->d->handling = true;
|
||||
if (!q->handleRequest(*request, socket))
|
||||
Q_EMIT q->missingHandler(*request, socket);
|
||||
request->d->handling = false;
|
||||
if (socket->state() == QAbstractSocket::UnconnectedState)
|
||||
socket->deleteLater();
|
||||
}
|
||||
|
||||
QAbstractHttpServer::QAbstractHttpServer(QObject *parent)
|
||||
: QAbstractHttpServer(*new QAbstractHttpServerPrivate, parent)
|
||||
{}
|
||||
|
||||
QAbstractHttpServer::QAbstractHttpServer(QAbstractHttpServerPrivate &dd, QObject *parent)
|
||||
: QObject(dd, parent)
|
||||
{
|
||||
#if defined(QT_WEBSOCKETS_LIB)
|
||||
Q_D(QAbstractHttpServer);
|
||||
connect(&d->websocketServer, &QWebSocketServer::newConnection,
|
||||
this, &QAbstractHttpServer::newWebSocketConnection);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Tries to bind a \c QTcpServer to \a address and \a port.
|
||||
|
||||
Returns the server port upon success, 0 otherwise.
|
||||
*/
|
||||
quint16 QAbstractHttpServer::listen(const QHostAddress &address, quint16 port)
|
||||
{
|
||||
#if QT_CONFIG(ssl)
|
||||
Q_D(QAbstractHttpServer);
|
||||
QTcpServer *tcpServer = d->sslEnabled ? new QSslServer(d->sslConfiguration, this)
|
||||
: new QTcpServer(this);
|
||||
#else
|
||||
auto tcpServer = new QTcpServer(this);
|
||||
#endif
|
||||
const auto listening = tcpServer->listen(address, port);
|
||||
if (listening) {
|
||||
bind(tcpServer);
|
||||
return tcpServer->serverPort();
|
||||
} else {
|
||||
qCCritical(lcHttpServer, "listen failed: %s",
|
||||
tcpServer->errorString().toStdString().c_str());
|
||||
}
|
||||
|
||||
delete tcpServer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the list of ports this instance of QAbstractHttpServer
|
||||
is listening to.
|
||||
|
||||
This function has the same guarantee as QObject::children,
|
||||
the latest server added is the last entry in the vector.
|
||||
|
||||
\sa servers()
|
||||
*/
|
||||
QVector<quint16> QAbstractHttpServer::serverPorts()
|
||||
{
|
||||
QVector<quint16> ports;
|
||||
auto children = findChildren<QTcpServer *>();
|
||||
ports.reserve(children.count());
|
||||
std::transform(children.cbegin(), children.cend(), std::back_inserter(ports),
|
||||
[](const QTcpServer *server) { return server->serverPort(); });
|
||||
return ports;
|
||||
}
|
||||
|
||||
/*!
|
||||
Bind the HTTP server to given TCP \a server over which
|
||||
the transmission happens. It is possible to call this function
|
||||
multiple times with different instances of TCP \a server to
|
||||
handle multiple connections and ports, for example both SSL and
|
||||
non-encrypted connections.
|
||||
|
||||
After calling this function, every _new_ connection will be
|
||||
handled and forwarded by the HTTP server.
|
||||
|
||||
It is the user's responsibility to call QTcpServer::listen() on
|
||||
the \a server.
|
||||
|
||||
If the \a server is null, then a new, default-constructed TCP
|
||||
server will be constructed, which will be listening on a random
|
||||
port and all interfaces.
|
||||
|
||||
The \a server will be parented to this HTTP server.
|
||||
|
||||
\sa QTcpServer, QTcpServer::listen()
|
||||
*/
|
||||
void QAbstractHttpServer::bind(QTcpServer *server)
|
||||
{
|
||||
Q_D(QAbstractHttpServer);
|
||||
if (!server) {
|
||||
server = new QTcpServer(this);
|
||||
if (!server->listen()) {
|
||||
qCCritical(lcHttpServer, "QTcpServer listen failed (%s)",
|
||||
qPrintable(server->errorString()));
|
||||
}
|
||||
} else {
|
||||
if (!server->isListening())
|
||||
qCWarning(lcHttpServer) << "The TCP server" << server << "is not listening.";
|
||||
server->setParent(this);
|
||||
}
|
||||
QObjectPrivate::connect(server, &QTcpServer::newConnection,
|
||||
d, &QAbstractHttpServerPrivate::handleNewConnections,
|
||||
Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns list of child TCP servers of this HTTP server.
|
||||
|
||||
\sa serverPorts()
|
||||
*/
|
||||
QVector<QTcpServer *> QAbstractHttpServer::servers() const
|
||||
{
|
||||
return findChildren<QTcpServer *>().toVector();
|
||||
}
|
||||
|
||||
#if defined(QT_WEBSOCKETS_LIB)
|
||||
/*!
|
||||
\fn QAbstractHttpServer::newConnection
|
||||
This signal is emitted every time a new WebSocket connection is
|
||||
available.
|
||||
|
||||
\sa hasPendingWebSocketConnections(), nextPendingWebSocketConnection()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Returns \c true if the server has pending WebSocket connections;
|
||||
otherwise returns \c false.
|
||||
|
||||
\sa newWebSocketConnection(), nextPendingWebSocketConnection()
|
||||
*/
|
||||
bool QAbstractHttpServer::hasPendingWebSocketConnections() const
|
||||
{
|
||||
Q_D(const QAbstractHttpServer);
|
||||
return d->websocketServer.hasPendingConnections();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the next pending connection as a connected QWebSocket
|
||||
object. QAbstractHttpServer does not take ownership of the
|
||||
returned QWebSocket object. It is up to the caller to delete the
|
||||
object explicitly when it will no longer be used, otherwise a
|
||||
memory leak will occur. \c nullptr is returned if this function
|
||||
is called when there are no pending connections.
|
||||
|
||||
\note The returned QWebSocket object cannot be used from another
|
||||
thread.
|
||||
|
||||
\sa newWebSocketConnection(), hasPendingWebSocketConnections()
|
||||
*/
|
||||
QWebSocket *QAbstractHttpServer::nextPendingWebSocketConnection()
|
||||
{
|
||||
Q_D(QAbstractHttpServer);
|
||||
return d->websocketServer.nextPendingConnection();
|
||||
}
|
||||
#endif
|
||||
|
||||
QHttpServerResponder QAbstractHttpServer::makeResponder(const QHttpServerRequest &request,
|
||||
QTcpSocket *socket)
|
||||
{
|
||||
return QHttpServerResponder(request, socket);
|
||||
}
|
||||
|
||||
#if QT_CONFIG(ssl)
|
||||
void QAbstractHttpServer::sslSetup(const QSslCertificate &certificate,
|
||||
const QSslKey &privateKey,
|
||||
QSsl::SslProtocol protocol)
|
||||
{
|
||||
QSslConfiguration conf;
|
||||
conf.setLocalCertificate(certificate);
|
||||
conf.setPrivateKey(privateKey);
|
||||
conf.setProtocol(protocol);
|
||||
sslSetup(conf);
|
||||
}
|
||||
|
||||
void QAbstractHttpServer::sslSetup(const QSslConfiguration &sslConfiguration)
|
||||
{
|
||||
Q_D(QAbstractHttpServer);
|
||||
d->sslConfiguration = sslConfiguration;
|
||||
d->sslEnabled = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
97
src/qthttpserver/src/httpserver/qabstracthttpserver.h
Normal file
97
src/qthttpserver/src/httpserver/qabstracthttpserver.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QABSTRACTHTTPSERVER_H
|
||||
#define QABSTRACTHTTPSERVER_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
|
||||
#include <QtHttpServer/qthttpserverglobal.h>
|
||||
|
||||
#include <QtNetwork/qhostaddress.h>
|
||||
|
||||
#if QT_CONFIG(ssl)
|
||||
#include <QtSslServer/qsslserver.h>
|
||||
#include <QSslCertificate>
|
||||
#include <QSslKey>
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttpServerRequest;
|
||||
class QHttpServerResponder;
|
||||
class QTcpServer;
|
||||
class QTcpSocket;
|
||||
class QWebSocket;
|
||||
|
||||
class QAbstractHttpServerPrivate;
|
||||
class Q_HTTPSERVER_EXPORT QAbstractHttpServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QAbstractHttpServer(QObject *parent = nullptr);
|
||||
|
||||
quint16 listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
|
||||
QVector<quint16> serverPorts();
|
||||
|
||||
void bind(QTcpServer *server = nullptr);
|
||||
QVector<QTcpServer *> servers() const;
|
||||
|
||||
#if QT_CONFIG(ssl)
|
||||
void sslSetup(const QSslCertificate &certificate, const QSslKey &privateKey,
|
||||
QSsl::SslProtocol protocol = QSsl::SecureProtocols);
|
||||
void sslSetup(const QSslConfiguration &sslConfiguration);
|
||||
#endif
|
||||
|
||||
Q_SIGNALS:
|
||||
void missingHandler(const QHttpServerRequest &request, QTcpSocket *socket);
|
||||
|
||||
#if defined(QT_WEBSOCKETS_LIB)
|
||||
void newWebSocketConnection();
|
||||
|
||||
public:
|
||||
bool hasPendingWebSocketConnections() const;
|
||||
QWebSocket *nextPendingWebSocketConnection();
|
||||
#endif // defined(QT_WEBSOCKETS_LIB)
|
||||
|
||||
protected:
|
||||
QAbstractHttpServer(QAbstractHttpServerPrivate &dd, QObject *parent = nullptr);
|
||||
|
||||
virtual bool handleRequest(const QHttpServerRequest &request, QTcpSocket *socket) = 0;
|
||||
static QHttpServerResponder makeResponder(const QHttpServerRequest &request,
|
||||
QTcpSocket *socket);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(QAbstractHttpServer)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QABSTRACTHTTPSERVER_H
|
||||
82
src/qthttpserver/src/httpserver/qabstracthttpserver_p.h
Normal file
82
src/qthttpserver/src/httpserver/qabstracthttpserver_p.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QABSTRACTHTTPSERVER_P_H
|
||||
#define QABSTRACTHTTPSERVER_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists for the convenience
|
||||
// of QHttpServer. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
|
||||
#include <QtHttpServer/qabstracthttpserver.h>
|
||||
#include <QtHttpServer/qthttpserverglobal.h>
|
||||
|
||||
#include <private/qobject_p.h>
|
||||
|
||||
#if defined(QT_WEBSOCKETS_LIB)
|
||||
#include <QtWebSockets/qwebsocketserver.h>
|
||||
#endif // defined(QT_WEBSOCKETS_LIB)
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttpServerRequest;
|
||||
|
||||
class Q_HTTPSERVER_EXPORT QAbstractHttpServerPrivate: public QObjectPrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QAbstractHttpServer)
|
||||
|
||||
public:
|
||||
QAbstractHttpServerPrivate();
|
||||
|
||||
#if defined(QT_WEBSOCKETS_LIB)
|
||||
QWebSocketServer websocketServer {
|
||||
QLatin1String("QtHttpServer"),
|
||||
QWebSocketServer::NonSecureMode
|
||||
};
|
||||
#endif // defined(QT_WEBSOCKETS_LIB)
|
||||
|
||||
void handleNewConnections();
|
||||
void handleReadyRead(QTcpSocket *socket,
|
||||
QHttpServerRequest *request);
|
||||
|
||||
#if QT_CONFIG(ssl)
|
||||
QSslConfiguration sslConfiguration;
|
||||
bool sslEnabled = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QABSTRACTHTTPSERVER_P_H
|
||||
183
src/qthttpserver/src/httpserver/qhttpserver.cpp
Normal file
183
src/qthttpserver/src/httpserver/qhttpserver.cpp
Normal file
@@ -0,0 +1,183 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtHttpServer/qhttpserver.h>
|
||||
|
||||
|
||||
#include <QtHttpServer/qhttpserverrequest.h>
|
||||
#include <QtHttpServer/qhttpserverresponder.h>
|
||||
#include <QtHttpServer/qhttpserverresponse.h>
|
||||
|
||||
#include <private/qhttpserver_p.h>
|
||||
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
|
||||
#include <QtNetwork/qtcpsocket.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_LOGGING_CATEGORY(lcHS, "qt.httpserver");
|
||||
|
||||
/*!
|
||||
\class QHttpServer
|
||||
\brief QHttpServer is a simplified API for QAbstractHttpServer and QHttpServerRouter.
|
||||
|
||||
\code
|
||||
|
||||
QHttpServer server;
|
||||
|
||||
server.route("/", [] () {
|
||||
return "hello world";
|
||||
});
|
||||
server.listen();
|
||||
|
||||
\endcode
|
||||
*/
|
||||
|
||||
QHttpServer::QHttpServer(QObject *parent)
|
||||
: QAbstractHttpServer(*new QHttpServerPrivate, parent)
|
||||
{
|
||||
connect(this, &QAbstractHttpServer::missingHandler, this,
|
||||
[=] (const QHttpServerRequest &request, QTcpSocket *socket) {
|
||||
qCDebug(lcHS) << tr("missing handler:") << request.url().path();
|
||||
sendResponse(QHttpServerResponder::StatusCode::NotFound, request, socket);
|
||||
});
|
||||
}
|
||||
|
||||
/*! \fn template<typename Rule = QHttpServerRouterRule, typename ... Args> bool route(Args && ... args)
|
||||
This function is just a wrapper to simplify the router API.
|
||||
|
||||
This function takes variadic arguments. The last argument is \c a callback (ViewHandler).
|
||||
The remaining arguments are used to create a new \a Rule (the default is QHttpServerRouterRule).
|
||||
This is in turn added to the QHttpServerRouter.
|
||||
|
||||
\c ViewHandler can only be a lambda. The lambda definition can take two optional special
|
||||
arguments: \c {const QHttpServerRequest&} and \c {QHttpServerResponder&&}.
|
||||
These special arguments must be the last in the parameter list.
|
||||
|
||||
Examples:
|
||||
|
||||
\code
|
||||
|
||||
QHttpServer server;
|
||||
|
||||
// Valid:
|
||||
server.route("test", [] (const int page) { return ""; });
|
||||
server.route("test", [] (const int page, const QHttpServerRequest &request) { return ""; });
|
||||
server.route("test", [] (QHttpServerResponder &&responder) { return ""; });
|
||||
|
||||
// Invalid (compile time error):
|
||||
server.route("test", [] (const QHttpServerRequest &request, const int page) { return ""; }); // request must be last
|
||||
server.route("test", [] (QHttpServerRequest &request) { return ""; }); // request must be passed by const reference
|
||||
server.route("test", [] (QHttpServerResponder &responder) { return ""; }); // responder must be passed by universal reference
|
||||
|
||||
\endcode
|
||||
|
||||
\sa QHttpServerRouter::addRule
|
||||
*/
|
||||
|
||||
/*! \fn template<typename ViewHandler> void afterRequest(ViewHandler &&viewHandler)
|
||||
Register a function to be run after each request.
|
||||
|
||||
\c ViewHandler can only be a lambda. The lambda definition can take two
|
||||
arguments: \c {QHttpServerResponse &&} and \c {const QHttpServerRequest&} (optional).
|
||||
|
||||
Examples:
|
||||
|
||||
\code
|
||||
|
||||
QHttpServer server;
|
||||
|
||||
// Valid:
|
||||
server.afterRequest([] (QHttpServerResponse &&resp, const QHttpServerRequest &request) {
|
||||
return std::move(resp);
|
||||
}
|
||||
server.afterRequest([] (const QHttpServerRequest &request, QHttpServerResponse &&resp) {
|
||||
return std::move(resp);
|
||||
}
|
||||
server.afterRequest([] (QHttpServerResponse &&resp) { return std::move(resp); }
|
||||
|
||||
// Invalid (compile time error):
|
||||
// resp must be passed by universal reference
|
||||
server.afterRequest([] (QHttpServerResponse &resp, const QHttpServerRequest &request) {
|
||||
return std::move(resp);
|
||||
}
|
||||
// request must be passed by const reference
|
||||
server.afterRequest([] (QHttpServerResponse &&resp, QHttpServerRequest &request) {
|
||||
return std::move(resp);
|
||||
}
|
||||
|
||||
\endcode
|
||||
*/
|
||||
|
||||
/*!
|
||||
Destroys a QHttpServer.
|
||||
*/
|
||||
QHttpServer::~QHttpServer()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the router object.
|
||||
*/
|
||||
QHttpServerRouter *QHttpServer::router()
|
||||
{
|
||||
Q_D(QHttpServer);
|
||||
return &d->router;
|
||||
}
|
||||
|
||||
void QHttpServer::afterRequestImpl(AfterRequestHandler &&afterRequestHandler)
|
||||
{
|
||||
Q_D(QHttpServer);
|
||||
d->afterRequestHandlers.push_back(std::move(afterRequestHandler));
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
void QHttpServer::sendResponse(QHttpServerResponse &&response,
|
||||
const QHttpServerRequest &request,
|
||||
QTcpSocket *socket)
|
||||
{
|
||||
Q_D(QHttpServer);
|
||||
for (auto afterRequestHandler : d->afterRequestHandlers)
|
||||
response = std::move(afterRequestHandler(std::move(response), request));
|
||||
response.write(makeResponder(request, socket));
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool QHttpServer::handleRequest(const QHttpServerRequest &request, QTcpSocket *socket)
|
||||
{
|
||||
Q_D(QHttpServer);
|
||||
return d->router.handleRequest(request, socket);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
222
src/qthttpserver/src/httpserver/qhttpserver.h
Normal file
222
src/qthttpserver/src/httpserver/qhttpserver.h
Normal file
@@ -0,0 +1,222 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Mikhail Svetkin <mikhail.svetkin@gmail.com>
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVER_H
|
||||
#define QHTTPSERVER_H
|
||||
|
||||
#include <QtHttpServer/qabstracthttpserver.h>
|
||||
#include <QtHttpServer/qhttpserverrouter.h>
|
||||
#include <QtHttpServer/qhttpserverrouterrule.h>
|
||||
#include <QtHttpServer/qhttpserverresponse.h>
|
||||
#include <QtHttpServer/qhttpserverrouterviewtraits.h>
|
||||
#include <QtHttpServer/qhttpserverviewtraits.h>
|
||||
|
||||
#include <tuple>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QTcpSocket;
|
||||
class QHttpServerRequest;
|
||||
|
||||
class QHttpServerPrivate;
|
||||
class Q_HTTPSERVER_EXPORT QHttpServer final : public QAbstractHttpServer
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DECLARE_PRIVATE(QHttpServer)
|
||||
|
||||
template<int I, typename ... Ts>
|
||||
struct VariadicTypeAt { using Type = typename std::tuple_element<I, std::tuple<Ts...>>::type; };
|
||||
|
||||
template<typename ... Ts>
|
||||
struct VariadicTypeLast {
|
||||
using Type = typename VariadicTypeAt<sizeof ... (Ts) - 1, Ts...>::Type;
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
using ResponseType =
|
||||
typename std::conditional<
|
||||
std::is_base_of<QHttpServerResponse, T>::value,
|
||||
T,
|
||||
QHttpServerResponse
|
||||
>::type;
|
||||
|
||||
public:
|
||||
explicit QHttpServer(QObject *parent = nullptr);
|
||||
~QHttpServer();
|
||||
|
||||
QHttpServerRouter *router();
|
||||
|
||||
template<typename Rule = QHttpServerRouterRule, typename ... Args>
|
||||
bool route(Args && ... args)
|
||||
{
|
||||
using ViewHandler = typename VariadicTypeLast<Args...>::Type;
|
||||
using ViewTraits = QHttpServerRouterViewTraits<ViewHandler>;
|
||||
static_assert(ViewTraits::Arguments::StaticAssert,
|
||||
"ViewHandler arguments are in the wrong order or not supported");
|
||||
return routeHelper<Rule, ViewHandler, ViewTraits>(
|
||||
QtPrivate::makeIndexSequence<sizeof ... (Args) - 1>{},
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename ViewHandler>
|
||||
void afterRequest(ViewHandler &&viewHandler)
|
||||
{
|
||||
using ViewTraits = QHttpServerAfterRequestViewTraits<ViewHandler>;
|
||||
static_assert(ViewTraits::Arguments::StaticAssert,
|
||||
"ViewHandler arguments are in the wrong order or not supported");
|
||||
afterRequestHelper<ViewTraits, ViewHandler>(std::move(viewHandler));
|
||||
}
|
||||
|
||||
using AfterRequestHandler =
|
||||
std::function<QHttpServerResponse(QHttpServerResponse &&response,
|
||||
const QHttpServerRequest &request)>;
|
||||
private:
|
||||
template<typename ViewTraits, typename ViewHandler>
|
||||
typename std::enable_if<ViewTraits::Arguments::Last::IsRequest::Value &&
|
||||
ViewTraits::Arguments::Count == 2, void>::type
|
||||
afterRequestHelper(ViewHandler &&viewHandler) {
|
||||
auto handler = [viewHandler](QHttpServerResponse &&resp,
|
||||
const QHttpServerRequest &request) {
|
||||
return std::move(viewHandler(std::move(resp), request));
|
||||
};
|
||||
|
||||
afterRequestImpl(std::move(handler));
|
||||
}
|
||||
|
||||
template<typename ViewTraits, typename ViewHandler>
|
||||
typename std::enable_if<ViewTraits::Arguments::Last::IsResponse::Value &&
|
||||
ViewTraits::Arguments::Count == 1, void>::type
|
||||
afterRequestHelper(ViewHandler &&viewHandler) {
|
||||
auto handler = [viewHandler](QHttpServerResponse &&resp,
|
||||
const QHttpServerRequest &) {
|
||||
return std::move(viewHandler(std::move(resp)));
|
||||
};
|
||||
|
||||
afterRequestImpl(std::move(handler));
|
||||
}
|
||||
|
||||
template<typename ViewTraits, typename ViewHandler>
|
||||
typename std::enable_if<ViewTraits::Arguments::Last::IsResponse::Value &&
|
||||
ViewTraits::Arguments::Count == 2, void>::type
|
||||
afterRequestHelper(ViewHandler &&viewHandler) {
|
||||
auto handler = [viewHandler](QHttpServerResponse &&resp,
|
||||
const QHttpServerRequest &request) {
|
||||
return std::move(viewHandler(request, std::move(resp)));
|
||||
};
|
||||
|
||||
afterRequestImpl(std::move(handler));
|
||||
}
|
||||
|
||||
void afterRequestImpl(AfterRequestHandler &&afterRequestHandler);
|
||||
|
||||
private:
|
||||
template<typename Rule, typename ViewHandler, typename ViewTraits, int ... I, typename ... Args>
|
||||
bool routeHelper(QtPrivate::IndexesList<I...>, Args &&... args)
|
||||
{
|
||||
return routeImpl<Rule,
|
||||
ViewHandler,
|
||||
ViewTraits,
|
||||
typename VariadicTypeAt<I, Args...>::Type...>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename Rule, typename ViewHandler, typename ViewTraits, typename ... Args>
|
||||
bool routeImpl(Args &&...args, ViewHandler &&viewHandler)
|
||||
{
|
||||
auto routerHandler = [this, viewHandler] (
|
||||
const QRegularExpressionMatch &match,
|
||||
const QHttpServerRequest &request,
|
||||
QTcpSocket *socket) mutable {
|
||||
auto boundViewHandler = router()->bindCaptured<ViewHandler, ViewTraits>(
|
||||
std::move(viewHandler), match);
|
||||
responseImpl<ViewTraits>(boundViewHandler, request, socket);
|
||||
};
|
||||
|
||||
return router()->addRule<ViewHandler, ViewTraits>(
|
||||
new Rule(std::forward<Args>(args)..., std::move(routerHandler)));
|
||||
}
|
||||
|
||||
template<typename ViewTraits, typename T>
|
||||
typename std::enable_if<!ViewTraits::Arguments::Last::IsSpecial::Value, void>::type
|
||||
responseImpl(T &boundViewHandler,
|
||||
const QHttpServerRequest &request,
|
||||
QTcpSocket *socket)
|
||||
{
|
||||
ResponseType<typename ViewTraits::ReturnType> response(boundViewHandler());
|
||||
sendResponse(std::move(response), request, socket);
|
||||
}
|
||||
|
||||
template<typename ViewTraits, typename T>
|
||||
typename std::enable_if<ViewTraits::Arguments::Last::IsRequest::Value &&
|
||||
ViewTraits::Arguments::PlaceholdersCount == 1, void>::type
|
||||
responseImpl(T &boundViewHandler, const QHttpServerRequest &request, QTcpSocket *socket)
|
||||
{
|
||||
ResponseType<typename ViewTraits::ReturnType> response(boundViewHandler(request));
|
||||
sendResponse(std::move(response), request, socket);
|
||||
}
|
||||
|
||||
template<typename ViewTraits, typename T>
|
||||
typename std::enable_if<ViewTraits::Arguments::Last::IsRequest::Value &&
|
||||
ViewTraits::Arguments::PlaceholdersCount == 2, void>::type
|
||||
responseImpl(T &boundViewHandler, const QHttpServerRequest &request, QTcpSocket *socket)
|
||||
{
|
||||
boundViewHandler(makeResponder(request, socket), request);
|
||||
}
|
||||
|
||||
template<typename ViewTraits, typename T>
|
||||
typename std::enable_if<ViewTraits::Arguments::Last::IsResponder::Value &&
|
||||
ViewTraits::Arguments::PlaceholdersCount == 2, void>::type
|
||||
responseImpl(T &boundViewHandler,
|
||||
const QHttpServerRequest &request,
|
||||
QTcpSocket *socket)
|
||||
{
|
||||
boundViewHandler(request, makeResponder(request, socket));
|
||||
}
|
||||
|
||||
template<typename ViewTraits, typename T>
|
||||
typename std::enable_if<ViewTraits::Arguments::Last::IsResponder::Value &&
|
||||
ViewTraits::Arguments::PlaceholdersCount == 1, void>::type
|
||||
responseImpl(T &boundViewHandler,
|
||||
const QHttpServerRequest &request,
|
||||
QTcpSocket *socket)
|
||||
{
|
||||
boundViewHandler(makeResponder(request, socket));
|
||||
}
|
||||
|
||||
bool handleRequest(const QHttpServerRequest &request, QTcpSocket *socket) override final;
|
||||
|
||||
void sendResponse(QHttpServerResponse &&response,
|
||||
const QHttpServerRequest &request,
|
||||
QTcpSocket *socket);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVER_H
|
||||
69
src/qthttpserver/src/httpserver/qhttpserver_p.h
Normal file
69
src/qthttpserver/src/httpserver/qhttpserver_p.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVER_P_H
|
||||
#define QHTTPSERVER_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists for the convenience
|
||||
// of QHttpServer. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
|
||||
#include <private/qabstracthttpserver_p.h>
|
||||
|
||||
#include <QtHttpServer/qhttpserver.h>
|
||||
#include <QtHttpServer/qhttpserverresponse.h>
|
||||
#include <QtHttpServer/qhttpserverrequest.h>
|
||||
#include <QtHttpServer/qhttpserverrouter.h>
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#include <list>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttpServerPrivate: public QAbstractHttpServerPrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QHttpServer)
|
||||
|
||||
public:
|
||||
QHttpServerPrivate() = default;
|
||||
|
||||
QHttpServerRouter router;
|
||||
std::list<QHttpServer::AfterRequestHandler> afterRequestHandlers;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVER_P_H
|
||||
166
src/qthttpserver/src/httpserver/qhttpserverfutureresponse.cpp
Normal file
166
src/qthttpserver/src/httpserver/qhttpserverfutureresponse.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Mikhail Svetkin <mikhail.svetkin@gmail.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qhttpserverfutureresponse.h"
|
||||
|
||||
#include <QtCore/qfuture.h>
|
||||
#include <QtCore/qfuturewatcher.h>
|
||||
|
||||
#include <QtNetwork/qtcpsocket.h>
|
||||
|
||||
#include <QtHttpServer/qhttpserverresponder.h>
|
||||
|
||||
#include <private/qhttpserverresponse_p.h>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\class QHttpServerFutureResponse
|
||||
\brief QHttpServerFutureResponse is a simplified API for asynchronous responses.
|
||||
|
||||
\code
|
||||
|
||||
QHttpServer server;
|
||||
|
||||
server.route("/feature/", [] (int id) -> QHttpServerFutureResponse {
|
||||
auto future = QtConcurrent::run([] () {
|
||||
return QHttpServerResponse("the future is coming");
|
||||
});
|
||||
|
||||
return future;
|
||||
});
|
||||
server.listen();
|
||||
|
||||
\endcode
|
||||
*/
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
|
||||
QHttpServerResponse QFutureInterface<QHttpServerResponse>::takeResult()
|
||||
{
|
||||
if (isCanceled()) {
|
||||
exceptionStore().throwPossibleException();
|
||||
return QHttpServerResponse::StatusCode::NotFound;
|
||||
}
|
||||
// Note: we wait for all, this is intentional,
|
||||
// not to mess with other unready results.
|
||||
waitForResult(-1);
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||
std::lock_guard<QMutex> locker{*mutex()};
|
||||
#else
|
||||
std::lock_guard<QMutex> locker{mutex(0)};
|
||||
#endif
|
||||
QtPrivate::ResultIteratorBase position = resultStoreBase().resultAt(0);
|
||||
auto ret = std::move_if_noexcept(
|
||||
*const_cast<QHttpServerResponse *>(position.pointer<QHttpServerResponse>()));
|
||||
resultStoreBase().template clear<QHttpServerResponse>();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
|
||||
struct QResponseWatcher : public QFutureWatcher<QHttpServerResponse>
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QResponseWatcher(QHttpServerResponder &&_responder)
|
||||
: QFutureWatcher<QHttpServerResponse>(),
|
||||
responder(std::move(_responder)) {
|
||||
}
|
||||
|
||||
QHttpServerResponder responder;
|
||||
};
|
||||
|
||||
class QHttpServerFutureResponsePrivate : public QHttpServerResponsePrivate
|
||||
{
|
||||
public:
|
||||
QHttpServerFutureResponsePrivate(const QFuture<QHttpServerResponse> &futureResponse)
|
||||
: QHttpServerResponsePrivate(),
|
||||
futureResp(futureResponse)
|
||||
{
|
||||
}
|
||||
|
||||
QFuture<QHttpServerResponse> futureResp;
|
||||
};
|
||||
|
||||
/*!
|
||||
Constructs a new QHttpServerFutureResponse with the \a future response.
|
||||
*/
|
||||
QHttpServerFutureResponse::QHttpServerFutureResponse(const QFuture<QHttpServerResponse> &futureResp)
|
||||
: QHttpServerFutureResponse(new QHttpServerFutureResponsePrivate{futureResp})
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
QHttpServerFutureResponse::QHttpServerFutureResponse(QHttpServerFutureResponsePrivate *d)
|
||||
: QHttpServerResponse(d)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\reimp
|
||||
*/
|
||||
void QHttpServerFutureResponse::write(QHttpServerResponder &&responder) const
|
||||
{
|
||||
if (!d_ptr->derived) {
|
||||
QHttpServerResponse::write(std::move(responder));
|
||||
return;
|
||||
}
|
||||
|
||||
Q_D(const QHttpServerFutureResponse);
|
||||
|
||||
auto socket = responder.socket();
|
||||
auto futureWatcher = new QResponseWatcher(std::move(responder));
|
||||
|
||||
QObject::connect(socket, &QObject::destroyed,
|
||||
futureWatcher, &QObject::deleteLater);
|
||||
QObject::connect(futureWatcher, &QFutureWatcherBase::finished,
|
||||
socket,
|
||||
[futureWatcher] () mutable {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
auto resp = futureWatcher->future().d.takeResult();
|
||||
#else
|
||||
auto resp = futureWatcher->future().takeResult();
|
||||
#endif
|
||||
resp.write(std::move(futureWatcher->responder));
|
||||
futureWatcher->deleteLater();
|
||||
});
|
||||
|
||||
futureWatcher->setFuture(d->futureResp);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "qhttpserverfutureresponse.moc"
|
||||
162
src/qthttpserver/src/httpserver/qhttpserverfutureresponse.h
Normal file
162
src/qthttpserver/src/httpserver/qhttpserverfutureresponse.h
Normal file
@@ -0,0 +1,162 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Mikhail Svetkin <mikhail.svetkin@gmail.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERFUTURERESPONSE_H
|
||||
#define QHTTPSERVERFUTURERESPONSE_H
|
||||
|
||||
#include <QtHttpServer/qhttpserverresponse.h>
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qfuture.h>
|
||||
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
|
||||
template <>
|
||||
class QFutureInterface<QHttpServerResponse> : public QFutureInterfaceBase
|
||||
{
|
||||
public:
|
||||
QFutureInterface(State initialState = NoState)
|
||||
: QFutureInterfaceBase(initialState)
|
||||
{
|
||||
refT();
|
||||
}
|
||||
QFutureInterface(const QFutureInterface &other)
|
||||
: QFutureInterfaceBase(other)
|
||||
{
|
||||
refT();
|
||||
}
|
||||
~QFutureInterface()
|
||||
{
|
||||
if (!derefT())
|
||||
resultStoreBase().template clear<QHttpServerResponse>();
|
||||
}
|
||||
|
||||
static QFutureInterface canceledResult()
|
||||
{ return QFutureInterface(State(Started | Finished | Canceled)); }
|
||||
|
||||
QFutureInterface &operator=(const QFutureInterface &other)
|
||||
{
|
||||
other.refT();
|
||||
if (!derefT())
|
||||
resultStoreBase().template clear<QHttpServerResponse>();
|
||||
QFutureInterfaceBase::operator=(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline QFuture<QHttpServerResponse> future()
|
||||
{
|
||||
return QFuture<QHttpServerResponse>(this);
|
||||
}
|
||||
|
||||
void reportAndMoveResult(QHttpServerResponse &&result, int index = -1)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||
std::lock_guard<QMutex> locker{*mutex()};
|
||||
#else
|
||||
std::lock_guard<QMutex> locker{mutex(0)};
|
||||
#endif
|
||||
if (queryState(Canceled) || queryState(Finished))
|
||||
return;
|
||||
|
||||
QtPrivate::ResultStoreBase &store = resultStoreBase();
|
||||
|
||||
const int oldResultCount = store.count();
|
||||
const int insertIndex = store.addResult(
|
||||
index, static_cast<void *>(new QHttpServerResponse(std::move_if_noexcept(result))));
|
||||
if (!store.filterMode() || oldResultCount < store.count()) // Let's make sure it's not in pending results.
|
||||
reportResultsReady(insertIndex, store.count());
|
||||
}
|
||||
|
||||
void reportFinished()
|
||||
{
|
||||
QFutureInterfaceBase::reportFinished();
|
||||
}
|
||||
|
||||
QHttpServerResponse takeResult();
|
||||
};
|
||||
|
||||
#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
|
||||
namespace QtConcurrent {
|
||||
|
||||
template <>
|
||||
class RunFunctionTask<QHttpServerResponse> : public RunFunctionTaskBase<QHttpServerResponse>
|
||||
{
|
||||
public:
|
||||
void run() override
|
||||
{
|
||||
if (this->isCanceled()) {
|
||||
this->reportFinished();
|
||||
return;
|
||||
}
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
this->runFunctor();
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
} catch (QException &e) {
|
||||
QFutureInterface<QHttpServerResponse>::reportException(e);
|
||||
} catch (...) {
|
||||
QFutureInterface<QHttpServerResponse>::reportException(QUnhandledException());
|
||||
}
|
||||
#endif
|
||||
this->reportAndMoveResult(std::move_if_noexcept(result));
|
||||
this->reportFinished();
|
||||
}
|
||||
|
||||
QHttpServerResponse result{QHttpServerResponse::StatusCode::NotFound};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class QHttpServerFutureResponsePrivate;
|
||||
class Q_HTTPSERVER_EXPORT QHttpServerFutureResponse : public QHttpServerResponse
|
||||
{
|
||||
Q_DECLARE_PRIVATE(QHttpServerFutureResponse)
|
||||
|
||||
public:
|
||||
using QHttpServerResponse::QHttpServerResponse;
|
||||
|
||||
QHttpServerFutureResponse(const QFuture<QHttpServerResponse> &futureResponse);
|
||||
|
||||
virtual void write(QHttpServerResponder &&responder) const override;
|
||||
|
||||
protected:
|
||||
QHttpServerFutureResponse(QHttpServerFutureResponsePrivate *d);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVERFUTURERESPONSE_H
|
||||
59
src/qthttpserver/src/httpserver/qhttpserverliterals.cpp
Normal file
59
src/qthttpserver/src/httpserver/qhttpserverliterals.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Mikhail Svetkin <mikhail.svetkin@gmail.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qhttpserverliterals_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QByteArray QHttpServerLiterals::contentTypeHeader()
|
||||
{
|
||||
return QByteArrayLiteral("Content-Type");
|
||||
}
|
||||
|
||||
QByteArray QHttpServerLiterals::contentTypeXEmpty()
|
||||
{
|
||||
return QByteArrayLiteral("application/x-empty");
|
||||
}
|
||||
|
||||
QByteArray QHttpServerLiterals::contentTypeTextHtml()
|
||||
{
|
||||
return QByteArrayLiteral("text/html");
|
||||
}
|
||||
|
||||
QByteArray QHttpServerLiterals::contentTypeJson()
|
||||
{
|
||||
return QByteArrayLiteral("application/json");
|
||||
}
|
||||
|
||||
QByteArray QHttpServerLiterals::contentLengthHeader()
|
||||
{
|
||||
return QByteArrayLiteral("Content-Length");
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
62
src/qthttpserver/src/httpserver/qhttpserverliterals_p.h
Normal file
62
src/qthttpserver/src/httpserver/qhttpserverliterals_p.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Mikhail Svetkin <mikhail.svetkin@gmail.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERLITERALS_P_H
|
||||
#define QHTTPSERVERLITERALS_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists for the convenience
|
||||
// of QHttpServer. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
|
||||
#include <QtHttpServer/qthttpserverglobal.h>
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class Q_HTTPSERVER_EXPORT QHttpServerLiterals
|
||||
{
|
||||
|
||||
public:
|
||||
static QByteArray contentTypeHeader();
|
||||
static QByteArray contentTypeXEmpty();
|
||||
static QByteArray contentTypeTextHtml();
|
||||
static QByteArray contentTypeJson();
|
||||
static QByteArray contentLengthHeader();
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVERLITERALS_P_H
|
||||
319
src/qthttpserver/src/httpserver/qhttpserverrequest.cpp
Normal file
319
src/qthttpserver/src/httpserver/qhttpserverrequest.cpp
Normal file
@@ -0,0 +1,319 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qhttpserverrequest_p.h"
|
||||
|
||||
#include <QtHttpServer/qhttpserverrequest.h>
|
||||
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
#include <QtNetwork/qtcpsocket.h>
|
||||
#if QT_CONFIG(ssl)
|
||||
#include <QtNetwork/qsslsocket.h>
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
|
||||
Q_LOGGING_CATEGORY(lc, "qt.httpserver.request")
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if !defined(QT_NO_DEBUG_STREAM)
|
||||
Q_HTTPSERVER_EXPORT QDebug operator<<(QDebug debug, const QHttpServerRequest &request)
|
||||
{
|
||||
const auto oldSetting = debug.autoInsertSpaces();
|
||||
debug.nospace() << "QHttpServerRequest(";
|
||||
debug << "(Url: " << request.url() << ")";
|
||||
debug << "(Headers: " << request.headers() << ")";
|
||||
debug << ')';
|
||||
debug.setAutoInsertSpaces(oldSetting);
|
||||
return debug.maybeSpace();
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const http_parser *const httpParser)
|
||||
{
|
||||
const auto oldSetting = debug.autoInsertSpaces();
|
||||
debug.nospace() << "http_parser(" << static_cast<const void *>(httpParser) << ": ";
|
||||
debug << "HTTP " << httpParser->http_major << "." << httpParser->http_minor << " "
|
||||
<< http_method_str(http_method(httpParser->method)) << ')';
|
||||
debug.setAutoInsertSpaces(oldSetting);
|
||||
return debug.maybeSpace();
|
||||
}
|
||||
#endif
|
||||
|
||||
static const std::array<void(*)(const QString &, QUrl *), UF_MAX> parseUrlFunctions {
|
||||
[](const QString &string, QUrl *url) { url->setScheme(string); },
|
||||
[](const QString &string, QUrl *url) { url->setHost(string); },
|
||||
[](const QString &string, QUrl *url) { url->setPort(string.toInt()); },
|
||||
[](const QString &string, QUrl *url) { url->setPath(string, QUrl::TolerantMode); },
|
||||
[](const QString &string, QUrl *url) { url->setQuery(string); },
|
||||
[](const QString &string, QUrl *url) { url->setFragment(string); },
|
||||
[](const QString &string, QUrl *url) { url->setUserInfo(string); },
|
||||
};
|
||||
|
||||
http_parser_settings QHttpServerRequestPrivate::httpParserSettings {
|
||||
&QHttpServerRequestPrivate::onMessageBegin,
|
||||
&QHttpServerRequestPrivate::onUrl,
|
||||
&QHttpServerRequestPrivate::onStatus,
|
||||
&QHttpServerRequestPrivate::onHeaderField,
|
||||
&QHttpServerRequestPrivate::onHeaderValue,
|
||||
&QHttpServerRequestPrivate::onHeadersComplete,
|
||||
&QHttpServerRequestPrivate::onBody,
|
||||
&QHttpServerRequestPrivate::onMessageComplete,
|
||||
&QHttpServerRequestPrivate::onChunkHeader,
|
||||
&QHttpServerRequestPrivate::onChunkComplete
|
||||
};
|
||||
|
||||
QHttpServerRequestPrivate::QHttpServerRequestPrivate(const QHostAddress &remoteAddress)
|
||||
: remoteAddress(remoteAddress)
|
||||
{
|
||||
httpParser.data = this;
|
||||
http_parser_init(&httpParser, HTTP_REQUEST);
|
||||
}
|
||||
|
||||
QByteArray QHttpServerRequestPrivate::header(const QByteArray &key) const
|
||||
{
|
||||
return headers.value(headerHash(key)).second;
|
||||
}
|
||||
|
||||
bool QHttpServerRequestPrivate::parse(QIODevice *socket)
|
||||
{
|
||||
const auto fragment = socket->readAll();
|
||||
if (fragment.size()) {
|
||||
#if QT_CONFIG(ssl)
|
||||
auto sslSocket = qobject_cast<QSslSocket *>(socket);
|
||||
url.setScheme(sslSocket && sslSocket->isEncrypted() ? QStringLiteral("https")
|
||||
: QStringLiteral("http"));
|
||||
#else
|
||||
url.setScheme(QStringLiteral("http"));
|
||||
#endif
|
||||
const auto parsed = http_parser_execute(&httpParser,
|
||||
&httpParserSettings,
|
||||
fragment.constData(),
|
||||
size_t(fragment.size()));
|
||||
if (int(parsed) < fragment.size()) {
|
||||
qCDebug(lc, "Parse error: %d", httpParser.http_errno);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint QHttpServerRequestPrivate::headerHash(const QByteArray &key) const
|
||||
{
|
||||
return qHash(key.toLower(), headersSeed);
|
||||
}
|
||||
|
||||
void QHttpServerRequestPrivate::clear()
|
||||
{
|
||||
url.clear();
|
||||
lastHeader.clear();
|
||||
headers.clear();
|
||||
body.clear();
|
||||
}
|
||||
|
||||
bool QHttpServerRequestPrivate::parseUrl(const char *at, size_t length, bool connect, QUrl *url)
|
||||
{
|
||||
struct http_parser_url u;
|
||||
if (http_parser_parse_url(at, length, connect ? 1 : 0, &u) != 0)
|
||||
return false;
|
||||
|
||||
for (auto i = 0u; i < UF_MAX; i++) {
|
||||
if (u.field_set & (1 << i)) {
|
||||
parseUrlFunctions[i](QString::fromUtf8(at + u.field_data[i].off,
|
||||
u.field_data[i].len),
|
||||
url);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QHttpServerRequestPrivate *QHttpServerRequestPrivate::instance(http_parser *httpParser)
|
||||
{
|
||||
return static_cast<QHttpServerRequestPrivate *>(httpParser->data);
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onMessageBegin(http_parser *httpParser)
|
||||
{
|
||||
qCDebug(lc) << static_cast<void *>(httpParser);
|
||||
instance(httpParser)->state = State::OnMessageBegin;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onUrl(http_parser *httpParser, const char *at, size_t length)
|
||||
{
|
||||
qCDebug(lc) << httpParser << QString::fromUtf8(at, int(length));
|
||||
auto instance = static_cast<QHttpServerRequestPrivate *>(httpParser->data);
|
||||
instance->state = State::OnUrl;
|
||||
parseUrl(at, length, false, &instance->url);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onStatus(http_parser *httpParser, const char *at, size_t length)
|
||||
{
|
||||
qCDebug(lc) << httpParser << QString::fromUtf8(at, int(length));
|
||||
instance(httpParser)->state = State::OnStatus;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onHeaderField(http_parser *httpParser, const char *at, size_t length)
|
||||
{
|
||||
qCDebug(lc) << httpParser << QString::fromUtf8(at, int(length));
|
||||
auto i = instance(httpParser);
|
||||
i->state = State::OnHeaders;
|
||||
const auto key = QByteArray(at, int(length));
|
||||
i->headers.insert(i->headerHash(key), qMakePair(key, QByteArray()));
|
||||
i->lastHeader = key;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onHeaderValue(http_parser *httpParser, const char *at, size_t length)
|
||||
{
|
||||
qCDebug(lc) << httpParser << QString::fromUtf8(at, int(length));
|
||||
auto i = instance(httpParser);
|
||||
i->state = State::OnHeaders;
|
||||
Q_ASSERT(!i->lastHeader.isEmpty());
|
||||
const auto value = QByteArray(at, int(length));
|
||||
i->headers[i->headerHash(i->lastHeader)] = qMakePair(i->lastHeader, value);
|
||||
if (i->lastHeader.compare(QByteArrayLiteral("host"), Qt::CaseInsensitive) == 0)
|
||||
parseUrl(at, length, true, &i->url);
|
||||
#if defined(QT_DEBUG)
|
||||
i->lastHeader.clear();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onHeadersComplete(http_parser *httpParser)
|
||||
{
|
||||
qCDebug(lc) << httpParser;
|
||||
instance(httpParser)->state = State::OnHeadersComplete;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onBody(http_parser *httpParser, const char *at, size_t length)
|
||||
{
|
||||
qCDebug(lc) << httpParser << QString::fromUtf8(at, int(length));
|
||||
auto i = instance(httpParser);
|
||||
i->state = State::OnBody;
|
||||
if (i->body.isEmpty()) {
|
||||
i->body.reserve(
|
||||
static_cast<int>(httpParser->content_length) +
|
||||
static_cast<int>(length));
|
||||
}
|
||||
|
||||
i->body.append(at, int(length));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onMessageComplete(http_parser *httpParser)
|
||||
{
|
||||
qCDebug(lc) << httpParser;
|
||||
instance(httpParser)->state = State::OnMessageComplete;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onChunkHeader(http_parser *httpParser)
|
||||
{
|
||||
qCDebug(lc) << httpParser;
|
||||
instance(httpParser)->state = State::OnChunkHeader;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onChunkComplete(http_parser *httpParser)
|
||||
{
|
||||
qCDebug(lc) << httpParser;
|
||||
instance(httpParser)->state = State::OnChunkComplete;
|
||||
return 0;
|
||||
}
|
||||
|
||||
QHttpServerRequest::QHttpServerRequest(const QHostAddress &remoteAddress) :
|
||||
d(new QHttpServerRequestPrivate(remoteAddress))
|
||||
{}
|
||||
|
||||
QHttpServerRequest::~QHttpServerRequest()
|
||||
{}
|
||||
|
||||
QByteArray QHttpServerRequest::value(const QByteArray &key) const
|
||||
{
|
||||
return d->headers.value(d->headerHash(key)).second;
|
||||
}
|
||||
|
||||
QUrl QHttpServerRequest::url() const
|
||||
{
|
||||
return d->url;
|
||||
}
|
||||
|
||||
QUrlQuery QHttpServerRequest::query() const
|
||||
{
|
||||
return QUrlQuery(d->url.query());
|
||||
}
|
||||
|
||||
QHttpServerRequest::Method QHttpServerRequest::method() const
|
||||
{
|
||||
switch (d->httpParser.method) {
|
||||
case HTTP_GET:
|
||||
return QHttpServerRequest::Method::Get;
|
||||
case HTTP_PUT:
|
||||
return QHttpServerRequest::Method::Put;
|
||||
case HTTP_DELETE:
|
||||
return QHttpServerRequest::Method::Delete;
|
||||
case HTTP_POST:
|
||||
return QHttpServerRequest::Method::Post;
|
||||
case HTTP_HEAD:
|
||||
return QHttpServerRequest::Method::Head;
|
||||
case HTTP_OPTIONS:
|
||||
return QHttpServerRequest::Method::Options;
|
||||
case HTTP_PATCH:
|
||||
return QHttpServerRequest::Method::Patch;
|
||||
case HTTP_CONNECT:
|
||||
return QHttpServerRequest::Method::Connect;
|
||||
default:
|
||||
return QHttpServerRequest::Method::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
QVariantMap QHttpServerRequest::headers() const
|
||||
{
|
||||
QVariantMap ret;
|
||||
for (auto it : d->headers)
|
||||
ret.insert(QString::fromUtf8(it.first), it.second);
|
||||
return ret;
|
||||
}
|
||||
|
||||
QByteArray QHttpServerRequest::body() const
|
||||
{
|
||||
return d->body;
|
||||
}
|
||||
|
||||
QHostAddress QHttpServerRequest::remoteAddress() const
|
||||
{
|
||||
return d->remoteAddress;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
108
src/qthttpserver/src/httpserver/qhttpserverrequest.h
Normal file
108
src/qthttpserver/src/httpserver/qhttpserverrequest.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERREQUEST_H
|
||||
#define QHTTPSERVERREQUEST_H
|
||||
|
||||
#include <QtHttpServer/qthttpserverglobal.h>
|
||||
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qurl.h>
|
||||
#include <QtCore/qurlquery.h>
|
||||
#include <QtNetwork/qhostaddress.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QRegularExpression;
|
||||
class QString;
|
||||
class QTcpSocket;
|
||||
|
||||
class QHttpServerRequestPrivate;
|
||||
class Q_HTTPSERVER_EXPORT QHttpServerRequest
|
||||
{
|
||||
friend class QAbstractHttpServerPrivate;
|
||||
friend class QHttpServerResponse;
|
||||
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
virtual ~QHttpServerRequest();
|
||||
|
||||
enum class Method
|
||||
{
|
||||
Unknown = 0x0000,
|
||||
Get = 0x0001,
|
||||
Put = 0x0002,
|
||||
Delete = 0x0004,
|
||||
Post = 0x0008,
|
||||
Head = 0x0010,
|
||||
Options = 0x0020,
|
||||
Patch = 0x0040,
|
||||
Connect = 0x0080,
|
||||
|
||||
All = Get | Put | Delete | Post | Head | Options | Patch | Connect,
|
||||
|
||||
// Include upper-case aliases for the sake of parsing from strings:
|
||||
GET = Get,
|
||||
PUT = Put,
|
||||
DELETE = Delete,
|
||||
POST = Post,
|
||||
HEAD = Head,
|
||||
OPTIONS = Options,
|
||||
PATCH = Patch,
|
||||
CONNECT = Connect
|
||||
};
|
||||
Q_ENUM(Method)
|
||||
Q_DECLARE_FLAGS(Methods, Method)
|
||||
Q_FLAG(Methods)
|
||||
|
||||
QByteArray value(const QByteArray &key) const;
|
||||
QUrl url() const;
|
||||
QUrlQuery query() const;
|
||||
Method method() const;
|
||||
QVariantMap headers() const;
|
||||
QByteArray body() const;
|
||||
QHostAddress remoteAddress() const;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QHttpServerRequest)
|
||||
|
||||
#if !defined(QT_NO_DEBUG_STREAM)
|
||||
friend Q_HTTPSERVER_EXPORT QDebug operator<<(QDebug debug, const QHttpServerRequest &request);
|
||||
#endif
|
||||
|
||||
explicit QHttpServerRequest(const QHostAddress &remoteAddress);
|
||||
|
||||
QScopedPointer<QHttpServerRequestPrivate> d;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVERREQUEST_H
|
||||
113
src/qthttpserver/src/httpserver/qhttpserverrequest_p.h
Normal file
113
src/qthttpserver/src/httpserver/qhttpserverrequest_p.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERREQUEST_P_H
|
||||
#define QHTTPSERVERREQUEST_P_H
|
||||
|
||||
#include <QtHttpServer/qhttpserverrequest.h>
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qpair.h>
|
||||
#include <QtCore/qregularexpression.h>
|
||||
#include <QtCore/qshareddata.h>
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtCore/qurl.h>
|
||||
#include <QtNetwork/qhostaddress.h>
|
||||
|
||||
#include "../3rdparty/http-parser/http_parser.h"
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists for the convenience
|
||||
// of QHttpServer. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttpServerRequestPrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
QHttpServerRequestPrivate(const QHostAddress &remoteAddress);
|
||||
|
||||
quint16 port = 0;
|
||||
enum class State {
|
||||
NotStarted,
|
||||
OnMessageBegin,
|
||||
OnUrl,
|
||||
OnStatus,
|
||||
OnHeaders,
|
||||
OnHeadersComplete,
|
||||
OnBody,
|
||||
OnMessageComplete,
|
||||
OnChunkHeader,
|
||||
OnChunkComplete
|
||||
} state = State::NotStarted;
|
||||
QByteArray body;
|
||||
|
||||
QUrl url;
|
||||
|
||||
http_parser httpParser;
|
||||
|
||||
QByteArray header(const QByteArray &key) const;
|
||||
bool parse(QIODevice *socket);
|
||||
|
||||
QByteArray lastHeader;
|
||||
QMap<uint, QPair<QByteArray, QByteArray>> headers;
|
||||
const uint headersSeed = uint(qGlobalQHashSeed());
|
||||
uint headerHash(const QByteArray &key) const;
|
||||
|
||||
void clear();
|
||||
QHostAddress remoteAddress;
|
||||
bool handling{false};
|
||||
|
||||
private:
|
||||
static http_parser_settings httpParserSettings;
|
||||
static bool parseUrl(const char *at, size_t length, bool connect, QUrl *url);
|
||||
|
||||
static QHttpServerRequestPrivate *instance(http_parser *httpParser);
|
||||
|
||||
static int onMessageBegin(http_parser *httpParser);
|
||||
static int onUrl(http_parser *httpParser, const char *at, size_t length);
|
||||
static int onStatus(http_parser *httpParser, const char *at, size_t length);
|
||||
static int onHeaderField(http_parser *httpParser, const char *at, size_t length);
|
||||
static int onHeaderValue(http_parser *httpParser, const char *at, size_t length);
|
||||
static int onHeadersComplete(http_parser *httpParser);
|
||||
static int onBody(http_parser *httpParser, const char *at, size_t length);
|
||||
static int onMessageComplete(http_parser *httpParser);
|
||||
static int onChunkHeader(http_parser *httpParser);
|
||||
static int onChunkComplete(http_parser *httpParser);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVERREQUEST_P_H
|
||||
413
src/qthttpserver/src/httpserver/qhttpserverresponder.cpp
Normal file
413
src/qthttpserver/src/httpserver/qhttpserverresponder.cpp
Normal file
@@ -0,0 +1,413 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtHttpServer/qhttpserverresponder.h>
|
||||
#include <QtHttpServer/qhttpserverrequest.h>
|
||||
#include <private/qhttpserverresponder_p.h>
|
||||
#include <private/qhttpserverliterals_p.h>
|
||||
#include <private/qhttpserverrequest_p.h>
|
||||
#include <QtCore/qjsondocument.h>
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
#include <QtNetwork/qtcpsocket.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "../3rdparty/http-parser/http_parser.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static const QLoggingCategory &lc()
|
||||
{
|
||||
static const QLoggingCategory category("qt.httpserver.response");
|
||||
return category;
|
||||
}
|
||||
|
||||
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||
static const std::map<QHttpServerResponder::StatusCode, QByteArray> statusString {
|
||||
#define XX(num, name, string) { static_cast<QHttpServerResponder::StatusCode>(num), QByteArrayLiteral(#string) },
|
||||
HTTP_STATUS_MAP(XX)
|
||||
#undef XX
|
||||
};
|
||||
|
||||
template <qint64 BUFFERSIZE = 512>
|
||||
struct IOChunkedTransfer
|
||||
{
|
||||
// TODO This is not the fastest implementation, as it does read & write
|
||||
// in a sequential fashion, but these operation could potentially overlap.
|
||||
// TODO Can we implement it without the buffer? Direct write to the target buffer
|
||||
// would be great.
|
||||
|
||||
const qint64 bufferSize = BUFFERSIZE;
|
||||
char buffer[BUFFERSIZE];
|
||||
qint64 beginIndex = -1;
|
||||
qint64 endIndex = -1;
|
||||
QPointer<QIODevice> source;
|
||||
const QPointer<QIODevice> sink;
|
||||
const QMetaObject::Connection bytesWrittenConnection;
|
||||
const QMetaObject::Connection readyReadConnection;
|
||||
IOChunkedTransfer(QIODevice *input, QIODevice *output) :
|
||||
source(input),
|
||||
sink(output),
|
||||
bytesWrittenConnection(QObject::connect(sink.data(), &QIODevice::bytesWritten, [this] () {
|
||||
writeToOutput();
|
||||
})),
|
||||
readyReadConnection(QObject::connect(source.data(), &QIODevice::readyRead, [this] () {
|
||||
readFromInput();
|
||||
}))
|
||||
{
|
||||
Q_ASSERT(!source->atEnd()); // TODO error out
|
||||
QObject::connect(sink.data(), &QObject::destroyed, source.data(), &QObject::deleteLater);
|
||||
QObject::connect(source.data(), &QObject::destroyed, [this] () {
|
||||
delete this;
|
||||
});
|
||||
readFromInput();
|
||||
}
|
||||
|
||||
~IOChunkedTransfer()
|
||||
{
|
||||
QObject::disconnect(bytesWrittenConnection);
|
||||
QObject::disconnect(readyReadConnection);
|
||||
}
|
||||
|
||||
inline bool isBufferEmpty()
|
||||
{
|
||||
Q_ASSERT(beginIndex <= endIndex);
|
||||
return beginIndex == endIndex;
|
||||
}
|
||||
|
||||
void readFromInput()
|
||||
{
|
||||
if (!isBufferEmpty()) // We haven't consumed all the data yet.
|
||||
return;
|
||||
beginIndex = 0;
|
||||
endIndex = source->read(buffer, bufferSize);
|
||||
if (endIndex < 0) {
|
||||
endIndex = beginIndex; // Mark the buffer as empty
|
||||
qCWarning(lc, "Error reading chunk: %s", qPrintable(source->errorString()));
|
||||
} else if (endIndex) {
|
||||
memset(buffer + endIndex, 0, sizeof(buffer) - std::size_t(endIndex));
|
||||
writeToOutput();
|
||||
}
|
||||
}
|
||||
|
||||
void writeToOutput()
|
||||
{
|
||||
if (isBufferEmpty())
|
||||
return;
|
||||
|
||||
const auto writtenBytes = sink->write(buffer + beginIndex, endIndex);
|
||||
if (writtenBytes < 0) {
|
||||
qCWarning(lc, "Error writing chunk: %s", qPrintable(sink->errorString()));
|
||||
return;
|
||||
}
|
||||
beginIndex += writtenBytes;
|
||||
if (isBufferEmpty()) {
|
||||
if (source->bytesAvailable())
|
||||
QTimer::singleShot(0, source.data(), [this]() { readFromInput(); });
|
||||
else if (source->atEnd()) // Finishing
|
||||
source->deleteLater();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
Constructs a QHttpServerResponder using the request \a request
|
||||
and the socket \a socket.
|
||||
*/
|
||||
QHttpServerResponder::QHttpServerResponder(const QHttpServerRequest &request,
|
||||
QTcpSocket *socket) :
|
||||
d_ptr(new QHttpServerResponderPrivate(request, socket))
|
||||
{
|
||||
Q_ASSERT(socket);
|
||||
}
|
||||
|
||||
/*!
|
||||
Move-constructs a QHttpServerResponder instance, making it point
|
||||
at the same object that \a other was pointing to.
|
||||
*/
|
||||
QHttpServerResponder::QHttpServerResponder(QHttpServerResponder &&other) :
|
||||
d_ptr(other.d_ptr.take())
|
||||
{}
|
||||
|
||||
/*!
|
||||
Destroys a QHttpServerResponder.
|
||||
*/
|
||||
QHttpServerResponder::~QHttpServerResponder()
|
||||
{}
|
||||
|
||||
/*!
|
||||
Answers a request with an HTTP status code \a status and
|
||||
HTTP headers \a headers. The I/O device \a data provides the body
|
||||
of the response. If \a data is sequential, the body of the
|
||||
message is sent in chunks: otherwise, the function assumes all
|
||||
the content is available and sends it all at once but the read
|
||||
is done in chunks.
|
||||
|
||||
\note This function takes the ownership of \a data.
|
||||
*/
|
||||
void QHttpServerResponder::write(QIODevice *data,
|
||||
HeaderList headers,
|
||||
StatusCode status)
|
||||
{
|
||||
Q_D(QHttpServerResponder);
|
||||
Q_ASSERT(d->socket);
|
||||
QScopedPointer<QIODevice, QScopedPointerDeleteLater> input(data);
|
||||
|
||||
input->setParent(nullptr);
|
||||
if (!input->isOpen()) {
|
||||
if (!input->open(QIODevice::ReadOnly)) {
|
||||
// TODO Add developer error handling
|
||||
qCDebug(lc, "500: Could not open device %s", qPrintable(input->errorString()));
|
||||
write(StatusCode::InternalServerError);
|
||||
return;
|
||||
}
|
||||
} else if (!(input->openMode() & QIODevice::ReadOnly)) {
|
||||
// TODO Add developer error handling
|
||||
qCDebug(lc) << "500: Device is opened in a wrong mode" << input->openMode();
|
||||
write(StatusCode::InternalServerError);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!d->socket->isOpen()) {
|
||||
qCWarning(lc, "Cannot write to socket. It's disconnected");
|
||||
return;
|
||||
}
|
||||
|
||||
writeStatusLine(status);
|
||||
|
||||
if (!input->isSequential()) { // Non-sequential QIODevice should know its data size
|
||||
writeHeader(QHttpServerLiterals::contentLengthHeader(),
|
||||
QByteArray::number(input->size()));
|
||||
}
|
||||
|
||||
for (auto &&header : headers)
|
||||
writeHeader(header.first, header.second);
|
||||
|
||||
d->socket->write("\r\n");
|
||||
|
||||
if (input->atEnd()) {
|
||||
qCDebug(lc, "No more data available.");
|
||||
return;
|
||||
}
|
||||
|
||||
// input takes ownership of the IOChunkedTransfer pointer inside his constructor
|
||||
new IOChunkedTransfer<>(input.take(), d->socket);
|
||||
}
|
||||
|
||||
/*!
|
||||
Answers a request with an HTTP status code \a status and a
|
||||
MIME type \a mimeType. The I/O device \a data provides the body
|
||||
of the response. If \a data is sequential, the body of the
|
||||
message is sent in chunks: otherwise, the function assumes all
|
||||
the content is available and sends it all at once but the read
|
||||
is done in chunks.
|
||||
|
||||
\note This function takes the ownership of \a data.
|
||||
*/
|
||||
void QHttpServerResponder::write(QIODevice *data,
|
||||
const QByteArray &mimeType,
|
||||
StatusCode status)
|
||||
{
|
||||
write(data,
|
||||
{{ QHttpServerLiterals::contentTypeHeader(), mimeType }},
|
||||
status);
|
||||
}
|
||||
|
||||
/*!
|
||||
Answers a request with an HTTP status code \a status, JSON
|
||||
document \a document and HTTP headers \a headers.
|
||||
|
||||
Note: This function sets HTTP Content-Type header as "application/json".
|
||||
*/
|
||||
void QHttpServerResponder::write(const QJsonDocument &document,
|
||||
HeaderList headers,
|
||||
StatusCode status)
|
||||
{
|
||||
const QByteArray &json = document.toJson();
|
||||
|
||||
writeStatusLine(status);
|
||||
writeHeader(QHttpServerLiterals::contentTypeHeader(),
|
||||
QHttpServerLiterals::contentTypeJson());
|
||||
writeHeader(QHttpServerLiterals::contentLengthHeader(),
|
||||
QByteArray::number(json.size()));
|
||||
writeHeaders(std::move(headers));
|
||||
writeBody(document.toJson());
|
||||
}
|
||||
|
||||
/*!
|
||||
Answers a request with an HTTP status code \a status, and JSON
|
||||
document \a document.
|
||||
|
||||
Note: This function sets HTTP Content-Type header as "application/json".
|
||||
*/
|
||||
void QHttpServerResponder::write(const QJsonDocument &document,
|
||||
StatusCode status)
|
||||
{
|
||||
write(document, {}, status);
|
||||
}
|
||||
|
||||
/*!
|
||||
Answers a request with an HTTP status code \a status,
|
||||
HTTP Headers \a headers and a body \a data.
|
||||
|
||||
Note: This function sets HTTP Content-Length header.
|
||||
*/
|
||||
void QHttpServerResponder::write(const QByteArray &data,
|
||||
HeaderList headers,
|
||||
StatusCode status)
|
||||
{
|
||||
writeStatusLine(status);
|
||||
|
||||
for (auto &&header : headers)
|
||||
writeHeader(header.first, header.second);
|
||||
|
||||
writeHeader(QHttpServerLiterals::contentLengthHeader(),
|
||||
QByteArray::number(data.size()));
|
||||
writeBody(data);
|
||||
}
|
||||
|
||||
/*!
|
||||
Answers a request with an HTTP status code \a status, a
|
||||
MIME type \a mimeType and a body \a data.
|
||||
*/
|
||||
void QHttpServerResponder::write(const QByteArray &data,
|
||||
const QByteArray &mimeType,
|
||||
StatusCode status)
|
||||
{
|
||||
write(data,
|
||||
{{ QHttpServerLiterals::contentTypeHeader(), mimeType }},
|
||||
status);
|
||||
}
|
||||
|
||||
/*!
|
||||
Answers a request with an HTTP status code \a status.
|
||||
|
||||
Note: This function sets HTTP Content-Type header as "application/x-empty".
|
||||
*/
|
||||
void QHttpServerResponder::write(StatusCode status)
|
||||
{
|
||||
write(QByteArray(), QHttpServerLiterals::contentTypeXEmpty(), status);
|
||||
}
|
||||
|
||||
/*!
|
||||
Answers a request with an HTTP status code \a status and
|
||||
HTTP Headers \a headers.
|
||||
*/
|
||||
void QHttpServerResponder::write(HeaderList headers, StatusCode status)
|
||||
{
|
||||
write(QByteArray(), std::move(headers), status);
|
||||
}
|
||||
|
||||
/*!
|
||||
This function writes HTTP status line with an HTTP status code \a status
|
||||
and an HTTP version \a version.
|
||||
*/
|
||||
void QHttpServerResponder::writeStatusLine(StatusCode status,
|
||||
const QPair<quint8, quint8> &version)
|
||||
{
|
||||
Q_D(const QHttpServerResponder);
|
||||
Q_ASSERT(d->socket->isOpen());
|
||||
d->socket->write("HTTP/");
|
||||
d->socket->write(QByteArray::number(version.first));
|
||||
d->socket->write(".");
|
||||
d->socket->write(QByteArray::number(version.second));
|
||||
d->socket->write(" ");
|
||||
d->socket->write(QByteArray::number(quint32(status)));
|
||||
d->socket->write(" ");
|
||||
d->socket->write(statusString.at(status));
|
||||
d->socket->write("\r\n");
|
||||
}
|
||||
|
||||
/*!
|
||||
This function writes an HTTP header \a header
|
||||
with \a value.
|
||||
*/
|
||||
void QHttpServerResponder::writeHeader(const QByteArray &header,
|
||||
const QByteArray &value)
|
||||
{
|
||||
Q_D(const QHttpServerResponder);
|
||||
Q_ASSERT(d->socket->isOpen());
|
||||
d->socket->write(header);
|
||||
d->socket->write(": ");
|
||||
d->socket->write(value);
|
||||
d->socket->write("\r\n");
|
||||
}
|
||||
|
||||
/*!
|
||||
This function writes HTTP headers \a headers.
|
||||
*/
|
||||
void QHttpServerResponder::writeHeaders(HeaderList headers)
|
||||
{
|
||||
for (auto &&header : headers)
|
||||
writeHeader(header.first, header.second);
|
||||
}
|
||||
|
||||
/*!
|
||||
This function writes HTTP body \a body with size \a size.
|
||||
*/
|
||||
void QHttpServerResponder::writeBody(const char *body, qint64 size)
|
||||
{
|
||||
Q_D(QHttpServerResponder);
|
||||
Q_ASSERT(d->socket->isOpen());
|
||||
|
||||
if (!d->bodyStarted) {
|
||||
d->socket->write("\r\n");
|
||||
d->bodyStarted = true;
|
||||
}
|
||||
|
||||
d->socket->write(body, size);
|
||||
}
|
||||
|
||||
/*!
|
||||
This function writes HTTP body \a body.
|
||||
*/
|
||||
void QHttpServerResponder::writeBody(const char *body)
|
||||
{
|
||||
writeBody(body, qstrlen(body));
|
||||
}
|
||||
|
||||
/*!
|
||||
This function writes HTTP body \a body.
|
||||
*/
|
||||
void QHttpServerResponder::writeBody(const QByteArray &body)
|
||||
{
|
||||
writeBody(body.constData(), body.size());
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the socket used.
|
||||
*/
|
||||
QTcpSocket *QHttpServerResponder::socket() const
|
||||
{
|
||||
Q_D(const QHttpServerResponder);
|
||||
return d->socket;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
187
src/qthttpserver/src/httpserver/qhttpserverresponder.h
Normal file
187
src/qthttpserver/src/httpserver/qhttpserverresponder.h
Normal file
@@ -0,0 +1,187 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERRESPONDER_H
|
||||
#define QHTTPSERVERRESPONDER_H
|
||||
|
||||
#include <QtHttpServer/qthttpserverglobal.h>
|
||||
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qpair.h>
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtCore/qscopedpointer.h>
|
||||
#include <QtCore/qmetatype.h>
|
||||
#include <QtCore/qmimetype.h>
|
||||
|
||||
#include <utility>
|
||||
#include <initializer_list>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QTcpSocket;
|
||||
class QHttpServerRequest;
|
||||
|
||||
class QHttpServerResponderPrivate;
|
||||
class Q_HTTPSERVER_EXPORT QHttpServerResponder final
|
||||
{
|
||||
Q_DECLARE_PRIVATE(QHttpServerResponder)
|
||||
|
||||
friend class QAbstractHttpServer;
|
||||
|
||||
public:
|
||||
enum class StatusCode {
|
||||
// 1xx: Informational
|
||||
Continue = 100,
|
||||
SwitchingProtocols,
|
||||
Processing,
|
||||
|
||||
// 2xx: Success
|
||||
Ok = 200,
|
||||
Created,
|
||||
Accepted,
|
||||
NonAuthoritativeInformation,
|
||||
NoContent,
|
||||
ResetContent,
|
||||
PartialContent,
|
||||
MultiStatus,
|
||||
AlreadyReported,
|
||||
IMUsed = 226,
|
||||
|
||||
// 3xx: Redirection
|
||||
MultipleChoices = 300,
|
||||
MovedPermanently,
|
||||
Found,
|
||||
SeeOther,
|
||||
NotModified,
|
||||
UseProxy,
|
||||
// 306: not used, was proposed as "Switch Proxy" but never standardized
|
||||
TemporaryRedirect = 307,
|
||||
PermanentRedirect,
|
||||
|
||||
// 4xx: Client Error
|
||||
BadRequest = 400,
|
||||
Unauthorized,
|
||||
PaymentRequired,
|
||||
Forbidden,
|
||||
NotFound,
|
||||
MethodNotAllowed,
|
||||
NotAcceptable,
|
||||
ProxyAuthenticationRequired,
|
||||
RequestTimeout,
|
||||
Conflict,
|
||||
Gone,
|
||||
LengthRequired,
|
||||
PreconditionFailed,
|
||||
PayloadTooLarge,
|
||||
UriTooLong,
|
||||
UnsupportedMediaType,
|
||||
RequestRangeNotSatisfiable,
|
||||
ExpectationFailed,
|
||||
ImATeapot,
|
||||
MisdirectedRequest = 421,
|
||||
UnprocessableEntity,
|
||||
Locked,
|
||||
FailedDependency,
|
||||
UpgradeRequired = 426,
|
||||
PreconditionRequired = 428,
|
||||
TooManyRequests,
|
||||
RequestHeaderFieldsTooLarge = 431,
|
||||
UnavailableForLegalReasons = 451,
|
||||
|
||||
// 5xx: Server Error
|
||||
InternalServerError = 500,
|
||||
NotImplemented,
|
||||
BadGateway,
|
||||
ServiceUnavailable,
|
||||
GatewayTimeout,
|
||||
HttpVersionNotSupported,
|
||||
VariantAlsoNegotiates,
|
||||
InsufficientStorage,
|
||||
LoopDetected,
|
||||
NotExtended = 510,
|
||||
NetworkAuthenticationRequired,
|
||||
NetworkConnectTimeoutError = 599,
|
||||
};
|
||||
|
||||
using HeaderList = std::initializer_list<std::pair<QByteArray, QByteArray>>;
|
||||
|
||||
QHttpServerResponder(QHttpServerResponder &&other);
|
||||
~QHttpServerResponder();
|
||||
|
||||
void write(QIODevice *data,
|
||||
HeaderList headers,
|
||||
StatusCode status = StatusCode::Ok);
|
||||
|
||||
void write(QIODevice *data,
|
||||
const QByteArray &mimeType,
|
||||
StatusCode status = StatusCode::Ok);
|
||||
|
||||
void write(const QJsonDocument &document,
|
||||
HeaderList headers,
|
||||
StatusCode status = StatusCode::Ok);
|
||||
|
||||
void write(const QJsonDocument &document,
|
||||
StatusCode status = StatusCode::Ok);
|
||||
|
||||
void write(const QByteArray &data,
|
||||
HeaderList headers,
|
||||
StatusCode status = StatusCode::Ok);
|
||||
|
||||
void write(const QByteArray &data,
|
||||
const QByteArray &mimeType,
|
||||
StatusCode status = StatusCode::Ok);
|
||||
|
||||
void write(HeaderList headers, StatusCode status = StatusCode::Ok);
|
||||
void write(StatusCode status = StatusCode::Ok);
|
||||
|
||||
|
||||
void writeStatusLine(StatusCode status = StatusCode::Ok,
|
||||
const QPair<quint8, quint8> &version = qMakePair(1u, 1u));
|
||||
|
||||
void writeHeader(const QByteArray &key, const QByteArray &value);
|
||||
void writeHeaders(HeaderList headers);
|
||||
|
||||
void writeBody(const char *body, qint64 size);
|
||||
void writeBody(const char *body);
|
||||
void writeBody(const QByteArray &body);
|
||||
|
||||
QTcpSocket *socket() const;
|
||||
|
||||
private:
|
||||
QHttpServerResponder(const QHttpServerRequest &request, QTcpSocket *socket);
|
||||
|
||||
QScopedPointer<QHttpServerResponderPrivate> d_ptr;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
Q_DECLARE_METATYPE(QHttpServerResponder::StatusCode)
|
||||
|
||||
#endif // QHTTPSERVERRESPONDER_H
|
||||
73
src/qthttpserver/src/httpserver/qhttpserverresponder_p.h
Normal file
73
src/qthttpserver/src/httpserver/qhttpserverresponder_p.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERRESPONDER_P_H
|
||||
#define QHTTPSERVERRESPONDER_P_H
|
||||
|
||||
#include <QtHttpServer/qthttpserverglobal.h>
|
||||
#include <QtHttpServer/qhttpserverrequest.h>
|
||||
#include <QtHttpServer/qhttpserverresponder.h>
|
||||
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtCore/qpair.h>
|
||||
#include <QtCore/qpointer.h>
|
||||
#include <QtCore/qsysinfo.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists for the convenience
|
||||
// of QHttpServer. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttpServerResponderPrivate
|
||||
{
|
||||
public:
|
||||
QHttpServerResponderPrivate(const QHttpServerRequest &request, QTcpSocket *const socket)
|
||||
: request(request), socket(socket) {}
|
||||
|
||||
const QHttpServerRequest &request;
|
||||
#if defined(QT_DEBUG)
|
||||
const QPointer<QTcpSocket> socket;
|
||||
#else
|
||||
QTcpSocket *const socket;
|
||||
#endif
|
||||
bool bodyStarted{false};
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVERRESPONDER_P_H
|
||||
370
src/qthttpserver/src/httpserver/qhttpserverresponse.cpp
Normal file
370
src/qthttpserver/src/httpserver/qhttpserverresponse.cpp
Normal file
@@ -0,0 +1,370 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtHttpServer/qhttpserverresponse.h>
|
||||
|
||||
#include <private/qhttpserverliterals_p.h>
|
||||
#include <private/qhttpserverresponse_p.h>
|
||||
#include <private/qhttpserverresponder_p.h>
|
||||
|
||||
#include <QtCore/qfile.h>
|
||||
#include <QtCore/qjsondocument.h>
|
||||
#include <QtCore/qjsonobject.h>
|
||||
#include <QtCore/qmimedatabase.h>
|
||||
#include <QtNetwork/qtcpsocket.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QHttpServerResponsePrivate::QHttpServerResponsePrivate(
|
||||
QByteArray &&d, const QHttpServerResponse::StatusCode sc)
|
||||
: data(std::move(d)),
|
||||
statusCode(sc)
|
||||
{ }
|
||||
|
||||
QHttpServerResponsePrivate::QHttpServerResponsePrivate(const QHttpServerResponse::StatusCode sc)
|
||||
: statusCode(sc)
|
||||
{ }
|
||||
|
||||
QHttpServerResponse::QHttpServerResponse(QHttpServerResponse &&other) noexcept
|
||||
: d_ptr(other.d_ptr.take())
|
||||
{
|
||||
}
|
||||
|
||||
QHttpServerResponse& QHttpServerResponse::operator=(QHttpServerResponse &&other) noexcept
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
qSwap(d_ptr, other.d_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
QHttpServerResponse::QHttpServerResponse(
|
||||
const QHttpServerResponse::StatusCode statusCode)
|
||||
: QHttpServerResponse(QHttpServerLiterals::contentTypeXEmpty(),
|
||||
QByteArray(),
|
||||
statusCode)
|
||||
{
|
||||
}
|
||||
|
||||
QHttpServerResponse::QHttpServerResponse(const char *data)
|
||||
: QHttpServerResponse(QByteArray::fromRawData(data, qstrlen(data)))
|
||||
{
|
||||
}
|
||||
|
||||
QHttpServerResponse::QHttpServerResponse(const QString &data)
|
||||
: QHttpServerResponse(data.toUtf8())
|
||||
{
|
||||
}
|
||||
|
||||
QHttpServerResponse::QHttpServerResponse(const QByteArray &data)
|
||||
: QHttpServerResponse(QMimeDatabase().mimeTypeForData(data).name().toLocal8Bit(), data)
|
||||
{
|
||||
}
|
||||
|
||||
QHttpServerResponse::QHttpServerResponse(QByteArray &&data)
|
||||
: QHttpServerResponse(
|
||||
QMimeDatabase().mimeTypeForData(data).name().toLocal8Bit(),
|
||||
std::move(data))
|
||||
{
|
||||
}
|
||||
|
||||
QHttpServerResponse::QHttpServerResponse(const QJsonObject &data)
|
||||
: QHttpServerResponse(QHttpServerLiterals::contentTypeJson(),
|
||||
QJsonDocument(data).toJson(QJsonDocument::Compact))
|
||||
{
|
||||
}
|
||||
|
||||
QHttpServerResponse::QHttpServerResponse(const QJsonArray &data)
|
||||
: QHttpServerResponse(QHttpServerLiterals::contentTypeJson(),
|
||||
QJsonDocument(data).toJson(QJsonDocument::Compact))
|
||||
{
|
||||
}
|
||||
|
||||
QHttpServerResponse::QHttpServerResponse(const QByteArray &mimeType,
|
||||
const QByteArray &data,
|
||||
const StatusCode status)
|
||||
: d_ptr(new QHttpServerResponsePrivate(QByteArray(data), status))
|
||||
{
|
||||
setHeader(QHttpServerLiterals::contentTypeHeader(), mimeType);
|
||||
}
|
||||
|
||||
QHttpServerResponse::QHttpServerResponse(QByteArray &&mimeType,
|
||||
const QByteArray &data,
|
||||
const StatusCode status)
|
||||
: d_ptr(new QHttpServerResponsePrivate(QByteArray(data), status))
|
||||
{
|
||||
setHeader(QHttpServerLiterals::contentTypeHeader(), std::move(mimeType));
|
||||
}
|
||||
|
||||
QHttpServerResponse::QHttpServerResponse(const QByteArray &mimeType,
|
||||
QByteArray &&data,
|
||||
const StatusCode status)
|
||||
: d_ptr(new QHttpServerResponsePrivate(std::move(data), status))
|
||||
{
|
||||
setHeader(QHttpServerLiterals::contentTypeHeader(), mimeType);
|
||||
}
|
||||
|
||||
QHttpServerResponse::QHttpServerResponse(QByteArray &&mimeType,
|
||||
QByteArray &&data,
|
||||
const StatusCode status)
|
||||
: d_ptr(new QHttpServerResponsePrivate(std::move(data), status))
|
||||
{
|
||||
setHeader(QHttpServerLiterals::contentTypeHeader(), std::move(mimeType));
|
||||
}
|
||||
|
||||
QHttpServerResponse::~QHttpServerResponse()
|
||||
{
|
||||
}
|
||||
|
||||
QHttpServerResponse QHttpServerResponse::fromFile(const QString &fileName)
|
||||
{
|
||||
QFile file(fileName);
|
||||
if (!file.open(QFile::ReadOnly))
|
||||
return QHttpServerResponse(StatusCode::NotFound);
|
||||
const QByteArray data = file.readAll();
|
||||
file.close();
|
||||
const QByteArray mimeType = QMimeDatabase().mimeTypeForFileNameAndData(fileName, data).name().toLocal8Bit();
|
||||
return QHttpServerResponse(mimeType, data);
|
||||
}
|
||||
|
||||
QHttpServerResponse::QHttpServerResponse(QHttpServerResponsePrivate *d)
|
||||
: d_ptr(d)
|
||||
{
|
||||
d->derived = true;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns response body.
|
||||
*/
|
||||
QByteArray QHttpServerResponse::data() const
|
||||
{
|
||||
Q_D(const QHttpServerResponse);
|
||||
return d->data;
|
||||
}
|
||||
|
||||
QHttpServerResponse::StatusCode QHttpServerResponse::statusCode() const
|
||||
{
|
||||
Q_D(const QHttpServerResponse);
|
||||
return d->statusCode;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns HTTP "Content-Type" header.
|
||||
|
||||
\note Default value is "text/html"
|
||||
*/
|
||||
QByteArray QHttpServerResponse::mimeType() const
|
||||
{
|
||||
Q_D(const QHttpServerResponse);
|
||||
const auto res = d->headers.find(
|
||||
QHttpServerLiterals::contentTypeHeader());
|
||||
if (res == d->headers.end())
|
||||
return QHttpServerLiterals::contentTypeTextHtml();
|
||||
|
||||
return res->second;
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds the HTTP header with name \a name and value \a value,
|
||||
does not override any previously set headers.
|
||||
*/
|
||||
void QHttpServerResponse::addHeader(QByteArray &&name, QByteArray &&value)
|
||||
{
|
||||
Q_D(QHttpServerResponse);
|
||||
d->headers.emplace(std::move(name), std::move(value));
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds the HTTP header with name \a name and value \a value,
|
||||
does not override any previously set headers.
|
||||
*/
|
||||
void QHttpServerResponse::addHeader(QByteArray &&name, const QByteArray &value)
|
||||
{
|
||||
Q_D(QHttpServerResponse);
|
||||
d->headers.emplace(std::move(name), value);
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds the HTTP header with name \a name and value \a value,
|
||||
does not override any previously set headers.
|
||||
*/
|
||||
void QHttpServerResponse::addHeader(const QByteArray &name, QByteArray &&value)
|
||||
{
|
||||
Q_D(QHttpServerResponse);
|
||||
d->headers.emplace(name, std::move(value));
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds the HTTP header with name \a name and value \a value,
|
||||
does not override any previously set headers.
|
||||
*/
|
||||
void QHttpServerResponse::addHeader(const QByteArray &name, const QByteArray &value)
|
||||
{
|
||||
Q_D(QHttpServerResponse);
|
||||
d->headers.emplace(name, value);
|
||||
}
|
||||
|
||||
void QHttpServerResponse::addHeaders(QHttpServerResponder::HeaderList headers)
|
||||
{
|
||||
for (auto &&header : headers)
|
||||
addHeader(header.first, header.second);
|
||||
}
|
||||
|
||||
/*!
|
||||
Removes the HTTP header with name \a name.
|
||||
*/
|
||||
void QHttpServerResponse::clearHeader(const QByteArray &name)
|
||||
{
|
||||
Q_D(QHttpServerResponse);
|
||||
d->headers.erase(name);
|
||||
}
|
||||
|
||||
/*!
|
||||
Removes all HTTP headers.
|
||||
*/
|
||||
void QHttpServerResponse::clearHeaders()
|
||||
{
|
||||
Q_D(QHttpServerResponse);
|
||||
d->headers.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the HTTP header with name \a name and value \a value,
|
||||
overriding any previously set headers.
|
||||
*/
|
||||
void QHttpServerResponse::setHeader(QByteArray &&name, QByteArray &&value)
|
||||
{
|
||||
clearHeader(name);
|
||||
addHeader(std::move(name), std::move(value));
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the HTTP header with name \a name and value \a value,
|
||||
overriding any previously set headers.
|
||||
*/
|
||||
void QHttpServerResponse::setHeader(QByteArray &&name, const QByteArray &value)
|
||||
{
|
||||
clearHeader(name);
|
||||
addHeader(std::move(name), value);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the HTTP header with name \a name and value \a value,
|
||||
overriding any previously set headers.
|
||||
*/
|
||||
void QHttpServerResponse::setHeader(const QByteArray &name, QByteArray &&value)
|
||||
{
|
||||
clearHeader(name);
|
||||
addHeader(name, std::move(value));
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the HTTP header with name \a name and value \a value,
|
||||
overriding any previously set headers.
|
||||
*/
|
||||
void QHttpServerResponse::setHeader(const QByteArray &name, const QByteArray &value)
|
||||
{
|
||||
clearHeader(name);
|
||||
addHeader(name, value);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the headers \a headers, overriding any previously set headers.
|
||||
*/
|
||||
void QHttpServerResponse::setHeaders(QHttpServerResponder::HeaderList headers)
|
||||
{
|
||||
for (auto &&header : headers)
|
||||
setHeader(header.first, header.second);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if the response contains an HTTP header with name \a name,
|
||||
otherwise returns false.
|
||||
*/
|
||||
bool QHttpServerResponse::hasHeader(const QByteArray &header) const
|
||||
{
|
||||
Q_D(const QHttpServerResponse);
|
||||
return d->headers.find(header) != d->headers.end();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if the response contains an HTTP header with name \a name and
|
||||
with value \a value, otherwise returns false.
|
||||
*/
|
||||
bool QHttpServerResponse::hasHeader(const QByteArray &name,
|
||||
const QByteArray &value) const
|
||||
{
|
||||
Q_D(const QHttpServerResponse);
|
||||
auto range = d->headers.equal_range(name);
|
||||
|
||||
auto condition = [&value] (const std::pair<QByteArray, QByteArray> &pair) {
|
||||
return pair.second == value;
|
||||
};
|
||||
|
||||
return std::find_if(range.first, range.second, condition) != range.second;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns values of the HTTP header with name \a name
|
||||
*/
|
||||
QVector<QByteArray> QHttpServerResponse::headers(const QByteArray &name) const
|
||||
{
|
||||
Q_D(const QHttpServerResponse);
|
||||
|
||||
QVector<QByteArray> results;
|
||||
auto range = d->headers.equal_range(name);
|
||||
|
||||
for (auto it = range.first; it != range.second; ++it)
|
||||
results.append(it->second);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/*!
|
||||
Writes HTTP response into QHttpServerResponder \a responder.
|
||||
*/
|
||||
void QHttpServerResponse::write(QHttpServerResponder &&responder) const
|
||||
{
|
||||
Q_D(const QHttpServerResponse);
|
||||
if (responder.socket()->state() != QAbstractSocket::ConnectedState)
|
||||
return;
|
||||
|
||||
responder.writeStatusLine(d->statusCode);
|
||||
|
||||
for (auto &&header : d->headers)
|
||||
responder.writeHeader(header.first, header.second);
|
||||
|
||||
responder.writeHeader(QHttpServerLiterals::contentLengthHeader(),
|
||||
QByteArray::number(d->data.size()));
|
||||
|
||||
responder.writeBody(d->data);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
129
src/qthttpserver/src/httpserver/qhttpserverresponse.h
Normal file
129
src/qthttpserver/src/httpserver/qhttpserverresponse.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERRESPONSE_H
|
||||
#define QHTTPSERVERRESPONSE_H
|
||||
|
||||
#include <QtHttpServer/qhttpserverresponder.h>
|
||||
|
||||
#include <QtCore/qscopedpointer.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
class QHttpServerResponsePrivate;
|
||||
class Q_HTTPSERVER_EXPORT QHttpServerResponse
|
||||
{
|
||||
Q_DECLARE_PRIVATE(QHttpServerResponse)
|
||||
|
||||
public:
|
||||
using StatusCode = QHttpServerResponder::StatusCode;
|
||||
|
||||
QHttpServerResponse() = delete;
|
||||
QHttpServerResponse(const QHttpServerResponse &other) = delete;
|
||||
QHttpServerResponse& operator=(const QHttpServerResponse &other) = delete;
|
||||
|
||||
QHttpServerResponse(QHttpServerResponse &&other) noexcept;
|
||||
QHttpServerResponse& operator=(QHttpServerResponse &&other) noexcept;
|
||||
|
||||
QHttpServerResponse(const StatusCode statusCode);
|
||||
|
||||
QHttpServerResponse(const char *data);
|
||||
|
||||
QHttpServerResponse(const QString &data);
|
||||
|
||||
explicit QHttpServerResponse(const QByteArray &data);
|
||||
explicit QHttpServerResponse(QByteArray &&data);
|
||||
|
||||
QHttpServerResponse(const QJsonObject &data);
|
||||
QHttpServerResponse(const QJsonArray &data);
|
||||
|
||||
QHttpServerResponse(const QByteArray &mimeType,
|
||||
const QByteArray &data,
|
||||
const StatusCode status = StatusCode::Ok);
|
||||
QHttpServerResponse(QByteArray &&mimeType,
|
||||
const QByteArray &data,
|
||||
const StatusCode status = StatusCode::Ok);
|
||||
QHttpServerResponse(const QByteArray &mimeType,
|
||||
QByteArray &&data,
|
||||
const StatusCode status = StatusCode::Ok);
|
||||
QHttpServerResponse(QByteArray &&mimeType,
|
||||
QByteArray &&data,
|
||||
const StatusCode status = StatusCode::Ok);
|
||||
|
||||
virtual ~QHttpServerResponse();
|
||||
static QHttpServerResponse fromFile(const QString &fileName);
|
||||
|
||||
QByteArray data() const;
|
||||
|
||||
QByteArray mimeType() const;
|
||||
|
||||
StatusCode statusCode() const;
|
||||
|
||||
void addHeader(QByteArray &&name, QByteArray &&value);
|
||||
void addHeader(QByteArray &&name, const QByteArray &value);
|
||||
void addHeader(const QByteArray &name, QByteArray &&value);
|
||||
void addHeader(const QByteArray &name, const QByteArray &value);
|
||||
|
||||
void addHeaders(QHttpServerResponder::HeaderList headers);
|
||||
|
||||
template<typename Container>
|
||||
void addHeaders(const Container &headerList)
|
||||
{
|
||||
for (const auto &header : headerList)
|
||||
addHeader(header.first, header.second);
|
||||
}
|
||||
|
||||
void clearHeader(const QByteArray &name);
|
||||
void clearHeaders();
|
||||
|
||||
void setHeader(QByteArray &&name, QByteArray &&value);
|
||||
void setHeader(QByteArray &&name, const QByteArray &value);
|
||||
void setHeader(const QByteArray &name, QByteArray &&value);
|
||||
void setHeader(const QByteArray &name, const QByteArray &value);
|
||||
|
||||
void setHeaders(QHttpServerResponder::HeaderList headers);
|
||||
|
||||
bool hasHeader(const QByteArray &name) const;
|
||||
bool hasHeader(const QByteArray &name, const QByteArray &value) const;
|
||||
|
||||
QVector<QByteArray> headers(const QByteArray &name) const;
|
||||
|
||||
virtual void write(QHttpServerResponder &&responder) const;
|
||||
|
||||
protected:
|
||||
QHttpServerResponse(QHttpServerResponsePrivate *d);
|
||||
|
||||
QScopedPointer<QHttpServerResponsePrivate> d_ptr;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVERRESPONSE_H
|
||||
76
src/qthttpserver/src/httpserver/qhttpserverresponse_p.h
Normal file
76
src/qthttpserver/src/httpserver/qhttpserverresponse_p.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERRESPONSE_P_H
|
||||
#define QHTTPSERVERRESPONSE_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists for the convenience
|
||||
// of QHttpServerResponse. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
|
||||
#include <private/qabstracthttpserver_p.h>
|
||||
|
||||
#include <QtHttpServer/qhttpserverresponse.h>
|
||||
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttpServerResponsePrivate
|
||||
{
|
||||
struct HashHelper {
|
||||
std::size_t operator()(const QByteArray& key) const
|
||||
{
|
||||
return qHash(key.toLower());
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
explicit QHttpServerResponsePrivate() = default;
|
||||
virtual ~QHttpServerResponsePrivate() = default;
|
||||
|
||||
QHttpServerResponsePrivate(QByteArray &&d, const QHttpServerResponse::StatusCode sc);
|
||||
QHttpServerResponsePrivate(const QHttpServerResponse::StatusCode sc);
|
||||
|
||||
QByteArray data;
|
||||
QHttpServerResponse::StatusCode statusCode;
|
||||
std::unordered_multimap<QByteArray, QByteArray, HashHelper> headers;
|
||||
bool derived{false};
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVERRESPONSE_P_H
|
||||
312
src/qthttpserver/src/httpserver/qhttpserverrouter.cpp
Normal file
312
src/qthttpserver/src/httpserver/qhttpserverrouter.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qhttpserverrouter_p.h"
|
||||
|
||||
#include <QtHttpServer/qhttpserverrouter.h>
|
||||
#include <QtHttpServer/qhttpserverrouterrule.h>
|
||||
#include <QtHttpServer/qhttpserverrequest.h>
|
||||
|
||||
#include <private/qhttpserverrouterrule_p.h>
|
||||
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
#include <QtCore/qmetatype.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_LOGGING_CATEGORY(lcRouter, "qt.httpserver.router")
|
||||
|
||||
static const QMap<int, QLatin1String> defaultConverters = {
|
||||
{ QMetaType::Int, QLatin1String("[+-]?\\d+") },
|
||||
{ QMetaType::Long, QLatin1String("[+-]?\\d+") },
|
||||
{ QMetaType::LongLong, QLatin1String("[+-]?\\d+") },
|
||||
{ QMetaType::Short, QLatin1String("[+-]?\\d+") },
|
||||
|
||||
{ QMetaType::UInt, QLatin1String("[+]?\\d+") },
|
||||
{ QMetaType::ULong, QLatin1String("[+]?\\d+") },
|
||||
{ QMetaType::ULongLong, QLatin1String("[+]?\\d+") },
|
||||
{ QMetaType::UShort, QLatin1String("[+]?\\d+") },
|
||||
|
||||
{ QMetaType::Double, QLatin1String("[+-]?(?:[0-9]+(?:[.][0-9]*)?|[.][0-9]+)") },
|
||||
{ QMetaType::Float, QLatin1String("[+-]?(?:[0-9]+(?:[.][0-9]*)?|[.][0-9]+)") },
|
||||
|
||||
{ QMetaType::QString, QLatin1String("[^/]+") },
|
||||
{ QMetaType::QByteArray, QLatin1String("[^/]+") },
|
||||
|
||||
{ QMetaType::QUrl, QLatin1String(".*") },
|
||||
|
||||
{ QMetaType::Void, QLatin1String("") },
|
||||
};
|
||||
|
||||
/*!
|
||||
\class QHttpServerRouter
|
||||
\brief Provides functions to bind a URL to a \c ViewHandler.
|
||||
|
||||
You can register \c ViewHandler as a callback for requests to a specific URL.
|
||||
Variable parts in the route can be specified by the arguments in ViewHandler.
|
||||
|
||||
\note This is a low-level routing API for an HTTP server.
|
||||
|
||||
See the following example:
|
||||
|
||||
\code
|
||||
auto pageView = [] (const quint64 page) {
|
||||
qDebug() << "page" << page;
|
||||
};
|
||||
using ViewHandler = decltype(pageView);
|
||||
|
||||
QHttpServerRouter router;
|
||||
|
||||
// register callback pageView on request "/page/<number>"
|
||||
// for example: "/page/10", "/page/15"
|
||||
router.addRoute<ViewHandler>(
|
||||
new QHttpServerRouterRule("/page/", [=] (QRegularExpressionMatch &match,
|
||||
const QHttpServerRequest &,
|
||||
QTcpSocket *) {
|
||||
auto boundView = router.bindCaptured(pageView, match);
|
||||
|
||||
// it calls pageView
|
||||
boundView();
|
||||
}));
|
||||
\endcode
|
||||
*/
|
||||
|
||||
/*! \fn template <typename Type> bool QHttpServerRouter::addConverter(const QLatin1String ®exp)
|
||||
|
||||
Adds a new converter for type \e Type matching regular expression \a regexp.
|
||||
|
||||
Automatically try to register an implicit converter from QString to \e Type.
|
||||
If there is already a converter of type \e Type, that converter's regexp
|
||||
is replaced with \a regexp.
|
||||
|
||||
\code
|
||||
struct CustomArg {
|
||||
int data = 10;
|
||||
|
||||
CustomArg() {} ;
|
||||
CustomArg(const QString &urlArg) : data(urlArg.toInt()) {}
|
||||
};
|
||||
Q_DECLARE_METATYPE(CustomArg);
|
||||
|
||||
QHttpServerRouter router;
|
||||
router.addConverter<CustomArg>(QLatin1String("[+-]?\\d+"));
|
||||
|
||||
auto pageView = [] (const CustomArg &customArg) {
|
||||
qDebug("data: %d", customArg.data);
|
||||
};
|
||||
using ViewHandler = decltype(pageView);
|
||||
|
||||
auto rule = new QHttpServerRouterRule(
|
||||
"/<arg>/<arg>/log",
|
||||
[&router, &pageView] (QRegularExpressionMatch &match,
|
||||
const QHttpServerRequest &request,
|
||||
QTcpSocket *socket) {
|
||||
// Bind and call viewHandler with match's captured string and quint32:
|
||||
router.bindCaptured(pageView, match)();
|
||||
});
|
||||
|
||||
router.addRule<ViewHandler>(rule);
|
||||
\endcode
|
||||
*/
|
||||
|
||||
/*! \fn template <typename ViewHandler, typename ViewTraits = QHttpServerRouterViewTraits<ViewHandler>> bool QHttpServerRouter::addRule(QHttpServerRouterRule *rule)
|
||||
|
||||
Adds a new \a rule.
|
||||
|
||||
Inside addRule, we determine ViewHandler arguments and generate a list of
|
||||
their QMetaType::Type ids. Then we parse the URL and replace each \c <arg>
|
||||
with a regexp for its type from the list.
|
||||
|
||||
\code
|
||||
QHttpServerRouter router;
|
||||
|
||||
using ViewHandler = decltype([] (const QString &page, const quint32 num) { });
|
||||
|
||||
auto rule = new QHttpServerRouterRule(
|
||||
"/<arg>/<arg>/log",
|
||||
[] (QRegularExpressionMatch &match,
|
||||
const QHttpServerRequest &request,
|
||||
QTcpSocket *socket) {
|
||||
});
|
||||
|
||||
router.addRule<ViewHandler>(rule);
|
||||
\endcode
|
||||
|
||||
\note This function takes over ownership of \a rule.
|
||||
*/
|
||||
|
||||
/*! \fn template<typename ViewHandler, typename ViewTraits = QHttpServerRouterViewTraits<ViewHandler>> auto bindCaptured(ViewHandler &&handler, QRegularExpressionMatch &match) const -> typename ViewTraits::BindableType
|
||||
|
||||
Supplies the \a handler with arguments derived from a URL.
|
||||
Returns the bound function that accepts whatever remaining arguments the handler may take,
|
||||
supplying them to the handler after the URL-derived values.
|
||||
Each match of the regex applied to the URL (as a string) is converted to the type
|
||||
of the handler's parameter at its position, so that passing it works.
|
||||
|
||||
\code
|
||||
QHttpServerRouter router;
|
||||
|
||||
auto pageView = [] (const QString &page, const quint32 num) {
|
||||
qDebug("page: %s, num: %d", qPrintable(page), num);
|
||||
};
|
||||
using ViewHandler = decltype(pageView);
|
||||
|
||||
auto rule = new QHttpServerRouterRule(
|
||||
"/<arg>/<arg>/log",
|
||||
[&router, &pageView] (QRegularExpressionMatch &match,
|
||||
const QHttpServerRequest &request,
|
||||
QTcpSocket *socket) {
|
||||
// Bind and call viewHandler with match's captured string and quint32:
|
||||
router.bindCaptured(pageView, match)();
|
||||
});
|
||||
|
||||
router.addRule<ViewHandler>(rule);
|
||||
\endcode
|
||||
*/
|
||||
|
||||
QHttpServerRouterPrivate::QHttpServerRouterPrivate()
|
||||
: converters(defaultConverters)
|
||||
{}
|
||||
|
||||
/*!
|
||||
Creates a QHttpServerRouter object with \c defaultConverters.
|
||||
|
||||
\sa defaultConverters()
|
||||
*/
|
||||
QHttpServerRouter::QHttpServerRouter()
|
||||
: d_ptr(new QHttpServerRouterPrivate)
|
||||
{}
|
||||
|
||||
/*!
|
||||
Destroys a QHttpServerRouter.
|
||||
*/
|
||||
QHttpServerRouter::~QHttpServerRouter()
|
||||
{}
|
||||
|
||||
/*!
|
||||
Adds a new converter for type \a type matching regular expression \a regexp.
|
||||
|
||||
If there is already a converter of type \a type, that converter's regexp
|
||||
is replaced with \a regexp.
|
||||
*/
|
||||
void QHttpServerRouter::addConverter(const int type, const QLatin1String ®exp)
|
||||
{
|
||||
Q_D(QHttpServerRouter);
|
||||
d->converters[type] = regexp;
|
||||
}
|
||||
|
||||
/*!
|
||||
Removes the converter for type \a type.
|
||||
*/
|
||||
void QHttpServerRouter::removeConverter(const int type)
|
||||
{
|
||||
Q_D(QHttpServerRouter);
|
||||
d->converters.remove(type);
|
||||
}
|
||||
|
||||
/*!
|
||||
Removes all converters.
|
||||
|
||||
\note clearConverters() does not set up \c defaultConverters.
|
||||
|
||||
\sa defaultConverters()
|
||||
*/
|
||||
void QHttpServerRouter::clearConverters()
|
||||
{
|
||||
Q_D(QHttpServerRouter);
|
||||
d->converters.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a map of converter type and regexp.
|
||||
*/
|
||||
const QMap<int, QLatin1String> &QHttpServerRouter::converters() const
|
||||
{
|
||||
Q_D(const QHttpServerRouter);
|
||||
return d->converters;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a map of default converter type and regexp.
|
||||
The following converters are available by default:
|
||||
|
||||
\value QMetaType::Int
|
||||
\value QMetaType::Long
|
||||
\value QMetaType::LongLong
|
||||
\value QMetaType::Short
|
||||
\value QMetaType::UInt
|
||||
\value QMetaType::ULong
|
||||
\value QMetaType::ULongLong
|
||||
\value QMetaType::UShort
|
||||
\value QMetaType::Double
|
||||
\value QMetaType::Float
|
||||
\value QMetaType::QString
|
||||
\value QMetaType::QByteArray
|
||||
\value QMetaType::QUrl
|
||||
\value QMetaType::Void An empty converter.
|
||||
*/
|
||||
const QMap<int, QLatin1String> &QHttpServerRouter::defaultConverters()
|
||||
{
|
||||
return ::defaultConverters;
|
||||
}
|
||||
|
||||
bool QHttpServerRouter::addRuleImpl(QHttpServerRouterRule *rule,
|
||||
const std::initializer_list<int> &types)
|
||||
{
|
||||
Q_D(QHttpServerRouter);
|
||||
|
||||
if (!rule->hasValidMethods() || !rule->createPathRegexp(types, d->converters)) {
|
||||
delete rule;
|
||||
return false;
|
||||
}
|
||||
|
||||
d->rules.emplace_back(rule);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
Handles each new request for the HTTP server.
|
||||
|
||||
Iterates through the list of rules to find the first that matches,
|
||||
then executes this rule, returning \c true. Returns \c false if no rule
|
||||
matches the request.
|
||||
*/
|
||||
bool QHttpServerRouter::handleRequest(const QHttpServerRequest &request,
|
||||
QTcpSocket *socket) const
|
||||
{
|
||||
Q_D(const QHttpServerRouter);
|
||||
for (const auto &rule : qAsConst(d->rules)) {
|
||||
if (rule->exec(request, socket))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
161
src/qthttpserver/src/httpserver/qhttpserverrouter.h
Normal file
161
src/qthttpserver/src/httpserver/qhttpserverrouter.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERROUTER_H
|
||||
#define QHTTPSERVERROUTER_H
|
||||
|
||||
#include <QtHttpServer/qthttpserverglobal.h>
|
||||
#include <QtHttpServer/qhttpserverrouterviewtraits.h>
|
||||
|
||||
#include <QtCore/qscopedpointer.h>
|
||||
#include <QtCore/qmetatype.h>
|
||||
#include <QtCore/qregularexpression.h>
|
||||
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QtPrivate {
|
||||
template<int> struct QHttpServerRouterPlaceholder {};
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace std {
|
||||
|
||||
template<int N>
|
||||
struct is_placeholder<QT_PREPEND_NAMESPACE(QtPrivate::QHttpServerRouterPlaceholder<N>)> :
|
||||
integral_constant<int , N + 1>
|
||||
{};
|
||||
|
||||
}
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QTcpSocket;
|
||||
class QHttpServerRequest;
|
||||
class QHttpServerRouterRule;
|
||||
|
||||
class QHttpServerRouterPrivate;
|
||||
class Q_HTTPSERVER_EXPORT QHttpServerRouter
|
||||
{
|
||||
Q_DECLARE_PRIVATE(QHttpServerRouter)
|
||||
|
||||
public:
|
||||
QHttpServerRouter();
|
||||
~QHttpServerRouter();
|
||||
|
||||
template<typename Type>
|
||||
bool addConverter(const QLatin1String ®exp) {
|
||||
static_assert(QMetaTypeId2<Type>::Defined,
|
||||
"Type is not registered with Qt's meta-object system: "
|
||||
"please apply Q_DECLARE_METATYPE() to it");
|
||||
|
||||
if (!QMetaType::registerConverter<QString, Type>())
|
||||
return false;
|
||||
|
||||
addConverter(qMetaTypeId<Type>(), regexp);
|
||||
return true;
|
||||
}
|
||||
|
||||
void addConverter(const int type, const QLatin1String ®exp);
|
||||
void removeConverter(const int);
|
||||
void clearConverters();
|
||||
const QMap<int, QLatin1String> &converters() const;
|
||||
|
||||
static const QMap<int, QLatin1String> &defaultConverters();
|
||||
|
||||
template<typename ViewHandler, typename ViewTraits = QHttpServerRouterViewTraits<ViewHandler>>
|
||||
bool addRule(QHttpServerRouterRule *rule)
|
||||
{
|
||||
return addRuleHelper<ViewTraits>(
|
||||
rule,
|
||||
typename ViewTraits::Arguments::Indexes{});
|
||||
}
|
||||
|
||||
template<typename ViewHandler, typename ViewTraits = QHttpServerRouterViewTraits<ViewHandler>>
|
||||
typename ViewTraits::BindableType bindCaptured(ViewHandler &&handler,
|
||||
const QRegularExpressionMatch &match) const
|
||||
{
|
||||
return bindCapturedImpl<ViewHandler, ViewTraits>(
|
||||
std::forward<ViewHandler>(handler),
|
||||
match,
|
||||
typename ViewTraits::Arguments::CapturableIndexes{},
|
||||
typename ViewTraits::Arguments::PlaceholdersIndexes{});
|
||||
}
|
||||
|
||||
bool handleRequest(const QHttpServerRequest &request,
|
||||
QTcpSocket *socket) const;
|
||||
|
||||
private:
|
||||
template<typename ViewTraits, int ... Idx>
|
||||
bool addRuleHelper(QHttpServerRouterRule *rule,
|
||||
QtPrivate::IndexesList<Idx...>)
|
||||
{
|
||||
const std::initializer_list<int> types = {
|
||||
ViewTraits::Arguments::template metaTypeId<Idx>()...};
|
||||
return addRuleImpl(rule, types);
|
||||
}
|
||||
|
||||
bool addRuleImpl(QHttpServerRouterRule *rule,
|
||||
const std::initializer_list<int> &metaTypes);
|
||||
|
||||
template<typename ViewHandler, typename ViewTraits, int ... Cx, int ... Px>
|
||||
typename std::enable_if<ViewTraits::Arguments::CapturableCount != 0, typename ViewTraits::BindableType>::type
|
||||
bindCapturedImpl(ViewHandler &&handler,
|
||||
const QRegularExpressionMatch &match,
|
||||
QtPrivate::IndexesList<Cx...>,
|
||||
QtPrivate::IndexesList<Px...>) const
|
||||
{
|
||||
return std::bind(
|
||||
std::forward<ViewHandler>(handler),
|
||||
QVariant(match.captured(Cx + 1))
|
||||
.value<typename ViewTraits::Arguments::template Arg<Cx>::CleanType>()...,
|
||||
QtPrivate::QHttpServerRouterPlaceholder<Px>{}...);
|
||||
}
|
||||
|
||||
template<typename ViewHandler, typename ViewTraits, int ... Cx, int ... Px>
|
||||
typename std::enable_if<!ViewTraits::Arguments::CapturableCount, typename ViewTraits::BindableType>::type
|
||||
bindCapturedImpl(ViewHandler &&handler,
|
||||
const QRegularExpressionMatch &,
|
||||
QtPrivate::IndexesList<Cx...>,
|
||||
QtPrivate::IndexesList<Px...>) const
|
||||
{
|
||||
return std::bind(
|
||||
std::forward<ViewHandler>(handler),
|
||||
QtPrivate::QHttpServerRouterPlaceholder<Px>{}...);
|
||||
}
|
||||
|
||||
QScopedPointer<QHttpServerRouterPrivate> d_ptr;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVERROUTER_H
|
||||
65
src/qthttpserver/src/httpserver/qhttpserverrouter_p.h
Normal file
65
src/qthttpserver/src/httpserver/qhttpserverrouter_p.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERROUTER_P_H
|
||||
#define QHTTPSERVERROUTER_P_H
|
||||
|
||||
#include <QtHttpServer/qhttpserverrouter.h>
|
||||
#include <QtHttpServer/qhttpserverrouterrule.h>
|
||||
|
||||
#include <QtCore/qmap.h>
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qstring.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists for the convenience
|
||||
// of QHttpServer. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttpServerRouterPrivate
|
||||
{
|
||||
public:
|
||||
QHttpServerRouterPrivate();
|
||||
|
||||
QMap<int, QLatin1String> converters;
|
||||
std::list<std::unique_ptr<QHttpServerRouterRule>> rules;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVERROUTER_P_H
|
||||
276
src/qthttpserver/src/httpserver/qhttpserverrouterrule.cpp
Normal file
276
src/qthttpserver/src/httpserver/qhttpserverrouterrule.cpp
Normal file
@@ -0,0 +1,276 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtHttpServer/qhttpserverrouterrule.h>
|
||||
|
||||
#include <private/qhttpserverrouterrule_p.h>
|
||||
#include <private/qhttpserverrequest_p.h>
|
||||
|
||||
#include <QtCore/qmetaobject.h>
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
#include <QtCore/qregularexpression.h>
|
||||
#include <QtCore/qstringbuilder.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_LOGGING_CATEGORY(lcRouterRule, "qt.httpserver.router.rule")
|
||||
|
||||
static const auto methodEnum = QMetaEnum::fromType<QHttpServerRequest::Method>();
|
||||
|
||||
static QHttpServerRequest::Methods strToMethods(const char *strMethods)
|
||||
{
|
||||
QHttpServerRequest::Methods methods;
|
||||
|
||||
bool ok = false;
|
||||
const int val = methodEnum.keysToValue(strMethods, &ok);
|
||||
if (ok)
|
||||
methods = static_cast<decltype(methods)>(val);
|
||||
else
|
||||
qCWarning(lcRouterRule, "Can not convert %s to QHttpServerRequest::Method", strMethods);
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
/*!
|
||||
\class QHttpServerRouterRule
|
||||
\brief The QHttpServerRouterRule is the base class for QHttpServerRouter rules.
|
||||
|
||||
Use QHttpServerRouterRule to specify expected request parameters:
|
||||
|
||||
\value path QUrl::path()
|
||||
\value HTTP methods QHttpServerRequest::Methods
|
||||
\value callback User-defined response callback
|
||||
|
||||
\note This is a low level API, see QHttpServer for higher level alternatives.
|
||||
|
||||
Example of QHttpServerRouterRule and QHttpServerRouter usage:
|
||||
|
||||
\code
|
||||
template<typename ViewHandler>
|
||||
void route(const char *path, const QHttpServerRequest::Methods methods, ViewHandler &&viewHandler)
|
||||
{
|
||||
auto rule = new QHttpServerRouterRule(
|
||||
path, methods, [this, &viewHandler] (QRegularExpressionMatch &match,
|
||||
const QHttpServerRequest &request,
|
||||
QTcpSocket *const socket) {
|
||||
auto boundViewHandler = router.bindCaptured<ViewHandler>(
|
||||
std::forward<ViewHandler>(viewHandler), match);
|
||||
// call viewHandler
|
||||
boundViewHandler();
|
||||
});
|
||||
|
||||
// QHttpServerRouter
|
||||
router.addRule<ViewHandler>(rule);
|
||||
}
|
||||
|
||||
// Valid:
|
||||
route("/user/", [] (qint64 id) { } ); // "/user/1"
|
||||
// "/user/3"
|
||||
//
|
||||
route("/user/<arg>/history", [] (qint64 id) { } ); // "/user/1/history"
|
||||
// "/user/2/history"
|
||||
//
|
||||
route("/user/<arg>/history/", [] (qint64 id, qint64 page) { } ); // "/user/1/history/1"
|
||||
// "/user/2/history/2"
|
||||
|
||||
// Invalid:
|
||||
route("/user/<arg>", [] () { } ); // ERROR: path pattern has <arg>, but ViewHandler does not have any arguments
|
||||
route("/user/\\d+", [] () { } ); // ERROR: path pattern does not support manual regexp
|
||||
\endcode
|
||||
|
||||
\note Regular expressions in the path pattern are not supported, but
|
||||
can be registered (to match a use of "<val>" to a specific type) using
|
||||
QHttpServerRouter::addConverter().
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs a rule with pathPattern \a pathPattern, and routerHandler \a routerHandler.
|
||||
|
||||
The rule accepts all HTTP methods by default.
|
||||
|
||||
\sq QHttpServerRequest::Method
|
||||
*/
|
||||
QHttpServerRouterRule::QHttpServerRouterRule(const QString &pathPattern,
|
||||
RouterHandler &&routerHandler)
|
||||
: QHttpServerRouterRule(pathPattern,
|
||||
QHttpServerRequest::Method::All,
|
||||
std::forward<RouterHandler>(routerHandler))
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a rule with pathPattern \a pathPattern, methods \a methods
|
||||
and routerHandler \a routerHandler.
|
||||
|
||||
The rule accepts any combinations of available HTTP methods.
|
||||
|
||||
\sa QHttpServerRequest::Method
|
||||
*/
|
||||
QHttpServerRouterRule::QHttpServerRouterRule(const QString &pathPattern,
|
||||
const QHttpServerRequest::Methods methods,
|
||||
RouterHandler &&routerHandler)
|
||||
: QHttpServerRouterRule(
|
||||
new QHttpServerRouterRulePrivate{pathPattern,
|
||||
methods,
|
||||
std::forward<RouterHandler>(routerHandler), {}})
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a rule with pathPattern \a pathPattern, methods \a methods
|
||||
and routerHandler \a routerHandler.
|
||||
|
||||
\note \a methods shall be joined with | as separator (not spaces or commas)
|
||||
and that either the upper-case or the capitalised form may be used.
|
||||
|
||||
\sa QMetaEnum::keysToValue
|
||||
*/
|
||||
QHttpServerRouterRule::QHttpServerRouterRule(const QString &pathPattern,
|
||||
const char *methods,
|
||||
RouterHandler &&routerHandler)
|
||||
: QHttpServerRouterRule(pathPattern,
|
||||
strToMethods(methods),
|
||||
std::forward<RouterHandler>(routerHandler))
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
QHttpServerRouterRule::QHttpServerRouterRule(QHttpServerRouterRulePrivate *d)
|
||||
: d_ptr(d)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys a QHttpServerRouterRule.
|
||||
*/
|
||||
QHttpServerRouterRule::~QHttpServerRouterRule()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if the methods is valid
|
||||
*/
|
||||
bool QHttpServerRouterRule::hasValidMethods() const
|
||||
{
|
||||
Q_D(const QHttpServerRouterRule);
|
||||
return d->methods & QHttpServerRequest::Method::All;
|
||||
}
|
||||
|
||||
/*!
|
||||
This function is called by QHttpServerRouter when a new request is received.
|
||||
*/
|
||||
bool QHttpServerRouterRule::exec(const QHttpServerRequest &request,
|
||||
QTcpSocket *socket) const
|
||||
{
|
||||
Q_D(const QHttpServerRouterRule);
|
||||
|
||||
QRegularExpressionMatch match;
|
||||
if (!matches(request, &match))
|
||||
return false;
|
||||
|
||||
d->routerHandler(match, request, socket);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
This virtual function is called by exec() to check if request matches the rule.
|
||||
*/
|
||||
bool QHttpServerRouterRule::matches(const QHttpServerRequest &request,
|
||||
QRegularExpressionMatch *match) const
|
||||
{
|
||||
Q_D(const QHttpServerRouterRule);
|
||||
|
||||
if (d->methods && !(d->methods & request.method()))
|
||||
return false;
|
||||
|
||||
*match = d->pathRegexp.match(request.url().path());
|
||||
return (match->hasMatch() && d->pathRegexp.captureCount() == match->lastCapturedIndex());
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool QHttpServerRouterRule::createPathRegexp(const std::initializer_list<int> &metaTypes,
|
||||
const QMap<int, QLatin1String> &converters)
|
||||
{
|
||||
Q_D(QHttpServerRouterRule);
|
||||
|
||||
QString pathRegexp = d->pathPattern;
|
||||
const QLatin1String arg("<arg>");
|
||||
for (auto type : metaTypes) {
|
||||
if (type >= QMetaType::User
|
||||
&& !QMetaType::hasRegisteredConverterFunction(qMetaTypeId<QString>(), type)) {
|
||||
qCWarning(lcRouterRule) << QMetaType::typeName(type)
|
||||
<< "has not registered a converter to QString."
|
||||
<< "Use QHttpServerRouter::addConveter<Type>(converter).";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = converters.constFind(type);
|
||||
if (it == converters.end()) {
|
||||
qCWarning(lcRouterRule) << "Can not find converter for type:"
|
||||
<< QMetaType::typeName(type);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (it->isEmpty())
|
||||
continue;
|
||||
|
||||
const auto index = pathRegexp.indexOf(arg);
|
||||
const QString ®exp = QLatin1Char('(') % *it % QLatin1Char(')');
|
||||
if (index == -1)
|
||||
pathRegexp.append(regexp);
|
||||
else
|
||||
pathRegexp.replace(index, arg.size(), regexp);
|
||||
}
|
||||
|
||||
if (pathRegexp.indexOf(arg) != -1) {
|
||||
qCWarning(lcRouterRule) << "not enough types or one of the types is not supported, regexp:"
|
||||
<< pathRegexp
|
||||
<< ", pattern:" << d->pathPattern
|
||||
<< ", types:" << std::list<int>(metaTypes);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pathRegexp.startsWith(QLatin1Char('^')))
|
||||
pathRegexp = QLatin1Char('^') % pathRegexp;
|
||||
if (!pathRegexp.endsWith(QLatin1Char('$')))
|
||||
pathRegexp += QLatin1String("$");
|
||||
|
||||
qCDebug(lcRouterRule) << "url pathRegexp:" << pathRegexp;
|
||||
|
||||
d->pathRegexp.setPattern(pathRegexp);
|
||||
d->pathRegexp.optimize();
|
||||
return true;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
90
src/qthttpserver/src/httpserver/qhttpserverrouterrule.h
Normal file
90
src/qthttpserver/src/httpserver/qhttpserverrouterrule.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERROUTERRULE_H
|
||||
#define QHTTPSERVERROUTERRULE_H
|
||||
|
||||
#include <QtHttpServer/qhttpserverrequest.h>
|
||||
|
||||
#include <QtCore/qmap.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QString;
|
||||
class QHttpServerRequest;
|
||||
class QTcpSocket;
|
||||
class QRegularExpressionMatch;
|
||||
class QHttpServerRouter;
|
||||
|
||||
class QHttpServerRouterRulePrivate;
|
||||
class Q_HTTPSERVER_EXPORT QHttpServerRouterRule
|
||||
{
|
||||
Q_DECLARE_PRIVATE(QHttpServerRouterRule)
|
||||
|
||||
public:
|
||||
using RouterHandler = std::function<void(const QRegularExpressionMatch &,
|
||||
const QHttpServerRequest &,
|
||||
QTcpSocket *)>;
|
||||
|
||||
explicit QHttpServerRouterRule(const QString &pathPattern, RouterHandler &&routerHandler);
|
||||
explicit QHttpServerRouterRule(const QString &pathPattern,
|
||||
const QHttpServerRequest::Methods methods,
|
||||
RouterHandler &&routerHandler);
|
||||
explicit QHttpServerRouterRule(const QString &pathPattern,
|
||||
const char * methods,
|
||||
RouterHandler &&routerHandler);
|
||||
|
||||
QHttpServerRouterRule(QHttpServerRouterRule &&other) = delete;
|
||||
QHttpServerRouterRule &operator=(QHttpServerRouterRule &&other) = delete;
|
||||
|
||||
virtual ~QHttpServerRouterRule();
|
||||
|
||||
protected:
|
||||
bool exec(const QHttpServerRequest &request, QTcpSocket *socket) const;
|
||||
|
||||
bool hasValidMethods() const;
|
||||
|
||||
bool createPathRegexp(const std::initializer_list<int> &metaTypes,
|
||||
const QMap<int, QLatin1String> &converters);
|
||||
|
||||
virtual bool matches(const QHttpServerRequest &request,
|
||||
QRegularExpressionMatch *match) const;
|
||||
|
||||
QHttpServerRouterRule(QHttpServerRouterRulePrivate *d);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QHttpServerRouterRule)
|
||||
QScopedPointer<QHttpServerRouterRulePrivate> d_ptr;
|
||||
|
||||
friend class QHttpServerRouter;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVERROUTERRULE_H
|
||||
62
src/qthttpserver/src/httpserver/qhttpserverrouterrule_p.h
Normal file
62
src/qthttpserver/src/httpserver/qhttpserverrouterrule_p.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERROUTERRULE_P_H
|
||||
#define QHTTPSERVERROUTERRULE_P_H
|
||||
|
||||
#include <QtHttpServer/qhttpserverrouterrule.h>
|
||||
|
||||
#include <QtCore/qregularexpression.h>
|
||||
#include <QtCore/qstring.h>
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists for the convenience
|
||||
// of QHttpServer. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class Q_HTTPSERVER_EXPORT QHttpServerRouterRulePrivate
|
||||
{
|
||||
public:
|
||||
QString pathPattern;
|
||||
QHttpServerRequest::Methods methods;
|
||||
QHttpServerRouterRule::RouterHandler routerHandler;
|
||||
|
||||
QRegularExpression pathRegexp;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVERROUTERRULE_P_H
|
||||
164
src/qthttpserver/src/httpserver/qhttpserverrouterviewtraits.h
Normal file
164
src/qthttpserver/src/httpserver/qhttpserverrouterviewtraits.h
Normal file
@@ -0,0 +1,164 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Mikhail Svetkin <mikhail.svetkin@gmail.com>
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERROUTERVIEWTRAITS_H
|
||||
#define QHTTPSERVERROUTERVIEWTRAITS_H
|
||||
|
||||
#include <QtHttpServer/qhttpserverviewtraits_impl.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttpServerRequest;
|
||||
class QHttpServerResponder;
|
||||
|
||||
namespace QtPrivate {
|
||||
|
||||
template<typename ViewHandler, bool DisableStaticAssert>
|
||||
struct RouterViewTraitsHelper : ViewTraits<ViewHandler, DisableStaticAssert> {
|
||||
using VTraits = ViewTraits<ViewHandler, DisableStaticAssert>;
|
||||
using FunctionTraits = typename VTraits::FTraits;
|
||||
|
||||
template<int I>
|
||||
struct ArgumentChecker : FunctionTraits::template Arg<I> {
|
||||
using IsRequest = typename VTraits::template Special<I, const QHttpServerRequest &>;
|
||||
static_assert(IsRequest::AssertCondition,
|
||||
"ViewHandler arguments error: "
|
||||
"QHttpServerRequest can only be passed as a const reference");
|
||||
|
||||
using IsResponder = typename VTraits::template Special<I, QHttpServerResponder &&>;
|
||||
static_assert(IsResponder::AssertCondition,
|
||||
"ViewHandler arguments error: "
|
||||
"QHttpServerResponder can only be passed as a universal reference");
|
||||
|
||||
using IsSpecial = CheckAny<IsRequest, IsResponder>;
|
||||
|
||||
struct IsSimple {
|
||||
static constexpr bool Value = !IsSpecial::Value &&
|
||||
I < FunctionTraits::ArgumentCount &&
|
||||
FunctionTraits::ArgumentIndexMax != -1;
|
||||
static constexpr bool Valid = FunctionTraits::template Arg<I>::Defined;
|
||||
|
||||
static constexpr bool StaticAssert =
|
||||
DisableStaticAssert || !Value || Valid;
|
||||
|
||||
|
||||
static_assert(StaticAssert,
|
||||
"ViewHandler arguments error: "
|
||||
"Type is not registered, please use the Q_DECLARE_METATYPE macro "
|
||||
"to make it known to Qt's meta-object system");
|
||||
};
|
||||
|
||||
using CheckOk = CheckAny<IsSimple, IsSpecial>;
|
||||
|
||||
static constexpr bool Valid = CheckOk::Valid;
|
||||
static constexpr bool StaticAssert = CheckOk::StaticAssert;
|
||||
};
|
||||
|
||||
|
||||
struct Arguments {
|
||||
template<int ... I>
|
||||
struct ArgumentsReturn {
|
||||
template<int Idx>
|
||||
using Arg = ArgumentChecker<Idx>;
|
||||
|
||||
template<int Idx>
|
||||
static constexpr int metaTypeId() noexcept
|
||||
{
|
||||
using Type = typename FunctionTraits::template Arg<Idx>::CleanType;
|
||||
|
||||
return qMetaTypeId<
|
||||
typename std::conditional<
|
||||
QMetaTypeId2<Type>::Defined,
|
||||
Type,
|
||||
void>::type>();
|
||||
}
|
||||
|
||||
static constexpr std::size_t Count = FunctionTraits::ArgumentCount;
|
||||
static constexpr std::size_t CapturableCount =
|
||||
StaticMath::Sum::eval(
|
||||
static_cast<std::size_t>(FunctionTraits::template Arg<I>::Defined)...);
|
||||
static constexpr std::size_t PlaceholdersCount = Count - CapturableCount;
|
||||
|
||||
static constexpr bool Valid = StaticMath::And::eval(Arg<I>::Valid...);
|
||||
static constexpr bool StaticAssert =
|
||||
StaticMath::And::eval(Arg<I>::StaticAssert...);
|
||||
|
||||
using Indexes = typename QtPrivate::IndexesList<I...>;
|
||||
|
||||
using CapturableIndexes =
|
||||
typename QtPrivate::Indexes<CapturableCount>::Value;
|
||||
|
||||
using PlaceholdersIndexes =
|
||||
typename QtPrivate::Indexes<PlaceholdersCount>::Value;
|
||||
|
||||
using Last = Arg<FunctionTraits::ArgumentIndexMax>;
|
||||
};
|
||||
|
||||
template<int ... I>
|
||||
static constexpr ArgumentsReturn<I...> eval(QtPrivate::IndexesList<I...>) noexcept
|
||||
{
|
||||
return ArgumentsReturn<I...>{};
|
||||
}
|
||||
};
|
||||
|
||||
template<int CaptureOffset>
|
||||
struct BindType {
|
||||
template<typename ... Args>
|
||||
struct FunctionWrapper {
|
||||
using Type = std::function<typename FunctionTraits::ReturnType (Args...)>;
|
||||
};
|
||||
|
||||
template<int Id>
|
||||
using OffsetArg = typename FunctionTraits::template Arg<CaptureOffset + Id>::Type;
|
||||
|
||||
template<int ... Idx>
|
||||
static constexpr typename FunctionWrapper<OffsetArg<Idx>...>::Type
|
||||
eval(QtPrivate::IndexesList<Idx...>) noexcept;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
} // namespace QtPrivate
|
||||
|
||||
template <typename ViewHandler, bool DisableStaticAssert = false>
|
||||
struct QHttpServerRouterViewTraits
|
||||
{
|
||||
using Helpers = typename QtPrivate::RouterViewTraitsHelper<ViewHandler, DisableStaticAssert>;
|
||||
using ReturnType = typename Helpers::FunctionTraits::ReturnType;
|
||||
using Arguments = decltype(Helpers::Arguments::eval(typename Helpers::ArgumentIndexes{}));
|
||||
using BindableType = decltype(
|
||||
Helpers::template BindType<Arguments::CapturableCount>::eval(
|
||||
typename Arguments::PlaceholdersIndexes{}));
|
||||
};
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVERROUTERVIEWTRAITS_H
|
||||
109
src/qthttpserver/src/httpserver/qhttpserverviewtraits.h
Normal file
109
src/qthttpserver/src/httpserver/qhttpserverviewtraits.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Mikhail Svetkin <mikhail.svetkin@gmail.com>
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERVIEWTRAITS_H
|
||||
#define QHTTPSERVERVIEWTRAITS_H
|
||||
|
||||
#include <QtHttpServer/qhttpserverviewtraits_impl.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttpServerRequest;
|
||||
class QHttpServerResponse;
|
||||
|
||||
namespace QtPrivate {
|
||||
|
||||
template <typename ViewHandler, bool DisableStaticAssert>
|
||||
struct AfterRequestViewTraitsHelper : ViewTraits<ViewHandler, DisableStaticAssert> {
|
||||
using VTraits = ViewTraits<ViewHandler, DisableStaticAssert>;
|
||||
using FunctionTraits = typename VTraits::FTraits;
|
||||
|
||||
static_assert(DisableStaticAssert ||
|
||||
FunctionTraits::ArgumentCount == 2 ||
|
||||
FunctionTraits::ArgumentCount == 1,
|
||||
"ViewHandler arguments error: "
|
||||
"afterRequest can only accept QHttpServerResponse and QHttpServerRequest");
|
||||
|
||||
static_assert(DisableStaticAssert ||
|
||||
std::is_same<typename FunctionTraits::ReturnType,
|
||||
QHttpServerResponse>::value,
|
||||
"ViewHandler return type error: "
|
||||
"Return type can only be QHttpServerResponse");
|
||||
|
||||
template<int I>
|
||||
struct ArgumentChecker {
|
||||
using IsRequest = typename VTraits::template Special<I, const QHttpServerRequest &>;
|
||||
static_assert(IsRequest::AssertCondition,
|
||||
"ViewHandler arguments error: "
|
||||
"QHttpServerRequest can only be passed as a const reference");
|
||||
|
||||
using IsResponse = typename VTraits::template Special<I, QHttpServerResponse &&>;
|
||||
static_assert(IsResponse::AssertCondition,
|
||||
"ViewHandler arguments error: "
|
||||
"QHttpServerResponse can only be passed as a universal reference");
|
||||
|
||||
using IsSpecial = CheckAny<IsRequest, IsResponse>;
|
||||
|
||||
static constexpr bool Valid = IsSpecial::Valid;
|
||||
static constexpr bool StaticAssert = IsSpecial::StaticAssert;
|
||||
};
|
||||
|
||||
struct Arguments {
|
||||
template<int ... I>
|
||||
struct ArgumentsReturn {
|
||||
template<int Idx>
|
||||
using Arg = ArgumentChecker<Idx>;
|
||||
static constexpr bool Valid = QtPrivate::StaticMath::And::eval(Arg<I>::Valid...);
|
||||
static constexpr bool StaticAssert = QtPrivate::StaticMath::And::eval(
|
||||
Arg<I>::StaticAssert...);
|
||||
using Last = Arg<FunctionTraits::ArgumentIndexMax>;
|
||||
static constexpr std::size_t Count = FunctionTraits::ArgumentCount;
|
||||
};
|
||||
|
||||
template<int ... I>
|
||||
static constexpr ArgumentsReturn<I...> eval(QtPrivate::IndexesList<I...>) noexcept
|
||||
{
|
||||
return ArgumentsReturn<I...>{};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace QtPrivate
|
||||
|
||||
template <typename ViewHandler, bool DisableStaticAssert = false>
|
||||
struct QHttpServerAfterRequestViewTraits
|
||||
{
|
||||
using Helpers = typename QtPrivate::AfterRequestViewTraitsHelper<ViewHandler, DisableStaticAssert>;
|
||||
using Arguments = decltype(Helpers::Arguments::eval(typename Helpers::ArgumentIndexes{}));
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVERVIEWTRAITS_H
|
||||
194
src/qthttpserver/src/httpserver/qhttpserverviewtraits_impl.h
Normal file
194
src/qthttpserver/src/httpserver/qhttpserverviewtraits_impl.h
Normal file
@@ -0,0 +1,194 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Mikhail Svetkin <mikhail.svetkin@gmail.com>
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERVIEWTRAITS_IMPL_H
|
||||
#define QHTTPSERVERVIEWTRAITS_IMPL_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qmetatype.h>
|
||||
#include <QtCore/qnamespace.h>
|
||||
#include <QtCore/qobjectdefs.h>
|
||||
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QtPrivate {
|
||||
|
||||
template<typename T>
|
||||
struct RemoveCVRef
|
||||
{
|
||||
using Type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
};
|
||||
|
||||
|
||||
template<bool classMember, typename ReturnT, typename ... Args>
|
||||
struct FunctionTraitsHelper
|
||||
{
|
||||
static constexpr const int ArgumentCount = sizeof ... (Args);
|
||||
static constexpr const int ArgumentIndexMax = ArgumentCount - 1;
|
||||
static constexpr const bool IsClassMember = classMember;
|
||||
using ReturnType = ReturnT;
|
||||
|
||||
template <int I>
|
||||
struct Arg {
|
||||
using Type = typename std::tuple_element<I, std::tuple<Args...>>::type;
|
||||
|
||||
using CleanType = typename QtPrivate::RemoveCVRef<Type>::Type;
|
||||
|
||||
static constexpr bool Defined = QMetaTypeId2<CleanType>::Defined;
|
||||
};
|
||||
};
|
||||
|
||||
template<bool classMember, typename ReturnT>
|
||||
struct FunctionTraitsHelper<classMember, ReturnT>
|
||||
{
|
||||
static constexpr const int ArgumentCount = 0;
|
||||
static constexpr const int ArgumentIndexMax = -1;
|
||||
static constexpr const bool IsClassMember = classMember;
|
||||
using ReturnType = ReturnT;
|
||||
|
||||
template <int I>
|
||||
struct Arg {
|
||||
using Type = std::false_type;
|
||||
using CleanType = Type;
|
||||
static constexpr bool Defined = QMetaTypeId2<CleanType>::Defined;
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct FunctionTraits;
|
||||
|
||||
template<typename T>
|
||||
struct FunctionTraits : public FunctionTraits<decltype(&T::operator())>{};
|
||||
|
||||
template<typename ReturnT, typename ... Args>
|
||||
struct FunctionTraits<ReturnT (*)(Args...)>
|
||||
: public FunctionTraitsHelper<false, ReturnT, Args...>
|
||||
{
|
||||
};
|
||||
|
||||
template<class ReturnT, class ClassT, class ...Args>
|
||||
struct FunctionTraits<ReturnT (ClassT::*)(Args...) const>
|
||||
: public FunctionTraitsHelper<true, ReturnT, Args...>
|
||||
{
|
||||
using classType = ClassT;
|
||||
};
|
||||
|
||||
struct StaticMath {
|
||||
template <template<typename> class Predicate, bool defaultValue>
|
||||
struct Loop {
|
||||
static constexpr bool eval() noexcept {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
template<typename T, typename ... N>
|
||||
static constexpr T eval(const T it, N ...n) noexcept {
|
||||
return Predicate<T>::eval(it, eval(n...));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct SumPredicate {
|
||||
static constexpr T eval(const T rs, const T ls) noexcept
|
||||
{
|
||||
return rs + ls;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct AndPredicate {
|
||||
static constexpr T eval(const T rs, const T ls) noexcept
|
||||
{
|
||||
return rs && ls;
|
||||
}
|
||||
};
|
||||
|
||||
using Sum = Loop<SumPredicate, false>;
|
||||
using And = Loop<AndPredicate, true>;
|
||||
using Or = Sum;
|
||||
};
|
||||
|
||||
template<typename ... T>
|
||||
struct CheckAny {
|
||||
static constexpr bool Value = StaticMath::Or::eval(T::Value...);
|
||||
static constexpr bool Valid = StaticMath::Or::eval(T::Valid...);
|
||||
static constexpr bool StaticAssert = StaticMath::Or::eval(T::StaticAssert...);
|
||||
};
|
||||
|
||||
template<typename ViewHandler, bool DisableStaticAssert>
|
||||
struct ViewTraits {
|
||||
using FTraits = FunctionTraits<ViewHandler>;
|
||||
using ArgumentIndexes = typename Indexes<FTraits::ArgumentCount>::Value;
|
||||
|
||||
template<int I, typename Special>
|
||||
struct SpecialHelper {
|
||||
using Arg = typename FTraits::template Arg<I>;
|
||||
using CleanSpecialT = typename RemoveCVRef<Special>::Type;
|
||||
|
||||
static constexpr bool TypeMatched = std::is_same<typename Arg::CleanType, CleanSpecialT>::value;
|
||||
static constexpr bool TypeCVRefMatched = std::is_same<typename Arg::Type, Special>::value;
|
||||
|
||||
static constexpr bool ValidPosition =
|
||||
(I == FTraits::ArgumentIndexMax ||
|
||||
I == FTraits::ArgumentIndexMax - 1);
|
||||
static constexpr bool ValidAll = TypeCVRefMatched && ValidPosition;
|
||||
|
||||
static constexpr bool AssertCondition =
|
||||
DisableStaticAssert || !TypeMatched || TypeCVRefMatched;
|
||||
|
||||
static constexpr bool AssertConditionOrder =
|
||||
DisableStaticAssert || !TypeMatched || ValidPosition;
|
||||
|
||||
static constexpr bool StaticAssert = AssertCondition && AssertConditionOrder;
|
||||
|
||||
static_assert(AssertConditionOrder,
|
||||
"ViewHandler arguments error: "
|
||||
"QHttpServerRequest or QHttpServerResponder"
|
||||
" can only be the last argument");
|
||||
};
|
||||
|
||||
template<int I, typename T>
|
||||
struct Special {
|
||||
using Helper = SpecialHelper<I, T>;
|
||||
static constexpr bool Value = Helper::TypeMatched;
|
||||
static constexpr bool Valid = Helper::ValidAll;
|
||||
static constexpr bool StaticAssert = Helper::StaticAssert;
|
||||
static constexpr bool AssertCondition = Helper::AssertCondition;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace QtPrivate
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVERVIEWTRAITS_IMPL_H
|
||||
50
src/qthttpserver/src/httpserver/qthttpserverglobal.h
Normal file
50
src/qthttpserver/src/httpserver/qthttpserverglobal.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTHTTPSERVERGLOBAL_H
|
||||
#define QTHTTPSERVERGLOBAL_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#ifndef QT_STATIC
|
||||
# if defined(QT_BUILD_HTTPSERVER_LIB)
|
||||
# define Q_HTTPSERVER_EXPORT Q_DECL_EXPORT
|
||||
# else
|
||||
# define Q_HTTPSERVER_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
#else
|
||||
# define Q_HTTPSERVER_EXPORT
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QTHTTPSERVERGLOBAL_H
|
||||
|
||||
37
src/qthttpserver/src/src.pro
Normal file
37
src/qthttpserver/src/src.pro
Normal file
@@ -0,0 +1,37 @@
|
||||
include(../qhttpserver.pri)
|
||||
|
||||
QHTTPSERVER_BASE = ..
|
||||
TEMPLATE = lib
|
||||
|
||||
TARGET = qhttpserver
|
||||
|
||||
!win32:VERSION = 0.1.0
|
||||
|
||||
QT += network
|
||||
QT -= gui
|
||||
|
||||
CONFIG += dll debug_and_release
|
||||
|
||||
CONFIG(debug, debug|release) {
|
||||
win32: TARGET = $$join(TARGET,,,d)
|
||||
}
|
||||
|
||||
DEFINES += QHTTPSERVER_EXPORT
|
||||
|
||||
INCLUDEPATH += $$QHTTPSERVER_BASE/http-parser
|
||||
|
||||
PRIVATE_HEADERS += $$QHTTPSERVER_BASE/http-parser/http_parser.h qhttpconnection.h
|
||||
|
||||
PUBLIC_HEADERS += qhttpserver.h qhttprequest.h qhttpresponse.h qhttpserverapi.h qhttpserverfwd.h
|
||||
|
||||
HEADERS = $$PRIVATE_HEADERS $$PUBLIC_HEADERS
|
||||
SOURCES = *.cpp $$QHTTPSERVER_BASE/http-parser/http_parser.c
|
||||
|
||||
OBJECTS_DIR = $$QHTTPSERVER_BASE/build
|
||||
MOC_DIR = $$QHTTPSERVER_BASE/build
|
||||
DESTDIR = $$QHTTPSERVER_BASE/lib
|
||||
|
||||
target.path = $$LIBDIR
|
||||
headers.path = $$INCLUDEDIR
|
||||
headers.files = $$PUBLIC_HEADERS
|
||||
INSTALLS += target headers
|
||||
16
src/qthttpserver/src/sslserver/CMakeLists.txt
Normal file
16
src/qthttpserver/src/sslserver/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
# Generated from sslserver.pro.
|
||||
|
||||
#####################################################################
|
||||
## SslServer Module:
|
||||
#####################################################################
|
||||
|
||||
qt_add_module(SslServer
|
||||
SOURCES
|
||||
qsslserver.cpp qsslserver.h qsslserver_p.h
|
||||
qtsslserverglobal.h
|
||||
INCLUDE_DIRECTORIES
|
||||
.
|
||||
PUBLIC_LIBRARIES
|
||||
Qt::Core
|
||||
Qt::Network
|
||||
)
|
||||
72
src/qthttpserver/src/sslserver/qsslserver.cpp
Normal file
72
src/qthttpserver/src/sslserver/qsslserver.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Sylvain Garcia <garcia.6l20@gmail.com>.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <private/qsslserver_p.h>
|
||||
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_LOGGING_CATEGORY(lcSS, "qt.sslserver");
|
||||
|
||||
QSslServer::QSslServer(QObject *parent):
|
||||
QTcpServer (parent), d(new QSslServerPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
QSslServer::QSslServer(const QSslConfiguration &sslConfiguration,
|
||||
QObject *parent):
|
||||
QTcpServer (parent), d(new QSslServerPrivate)
|
||||
{
|
||||
d->sslConfiguration = sslConfiguration;
|
||||
}
|
||||
|
||||
QSslServer::~QSslServer() = default;
|
||||
|
||||
void QSslServer::incomingConnection(qintptr handle)
|
||||
{
|
||||
QSslSocket *socket = new QSslSocket(this);
|
||||
connect(socket, QOverload<const QList<QSslError>&>::of(&QSslSocket::sslErrors),
|
||||
[this, socket](const QList<QSslError> &errors) {
|
||||
for (auto &err: errors)
|
||||
qCCritical(lcSS) << err;
|
||||
Q_EMIT sslErrors(socket, errors);
|
||||
});
|
||||
socket->setSocketDescriptor(handle);
|
||||
socket->setSslConfiguration(d->sslConfiguration);
|
||||
socket->startServerEncryption();
|
||||
|
||||
addPendingConnection(socket);
|
||||
}
|
||||
|
||||
void QSslServer::setSslConfiguration(const QSslConfiguration &sslConfiguration)
|
||||
{
|
||||
d->sslConfiguration = sslConfiguration;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
65
src/qthttpserver/src/sslserver/qsslserver.h
Normal file
65
src/qthttpserver/src/sslserver/qsslserver.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Sylvain Garcia <garcia.6l20@gmail.com>.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QSSLSERVER_H
|
||||
#define QSSLSERVER_H
|
||||
|
||||
#include <QtSslServer/qtsslserverglobal.h>
|
||||
|
||||
#include <QtNetwork/qtcpserver.h>
|
||||
#include <QtNetwork/qsslconfiguration.h>
|
||||
|
||||
#include <QtCore/qscopedpointer.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QSslServerPrivate;
|
||||
class Q_SSLSERVER_EXPORT QSslServer : public QTcpServer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QSslServer(QObject *parent = nullptr);
|
||||
QSslServer(const QSslConfiguration &sslConfiguration, QObject *parent = nullptr);
|
||||
~QSslServer();
|
||||
|
||||
void setSslConfiguration(const QSslConfiguration &sslConfiguration);
|
||||
|
||||
Q_SIGNALS:
|
||||
void sslErrors(QSslSocket *socket, const QList<QSslError> &errors);
|
||||
|
||||
protected:
|
||||
void incomingConnection(qintptr handle) override final;
|
||||
|
||||
private:
|
||||
QScopedPointer<QSslServerPrivate> d;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QSSLSERVER_HPP
|
||||
45
src/qthttpserver/src/sslserver/qsslserver_p.h
Normal file
45
src/qthttpserver/src/sslserver/qsslserver_p.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Sylvain Garcia <garcia.6l20@gmail.com>.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QSSLSERVER_P_H
|
||||
#define QSSLSERVER_P_H
|
||||
|
||||
#include <QtSslServer/qsslserver.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QSslServerPrivate
|
||||
{
|
||||
public:
|
||||
QSslConfiguration sslConfiguration;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QSSLSERVER_P_H
|
||||
49
src/qthttpserver/src/sslserver/qtsslserverglobal.h
Normal file
49
src/qthttpserver/src/sslserver/qtsslserverglobal.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Sylvain Garcia <garcia.6l20@gmail.com>.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTSSLSERVERGLOBAL_H
|
||||
#define QTSSLSERVERGLOBAL_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#ifndef QT_STATIC
|
||||
# if defined(QT_BUILD_SSLSERVER_LIB)
|
||||
# define Q_SSLSERVER_EXPORT Q_DECL_EXPORT
|
||||
# else
|
||||
# define Q_SSLSERVER_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
#else
|
||||
# define Q_SSLSERVER_EXPORT
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QTSSLSERVERGLOBAL_H
|
||||
14
src/qthttpserver/src/sslserver/sslserver.pro
Normal file
14
src/qthttpserver/src/sslserver/sslserver.pro
Normal file
@@ -0,0 +1,14 @@
|
||||
TARGET = QtSslServer
|
||||
INCLUDEPATH += .
|
||||
|
||||
QT = network core
|
||||
|
||||
HEADERS += \
|
||||
qsslserver.h \
|
||||
qtsslserverglobal.h \
|
||||
qsslserver_p.h
|
||||
|
||||
SOURCES += \
|
||||
qsslserver.cpp
|
||||
|
||||
load(qt_module)
|
||||
4
src/qthttpserver/sync.profile
Normal file
4
src/qthttpserver/sync.profile
Normal file
@@ -0,0 +1,4 @@
|
||||
%modules = ( # path to module name map
|
||||
"QtHttpServer" => "$basedir/src/httpserver",
|
||||
"QtSslServer" => "$basedir/src/sslserver",
|
||||
);
|
||||
7
src/qthttpserver/tests/CMakeLists.txt
Normal file
7
src/qthttpserver/tests/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
# Generated from tests.pro.
|
||||
|
||||
if(QT_BUILD_STANDALONE_TESTS)
|
||||
# Add qt_find_package calls for extra dependencies that need to be found when building
|
||||
# the standalone tests here.
|
||||
endif()
|
||||
qt_build_tests()
|
||||
7
src/qthttpserver/tests/auto/CMakeLists.txt
Normal file
7
src/qthttpserver/tests/auto/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
# Generated from auto.pro.
|
||||
|
||||
add_subdirectory(qabstracthttpserver)
|
||||
add_subdirectory(qhttpserver)
|
||||
add_subdirectory(qhttpserverresponder)
|
||||
add_subdirectory(qhttpserverrouter)
|
||||
add_subdirectory(qhttpserverresponse)
|
||||
11
src/qthttpserver/tests/auto/auto.pro
Normal file
11
src/qthttpserver/tests/auto/auto.pro
Normal file
@@ -0,0 +1,11 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
QT = network
|
||||
|
||||
SUBDIRS = \
|
||||
cmake \
|
||||
qabstracthttpserver \
|
||||
qhttpserver \
|
||||
qhttpserverresponder \
|
||||
qhttpserverrouter \
|
||||
qhttpserverresponse
|
||||
12
src/qthttpserver/tests/auto/cmake/CMakeLists.txt
Normal file
12
src/qthttpserver/tests/auto/cmake/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
project(qmake_cmake_files)
|
||||
|
||||
enable_testing()
|
||||
|
||||
find_package(Qt5Core REQUIRED)
|
||||
|
||||
include("${_Qt5CTestMacros}")
|
||||
|
||||
expect_pass(abstracthttpserver)
|
||||
@@ -0,0 +1,7 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
project(qabstracthttpserver_test)
|
||||
|
||||
find_package(Qt5 COMPONENTS HttpServer REQUIRED)
|
||||
|
||||
add_executable(qabstracthttpserver_test "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp")
|
||||
target_link_libraries(qabstracthttpserver_test Qt5::HttpServer)
|
||||
@@ -0,0 +1,41 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtHttpServer/qabstracthttpserver.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
struct HttpServer : QAbstractHttpServer {
|
||||
bool handleRequest(const QHttpServerRequest &, QTcpSocket *) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} httpServer;
|
||||
Q_UNUSED(httpServer);
|
||||
return 0;
|
||||
}
|
||||
4
src/qthttpserver/tests/auto/cmake/cmake.pro
Normal file
4
src/qthttpserver/tests/auto/cmake/cmake.pro
Normal file
@@ -0,0 +1,4 @@
|
||||
# Cause make to do nothing.
|
||||
TEMPLATE = subdirs
|
||||
CMAKE_QT_MODULES_UNDER_TEST = httpserver
|
||||
CONFIG += ctest_testcase
|
||||
@@ -0,0 +1,12 @@
|
||||
# Generated from qabstracthttpserver.pro.
|
||||
|
||||
#####################################################################
|
||||
## tst_qabstracthttpserver Test:
|
||||
#####################################################################
|
||||
|
||||
qt_add_test(tst_qabstracthttpserver
|
||||
SOURCES
|
||||
tst_qabstracthttpserver.cpp
|
||||
PUBLIC_LIBRARIES
|
||||
Qt::HttpServer
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
CONFIG += testcase
|
||||
TARGET = tst_qabstracthttpserver
|
||||
SOURCES += tst_qabstracthttpserver.cpp
|
||||
|
||||
QT = httpserver testlib
|
||||
@@ -0,0 +1,280 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtHttpServer/qabstracthttpserver.h>
|
||||
|
||||
#if defined(QT_WEBSOCKETS_LIB)
|
||||
# include <QtWebSockets/qwebsocket.h>
|
||||
#endif
|
||||
|
||||
#include <QtTest/qsignalspy.h>
|
||||
#include <QtTest/qtest.h>
|
||||
|
||||
#include <QtCore/qregularexpression.h>
|
||||
#include <QtCore/qurl.h>
|
||||
#include <QtNetwork/qnetworkaccessmanager.h>
|
||||
#include <QtNetwork/qnetworkreply.h>
|
||||
#include <QtNetwork/qnetworkrequest.h>
|
||||
#include <QtNetwork/qtcpserver.h>
|
||||
#include <QtHttpServer/qhttpserverrequest.h>
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
# include <signal.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class tst_QAbstractHttpServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void request_data();
|
||||
void request();
|
||||
void checkListenWarns();
|
||||
void websocket();
|
||||
void servers();
|
||||
void fork();
|
||||
void qtbug82053();
|
||||
};
|
||||
|
||||
void tst_QAbstractHttpServer::request_data()
|
||||
{
|
||||
QTest::addColumn<QString>("host");
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<QString>("query");
|
||||
|
||||
QTest::addRow("127.0.0.1") << "127.0.0.1" << "/" << QString();
|
||||
QTest::addRow("0.0.0.0") << "0.0.0.0" << "/" << QString();
|
||||
QTest::addRow("localhost") << "localhost" << "/" << QString();
|
||||
QTest::addRow("localhost with query") << "localhost" << "/" << QString("key=value");
|
||||
QTest::addRow("0.0.0.0 path with spaces") << "0.0.0.0" << "/test test" << QString();
|
||||
QTest::addRow("0.0.0.0 path with spec spaces") << "0.0.0.0" << "/test%20test" << QString();
|
||||
QTest::addRow("127.0.0.1 path with spaces") << "127.0.0.1" << "/test test" << QString();
|
||||
QTest::addRow("127.0.0.1 path with spec spaces") << "127.0.0.1" << "/test%20test" << QString();
|
||||
}
|
||||
|
||||
void tst_QAbstractHttpServer::request()
|
||||
{
|
||||
QFETCH(QString, host);
|
||||
QFETCH(QString, path);
|
||||
QFETCH(QString, query);
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
if (host == QLatin1String("0.0.0.0"))
|
||||
QSKIP("Windows has problems with 0.0.0.0");
|
||||
#endif
|
||||
|
||||
struct HttpServer : QAbstractHttpServer
|
||||
{
|
||||
QUrl url;
|
||||
QByteArray body;
|
||||
QHttpServerRequest::Method method = QHttpServerRequest::Method::Unknown;
|
||||
quint8 padding[4];
|
||||
|
||||
bool handleRequest(const QHttpServerRequest &request, QTcpSocket *) override
|
||||
{
|
||||
method = request.method();
|
||||
url = request.url();
|
||||
body = request.body();
|
||||
return true;
|
||||
}
|
||||
} server;
|
||||
auto tcpServer = new QTcpServer;
|
||||
QVERIFY(tcpServer->listen());
|
||||
server.bind(tcpServer);
|
||||
QNetworkAccessManager networkAccessManager;
|
||||
QUrl url(QStringLiteral("http://%1:%2%3")
|
||||
.arg(host)
|
||||
.arg(tcpServer->serverPort())
|
||||
.arg(path));
|
||||
if (!query.isEmpty())
|
||||
url.setQuery(query);
|
||||
const QNetworkRequest request(url);
|
||||
networkAccessManager.get(request);
|
||||
QTRY_COMPARE(server.method, QHttpServerRequest::Method::Get);
|
||||
QCOMPARE(server.url, url);
|
||||
QCOMPARE(server.body, QByteArray());
|
||||
}
|
||||
|
||||
void tst_QAbstractHttpServer::checkListenWarns()
|
||||
{
|
||||
struct HttpServer : QAbstractHttpServer
|
||||
{
|
||||
bool handleRequest(const QHttpServerRequest &, QTcpSocket *) override { return true; }
|
||||
} server;
|
||||
auto tcpServer = new QTcpServer;
|
||||
QTest::ignoreMessage(QtWarningMsg,
|
||||
QRegularExpression(QStringLiteral("The TCP server .* is not listening.")));
|
||||
server.bind(tcpServer);
|
||||
}
|
||||
|
||||
void tst_QAbstractHttpServer::websocket()
|
||||
{
|
||||
#if !defined(QT_WEBSOCKETS_LIB)
|
||||
QSKIP("This test requires WebSocket support");
|
||||
#else
|
||||
struct HttpServer : QAbstractHttpServer
|
||||
{
|
||||
bool handleRequest(const QHttpServerRequest &, QTcpSocket *) override { return false; }
|
||||
} server;
|
||||
auto tcpServer = new QTcpServer;
|
||||
tcpServer->listen();
|
||||
server.bind(tcpServer);
|
||||
auto makeWebSocket = [this, tcpServer] () mutable {
|
||||
auto s = new QWebSocket(QString::fromUtf8(""),
|
||||
QWebSocketProtocol::VersionLatest,
|
||||
this);
|
||||
const QUrl url(QString::fromLatin1("ws://localhost:%1").arg(tcpServer->serverPort()));
|
||||
s->open(url);
|
||||
return s;
|
||||
};
|
||||
|
||||
// We have to send two requests to make sure that swapping between
|
||||
// QTcpSocket and QWebSockets works correctly
|
||||
auto s1 = makeWebSocket();
|
||||
auto s2 = makeWebSocket();
|
||||
|
||||
QSignalSpy newConnectionSpy(&server, &HttpServer::newWebSocketConnection);
|
||||
QTRY_COMPARE(newConnectionSpy.count(), 2);
|
||||
delete server.nextPendingWebSocketConnection();
|
||||
delete server.nextPendingWebSocketConnection();
|
||||
delete s1;
|
||||
delete s2;
|
||||
#endif // defined(QT_WEBSOCKETS_LIB)
|
||||
}
|
||||
|
||||
void tst_QAbstractHttpServer::servers()
|
||||
{
|
||||
struct HttpServer : QAbstractHttpServer
|
||||
{
|
||||
bool handleRequest(const QHttpServerRequest &, QTcpSocket *) override { return true; }
|
||||
} server;
|
||||
auto tcpServer = new QTcpServer;
|
||||
tcpServer->listen();
|
||||
server.bind(tcpServer);
|
||||
auto tcpServer2 = new QTcpServer;
|
||||
tcpServer2->listen();
|
||||
server.bind(tcpServer2);
|
||||
QTRY_COMPARE(server.servers().count(), 2);
|
||||
QTRY_COMPARE(server.serverPorts().count(), 2);
|
||||
QTRY_COMPARE(server.servers().first(), tcpServer);
|
||||
QTRY_COMPARE(server.serverPorts().first(), tcpServer->serverPort());
|
||||
QTRY_COMPARE(server.servers().last(), tcpServer2);
|
||||
QTRY_COMPARE(server.serverPorts().last(), tcpServer2->serverPort());
|
||||
}
|
||||
|
||||
void tst_QAbstractHttpServer::fork()
|
||||
{
|
||||
#if defined(Q_OS_UNIX)
|
||||
const auto message = QByteArrayLiteral("Hello world!");
|
||||
struct HttpServer : QAbstractHttpServer
|
||||
{
|
||||
const QByteArray &message;
|
||||
HttpServer(const QByteArray &message) : message(message) {}
|
||||
bool handleRequest(const QHttpServerRequest &, QTcpSocket *socket) override
|
||||
{
|
||||
socket->write(QByteArrayLiteral("HTTP/1.1 200 OK"));
|
||||
socket->write(QByteArrayLiteral("\r\n"));
|
||||
socket->write(QByteArrayLiteral("Content-Length: "));
|
||||
socket->write(QByteArray::number(message.size()));
|
||||
socket->write(QByteArrayLiteral("\r\n"));
|
||||
socket->write(QByteArrayLiteral("Connection: close"));
|
||||
socket->write(QByteArrayLiteral("\r\n"));
|
||||
socket->write(QByteArrayLiteral("Content-Type: text/html"));
|
||||
socket->write(QByteArrayLiteral("\r\n\r\n"));
|
||||
socket->write(message);
|
||||
socket->flush();
|
||||
::kill(::getpid(), SIGKILL); // Avoids continuing running tests in the child process
|
||||
return true;
|
||||
}
|
||||
} server = { message };
|
||||
|
||||
struct TcpServer : QTcpServer
|
||||
{
|
||||
void incomingConnection(qintptr socketDescriptor) override
|
||||
{
|
||||
if (::fork() != 0) {
|
||||
// Parent process: Create a QTcpSocket with the descriptor to close it properly
|
||||
QTcpSocket socket;
|
||||
socket.setSocketDescriptor(socketDescriptor);
|
||||
socket.close();
|
||||
} else {
|
||||
// Child process: It will parse the request and call HttpServer::handleRequest
|
||||
QTcpServer::incomingConnection(socketDescriptor);
|
||||
}
|
||||
}
|
||||
};
|
||||
auto tcpServer = new TcpServer;
|
||||
tcpServer->listen();
|
||||
server.bind(tcpServer);
|
||||
QNetworkAccessManager networkAccessManager;
|
||||
const QUrl url(QString::fromLatin1("http://localhost:%1").arg(tcpServer->serverPort()));
|
||||
auto reply = networkAccessManager.get(QNetworkRequest(url));
|
||||
QSignalSpy finishedSpy(reply, &QNetworkReply::finished);
|
||||
QTRY_VERIFY(finishedSpy.count());
|
||||
QCOMPARE(reply->readAll(), message);
|
||||
reply->close();
|
||||
reply->deleteLater();
|
||||
#else
|
||||
QSKIP("fork() not supported by this platform");
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QAbstractHttpServer::qtbug82053()
|
||||
{
|
||||
struct HttpServer : QAbstractHttpServer
|
||||
{
|
||||
bool wasConnectRequest{false};
|
||||
bool handleRequest(const QHttpServerRequest &req, QTcpSocket *) override
|
||||
{
|
||||
wasConnectRequest = (req.method() == QHttpServerRequest::Method::Connect);
|
||||
return false;
|
||||
}
|
||||
} server;
|
||||
auto tcpServer = new QTcpServer;
|
||||
tcpServer->listen();
|
||||
server.bind(tcpServer);
|
||||
|
||||
QTcpSocket client;
|
||||
client.connectToHost(QHostAddress::LocalHost, tcpServer->serverPort());
|
||||
client.waitForConnected();
|
||||
client.write("CONNECT / HTTP/1.1\n\n");
|
||||
client.waitForBytesWritten();
|
||||
QTest::qWait(0);
|
||||
QCOMPARE(client.state(), QAbstractSocket::ConnectedState);
|
||||
QTRY_VERIFY(server.wasConnectRequest);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QTEST_MAIN(tst_QAbstractHttpServer)
|
||||
|
||||
#include "tst_qabstracthttpserver.moc"
|
||||
17
src/qthttpserver/tests/auto/qhttpserver/CMakeLists.txt
Normal file
17
src/qthttpserver/tests/auto/qhttpserver/CMakeLists.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
# Generated from qhttpserver.pro.
|
||||
|
||||
#####################################################################
|
||||
## tst_qhttpserver Test:
|
||||
#####################################################################
|
||||
|
||||
# Collect test data
|
||||
list(APPEND test_data "data/")
|
||||
|
||||
qt_add_test(tst_qhttpserver
|
||||
SOURCES
|
||||
tst_qhttpserver.cpp
|
||||
PUBLIC_LIBRARIES
|
||||
Qt::HttpServer
|
||||
Qt::HttpServerPrivate
|
||||
TESTDATA ${test_data}
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
{ "key": "value" }
|
||||
1
src/qthttpserver/tests/auto/qhttpserver/data/text.html
Normal file
1
src/qthttpserver/tests/auto/qhttpserver/data/text.html
Normal file
@@ -0,0 +1 @@
|
||||
<html></html>
|
||||
7
src/qthttpserver/tests/auto/qhttpserver/qhttpserver.pro
Normal file
7
src/qthttpserver/tests/auto/qhttpserver/qhttpserver.pro
Normal file
@@ -0,0 +1,7 @@
|
||||
CONFIG += testcase
|
||||
TARGET = tst_qhttpserver
|
||||
SOURCES += tst_qhttpserver.cpp
|
||||
|
||||
QT = httpserver httpserver-private testlib
|
||||
|
||||
TESTDATA += data/
|
||||
977
src/qthttpserver/tests/auto/qhttpserver/tst_qhttpserver.cpp
Normal file
977
src/qthttpserver/tests/auto/qhttpserver/tst_qhttpserver.cpp
Normal file
@@ -0,0 +1,977 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtHttpServer/qhttpserver.h>
|
||||
#include <QtHttpServer/qhttpserverrequest.h>
|
||||
#include <QtHttpServer/qhttpserverrouterrule.h>
|
||||
|
||||
#if QT_CONFIG(concurrent)
|
||||
# include <QtHttpServer/qhttpserverfutureresponse.h>
|
||||
#endif
|
||||
|
||||
#include <private/qhttpserverrouterrule_p.h>
|
||||
#include <private/qhttpserverliterals_p.h>
|
||||
|
||||
#include <QtTest/qtest.h>
|
||||
#include <QtTest/qsignalspy.h>
|
||||
|
||||
#include <QtCore/qurl.h>
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qdatetime.h>
|
||||
#include <QtCore/qmetaobject.h>
|
||||
#include <QtCore/qjsonobject.h>
|
||||
#include <QtCore/qjsonvalue.h>
|
||||
#include <QtCore/qjsonarray.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
|
||||
#include <QtNetwork/qnetworkaccessmanager.h>
|
||||
#include <QtNetwork/qnetworkreply.h>
|
||||
#include <QtNetwork/qnetworkrequest.h>
|
||||
|
||||
|
||||
static const char g_privateKey[] = R"(-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXQIBAAKBgQDykG51ZjNJra8iS27g3DJojH1qG8C3Z+Avo5U6Qz6NkOsjvr22
|
||||
gXqOS4uwVUXdCAKxsP0Wwn2zGz5vxGpLPVKtbAmaqHYZuipMG/Qun3t+QYBgR+9t
|
||||
lmHdI8TNP2Om8stDO5uQyBH7DcMjPyIgpfc8fBoNLhCn4oC2n6JK9EMuhQIDAQAB
|
||||
AoGAUHTLzrEJjgTINI3kxz0Ck18WMl3mPG9+Ew8lbl/jnb1V4VNhReoIpq40NVbz
|
||||
h28ixaG5MRVt8Dy3Jwd1YmOCylHSujdFQ2u0pcHFmERgDS2bOMwMTRoFOj2qgMGS
|
||||
9SM+iXlPY5AQY8nEg7rLjMSfaC/8Hq4RXpkj4PeHh6N7AzkCQQD++HzM3xBr+Gvh
|
||||
zco9Kt8IiKNlfeiA5gUQq1UPJzcWIEgW1Tgr5UzMUOcZ0HfYwhqL3+wMhzN4sba+
|
||||
1plB1QRXAkEA84sfM0jm9BRSqtYTPlhsYAmuPjeo24Pxel8ijEkToAu0ppEC6AQ3
|
||||
zfwZD0ISgaWQ7af5TN/RCsoNVX79twP6gwJBANbtB+Z6shERm38ARdZB6Tf8ViAb
|
||||
fn4JZ4OhqVXYrKrOE3aLzYnTBGXGXMh53kytcksuOoBlB5JZ274Kj63arokCQFPo
|
||||
9xMAZzJpXiImJ/MvHAfqzfH501/ukeCLrqeO9ggKgG9zPwEZkvCRj0DGjwHEPa7k
|
||||
VOy7oJaLDxUJ7/iCkmkCQQCtTLsvDbGH4tyFK5VIPJbUcccIib+dTzSTeONdUxKL
|
||||
Yk+C6o7OpaUWX+ikp4Ow/6iHOAgXaeA2OolDer/NspUy
|
||||
-----END RSA PRIVATE KEY-----)";
|
||||
|
||||
static const char g_certificate[] = R"(-----BEGIN CERTIFICATE-----
|
||||
MIICrjCCAhegAwIBAgIUcuXjCSkJ2+v/Rqv/UHThTRGFlpswDQYJKoZIhvcNAQEL
|
||||
BQAwaDELMAkGA1UEBhMCRlIxDzANBgNVBAgMBkZyYW5jZTERMA8GA1UEBwwIR3Jl
|
||||
bm9ibGUxFjAUBgNVBAoMDVF0Q29udHJpYnV0b3IxHTAbBgNVBAMMFHFodHRwc3Nl
|
||||
cnZlcnRlc3QuY29tMCAXDTE5MDkyNjA4NTc1MloYDzIyNTUwMzEzMDg1NzUyWjBo
|
||||
MQswCQYDVQQGEwJGUjEPMA0GA1UECAwGRnJhbmNlMREwDwYDVQQHDAhHcmVub2Js
|
||||
ZTEWMBQGA1UECgwNUXRDb250cmlidXRvcjEdMBsGA1UEAwwUcWh0dHBzc2VydmVy
|
||||
dGVzdC5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPKQbnVmM0mtryJL
|
||||
buDcMmiMfWobwLdn4C+jlTpDPo2Q6yO+vbaBeo5Li7BVRd0IArGw/RbCfbMbPm/E
|
||||
aks9Uq1sCZqodhm6Kkwb9C6fe35BgGBH722WYd0jxM0/Y6byy0M7m5DIEfsNwyM/
|
||||
IiCl9zx8Gg0uEKfigLafokr0Qy6FAgMBAAGjUzBRMB0GA1UdDgQWBBTDMYCcl2jz
|
||||
UUWByEzTj5Ew/LWkeDAfBgNVHSMEGDAWgBTDMYCcl2jzUUWByEzTj5Ew/LWkeDAP
|
||||
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBAMNupAOXoBih6RvuAn3w
|
||||
W8jOIZfkn5CMYdbUSndY/Wrt4p07M8r9uFPWG4bXSwG6n9Nzl75X9b0ka/jqPjQ3
|
||||
X769simPygCblBp2xwE6w14aHEBx4kcF1p2QbC1vHynszJxyVLvHqUjuJwVAoPrM
|
||||
Imy6LOiw2tRTHPsj7UH16M6C
|
||||
-----END CERTIFICATE-----)";
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QueryRequireRouterRule : public QHttpServerRouterRule
|
||||
{
|
||||
public:
|
||||
QueryRequireRouterRule(const QString &pathPattern,
|
||||
const char *queryKey,
|
||||
RouterHandler &&routerHandler)
|
||||
: QHttpServerRouterRule(pathPattern, std::forward<RouterHandler>(routerHandler)),
|
||||
m_queryKey(queryKey)
|
||||
{
|
||||
}
|
||||
|
||||
bool matches(const QHttpServerRequest &request, QRegularExpressionMatch *match) const override
|
||||
{
|
||||
if (QHttpServerRouterRule::matches(request, match)) {
|
||||
if (request.query().hasQueryItem(m_queryKey))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const char * m_queryKey;
|
||||
};
|
||||
|
||||
class tst_QHttpServer final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void routeGet_data();
|
||||
void routeGet();
|
||||
void routeKeepAlive();
|
||||
void routePost_data();
|
||||
void routePost();
|
||||
void routeDelete_data();
|
||||
void routeDelete();
|
||||
void routeExtraHeaders();
|
||||
void invalidRouterArguments();
|
||||
void checkRouteLambdaCapture();
|
||||
void afterRequest();
|
||||
void disconnectedInEventLoop();
|
||||
|
||||
private:
|
||||
void checkReply(QNetworkReply *reply, const QString &response);
|
||||
|
||||
private:
|
||||
QHttpServer httpserver;
|
||||
QString urlBase;
|
||||
QString sslUrlBase;
|
||||
QNetworkAccessManager networkAccessManager;
|
||||
};
|
||||
|
||||
struct CustomArg {
|
||||
int data = 10;
|
||||
|
||||
CustomArg() {} ;
|
||||
CustomArg(const QString &urlArg) : data(urlArg.toInt()) {}
|
||||
};
|
||||
|
||||
void tst_QHttpServer::initTestCase()
|
||||
{
|
||||
|
||||
httpserver.route("/req-and-resp", [] (QHttpServerResponder &&resp,
|
||||
const QHttpServerRequest &req) {
|
||||
resp.write(req.body(),
|
||||
QHttpServerLiterals::contentTypeTextHtml());
|
||||
});
|
||||
|
||||
httpserver.route("/resp-and-req", [] (const QHttpServerRequest &req,
|
||||
QHttpServerResponder &&resp) {
|
||||
resp.write(req.body(),
|
||||
QHttpServerLiterals::contentTypeTextHtml());
|
||||
});
|
||||
|
||||
httpserver.route("/test", [] (QHttpServerResponder &&responder) {
|
||||
responder.write("test msg",
|
||||
QHttpServerLiterals::contentTypeTextHtml());
|
||||
});
|
||||
|
||||
httpserver.route("/", QHttpServerRequest::Method::Get, [] () {
|
||||
return "Hello world get";
|
||||
});
|
||||
|
||||
httpserver.route("/", QHttpServerRequest::Method::Post, [] () {
|
||||
return "Hello world post";
|
||||
});
|
||||
|
||||
httpserver.route("/post-and-get", "GET|POST", [] (const QHttpServerRequest &request) {
|
||||
if (request.method() == QHttpServerRequest::Method::Get)
|
||||
return "Hello world get";
|
||||
else if (request.method() == QHttpServerRequest::Method::Post)
|
||||
return "Hello world post";
|
||||
|
||||
return "This should not work";
|
||||
});
|
||||
|
||||
httpserver.route("/any", "All", [] (const QHttpServerRequest &request) {
|
||||
static const auto metaEnum = QMetaEnum::fromType<QHttpServerRequest::Method>();
|
||||
return metaEnum.valueToKey(static_cast<int>(request.method()));
|
||||
});
|
||||
|
||||
httpserver.route("/page/", [] (const qint32 number) {
|
||||
return QString("page: %1").arg(number);
|
||||
});
|
||||
|
||||
httpserver.route("/page/<arg>/detail", [] (const quint32 number) {
|
||||
return QString("page: %1 detail").arg(number);
|
||||
});
|
||||
|
||||
httpserver.route("/user/", [] (const QString &name) {
|
||||
return QString("%1").arg(name);
|
||||
});
|
||||
|
||||
httpserver.route("/user/<arg>/", [] (const QString &name, const QByteArray &ba) {
|
||||
return QString("%1-%2").arg(name).arg(QString::fromLatin1(ba));
|
||||
});
|
||||
|
||||
httpserver.route("/test/", [] (const QUrl &url) {
|
||||
return QString("path: %1").arg(url.path());
|
||||
});
|
||||
|
||||
httpserver.route("/api/v", [] (const float api) {
|
||||
return QString("api %1v").arg(api);
|
||||
});
|
||||
|
||||
httpserver.route("/api/v<arg>/user/", [] (const float api, const quint64 user) {
|
||||
return QString("api %1v, user id - %2").arg(api).arg(user);
|
||||
});
|
||||
|
||||
httpserver.route("/api/v<arg>/user/<arg>/settings", [] (const float api, const quint64 user,
|
||||
const QHttpServerRequest &request) {
|
||||
const auto &role = request.query().queryItemValue(QString::fromLatin1("role"));
|
||||
const auto &fragment = request.url().fragment();
|
||||
|
||||
return QString("api %1v, user id - %2, set settings role=%3#'%4'")
|
||||
.arg(api).arg(user).arg(role, fragment);
|
||||
});
|
||||
|
||||
httpserver.route<QueryRequireRouterRule>(
|
||||
"/custom/",
|
||||
"key",
|
||||
[] (const quint64 num, const QHttpServerRequest &request) {
|
||||
return QString("Custom router rule: %1, key=%2")
|
||||
.arg(num)
|
||||
.arg(request.query().queryItemValue("key"));
|
||||
});
|
||||
|
||||
httpserver.router()->addConverter<CustomArg>(QLatin1String("[+-]?\\d+"));
|
||||
httpserver.route("/check-custom-type/", [] (const CustomArg &customArg) {
|
||||
return QString("data = %1").arg(customArg.data);
|
||||
});
|
||||
|
||||
httpserver.route("/post-body", "POST", [] (const QHttpServerRequest &request) {
|
||||
return request.body();
|
||||
});
|
||||
|
||||
httpserver.route("/file/", [] (const QString &file) {
|
||||
return QHttpServerResponse::fromFile(QFINDTESTDATA(QLatin1String("data/") + file));
|
||||
});
|
||||
|
||||
httpserver.route("/json-object/", [] () {
|
||||
return QJsonObject{
|
||||
{"property", "test"},
|
||||
{"value", 1}
|
||||
};
|
||||
});
|
||||
|
||||
httpserver.route("/json-array/", [] () {
|
||||
return QJsonArray{
|
||||
1, "2",
|
||||
QJsonObject{
|
||||
{"name", "test"}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
httpserver.route("/chunked/", [] (QHttpServerResponder &&responder) {
|
||||
responder.writeStatusLine(QHttpServerResponder::StatusCode::Ok);
|
||||
responder.writeHeaders({
|
||||
{"Content-Type", "text/plain"},
|
||||
{"Transfer-Encoding", "chunked"} });
|
||||
|
||||
auto writeChunk = [&responder] (const char *message) {
|
||||
responder.writeBody(QByteArray::number(qstrlen(message), 16));
|
||||
responder.writeBody("\r\n");
|
||||
responder.writeBody(message);
|
||||
responder.writeBody("\r\n");
|
||||
};
|
||||
|
||||
writeChunk("part 1 of the message, ");
|
||||
writeChunk("part 2 of the message");
|
||||
writeChunk("");
|
||||
});
|
||||
|
||||
httpserver.route("/extra-headers", [] () {
|
||||
QHttpServerResponse resp("");
|
||||
resp.setHeader("Content-Type", "application/x-empty");
|
||||
resp.setHeader("Server", "test server");
|
||||
return resp;
|
||||
});
|
||||
|
||||
httpserver.afterRequest([] (QHttpServerResponse &&resp) {
|
||||
return std::move(resp);
|
||||
});
|
||||
|
||||
#if QT_CONFIG(concurrent)
|
||||
httpserver.route("/future/", [] (int id) -> QHttpServerFutureResponse {
|
||||
if (id == 0)
|
||||
return QHttpServerResponse::StatusCode::NotFound;
|
||||
|
||||
auto future = QtConcurrent::run([] () {
|
||||
QTest::qSleep(500);
|
||||
return QHttpServerResponse("future is coming");
|
||||
});
|
||||
|
||||
return future;
|
||||
});
|
||||
#endif
|
||||
|
||||
quint16 port = httpserver.listen();
|
||||
if (!port)
|
||||
qCritical() << "Http server listen failed";
|
||||
|
||||
urlBase = QStringLiteral("http://localhost:%1%2").arg(port);
|
||||
|
||||
#if QT_CONFIG(ssl)
|
||||
httpserver.sslSetup(QSslCertificate(g_certificate),
|
||||
QSslKey(g_privateKey, QSsl::Rsa));
|
||||
|
||||
port = httpserver.listen();
|
||||
if (!port)
|
||||
qCritical() << "Http server listen failed";
|
||||
|
||||
sslUrlBase = QStringLiteral("https://localhost:%1%2").arg(port);
|
||||
|
||||
QList<QSslError> expectedSslErrors;
|
||||
|
||||
// Non-OpenSSL backends are not able to report a specific error code
|
||||
// for self-signed certificates.
|
||||
#ifndef QT_NO_OPENSSL
|
||||
# define FLUKE_CERTIFICATE_ERROR QSslError::SelfSignedCertificate
|
||||
#else
|
||||
# define FLUKE_CERTIFICATE_ERROR QSslError::CertificateUntrusted
|
||||
#endif
|
||||
|
||||
expectedSslErrors.append(QSslError(FLUKE_CERTIFICATE_ERROR,
|
||||
QSslCertificate(g_certificate)));
|
||||
expectedSslErrors.append(QSslError(QSslError::HostNameMismatch,
|
||||
QSslCertificate(g_certificate)));
|
||||
|
||||
connect(&networkAccessManager, &QNetworkAccessManager::sslErrors,
|
||||
[expectedSslErrors](QNetworkReply *reply,
|
||||
const QList<QSslError> &errors) {
|
||||
for (const auto &error: errors) {
|
||||
for (const auto &expectedError: expectedSslErrors) {
|
||||
if (error.error() != expectedError.error() ||
|
||||
error.certificate() != expectedError.certificate()) {
|
||||
qCritical() << "Got unexpected ssl error:"
|
||||
<< error << error.certificate();
|
||||
}
|
||||
}
|
||||
}
|
||||
reply->ignoreSslErrors(expectedSslErrors);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QHttpServer::routeGet_data()
|
||||
{
|
||||
QTest::addColumn<QString>("url");
|
||||
QTest::addColumn<int>("code");
|
||||
QTest::addColumn<QString>("type");
|
||||
QTest::addColumn<QString>("body");
|
||||
|
||||
QTest::addRow("hello world")
|
||||
<< urlBase.arg("/")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "Hello world get";
|
||||
|
||||
QTest::addRow("test msg")
|
||||
<< urlBase.arg("/test")
|
||||
<< 200
|
||||
<< "text/html"
|
||||
<< "test msg";
|
||||
|
||||
QTest::addRow("not found")
|
||||
<< urlBase.arg("/not-found")
|
||||
<< 404
|
||||
<< "application/x-empty"
|
||||
<< "";
|
||||
|
||||
QTest::addRow("arg:int")
|
||||
<< urlBase.arg("/page/10")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "page: 10";
|
||||
|
||||
QTest::addRow("arg:-int")
|
||||
<< urlBase.arg("/page/-10")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "page: -10";
|
||||
|
||||
QTest::addRow("arg:uint")
|
||||
<< urlBase.arg("/page/10/detail")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "page: 10 detail";
|
||||
|
||||
QTest::addRow("arg:-uint")
|
||||
<< urlBase.arg("/page/-10/detail")
|
||||
<< 404
|
||||
<< "application/x-empty"
|
||||
<< "";
|
||||
|
||||
QTest::addRow("arg:string")
|
||||
<< urlBase.arg("/user/test")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "test";
|
||||
|
||||
QTest::addRow("arg:string")
|
||||
<< urlBase.arg("/user/test test ,!a+.")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "test test ,!a+.";
|
||||
|
||||
QTest::addRow("arg:string,ba")
|
||||
<< urlBase.arg("/user/james/bond")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "james-bond";
|
||||
|
||||
QTest::addRow("arg:url")
|
||||
<< urlBase.arg("/test/api/v0/cmds?val=1")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "path: api/v0/cmds";
|
||||
|
||||
QTest::addRow("arg:float 5.1")
|
||||
<< urlBase.arg("/api/v5.1")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "api 5.1v";
|
||||
|
||||
QTest::addRow("arg:float 5.")
|
||||
<< urlBase.arg("/api/v5.")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "api 5v";
|
||||
|
||||
QTest::addRow("arg:float 6.0")
|
||||
<< urlBase.arg("/api/v6.0")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "api 6v";
|
||||
|
||||
QTest::addRow("arg:float,uint")
|
||||
<< urlBase.arg("/api/v5.1/user/10")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "api 5.1v, user id - 10";
|
||||
|
||||
QTest::addRow("arg:float,uint,query")
|
||||
<< urlBase.arg("/api/v5.2/user/11/settings?role=admin")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "api 5.2v, user id - 11, set settings role=admin#''";
|
||||
|
||||
// The fragment isn't actually sent via HTTP (it's information for the user agent)
|
||||
QTest::addRow("arg:float,uint, query+fragment")
|
||||
<< urlBase.arg("/api/v5.2/user/11/settings?role=admin#tag")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "api 5.2v, user id - 11, set settings role=admin#''";
|
||||
|
||||
QTest::addRow("custom route rule")
|
||||
<< urlBase.arg("/custom/15")
|
||||
<< 404
|
||||
<< "application/x-empty"
|
||||
<< "";
|
||||
|
||||
QTest::addRow("custom route rule + query")
|
||||
<< urlBase.arg("/custom/10?key=11&g=1")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "Custom router rule: 10, key=11";
|
||||
|
||||
QTest::addRow("custom route rule + query key req")
|
||||
<< urlBase.arg("/custom/10?g=1&key=12")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "Custom router rule: 10, key=12";
|
||||
|
||||
QTest::addRow("post-and-get, get")
|
||||
<< urlBase.arg("/post-and-get")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "Hello world get";
|
||||
|
||||
QTest::addRow("invalid-rule-method, get")
|
||||
<< urlBase.arg("/invalid-rule-method")
|
||||
<< 404
|
||||
<< "application/x-empty"
|
||||
<< "";
|
||||
|
||||
QTest::addRow("check custom type, data=1")
|
||||
<< urlBase.arg("/check-custom-type/1")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "data = 1";
|
||||
|
||||
QTest::addRow("any, get")
|
||||
<< urlBase.arg("/any")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "Get";
|
||||
|
||||
QTest::addRow("response from html file")
|
||||
<< urlBase.arg("/file/text.html")
|
||||
<< 200
|
||||
<< "text/html"
|
||||
<< "<html></html>";
|
||||
|
||||
QTest::addRow("response from json file")
|
||||
<< urlBase.arg("/file/application.json")
|
||||
<< 200
|
||||
<< "application/json"
|
||||
<< "{ \"key\": \"value\" }";
|
||||
|
||||
QTest::addRow("json-object")
|
||||
<< urlBase.arg("/json-object/")
|
||||
<< 200
|
||||
<< "application/json"
|
||||
<< "{\"property\":\"test\",\"value\":1}";
|
||||
|
||||
QTest::addRow("json-array")
|
||||
<< urlBase.arg("/json-array/")
|
||||
<< 200
|
||||
<< "application/json"
|
||||
<< "[1,\"2\",{\"name\":\"test\"}]";
|
||||
|
||||
QTest::addRow("chunked")
|
||||
<< urlBase.arg("/chunked/")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "part 1 of the message, part 2 of the message";
|
||||
|
||||
#if QT_CONFIG(concurrent)
|
||||
QTest::addRow("future")
|
||||
<< urlBase.arg("/future/1")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "future is coming";
|
||||
|
||||
QTest::addRow("future-not-found")
|
||||
<< urlBase.arg("/future/0")
|
||||
<< 404
|
||||
<< "application/x-empty"
|
||||
<< "";
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(ssl)
|
||||
|
||||
QTest::addRow("hello world, ssl")
|
||||
<< sslUrlBase.arg("/")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "Hello world get";
|
||||
|
||||
QTest::addRow("post-and-get, get, ssl")
|
||||
<< sslUrlBase.arg("/post-and-get")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "Hello world get";
|
||||
|
||||
QTest::addRow("invalid-rule-method, get, ssl")
|
||||
<< sslUrlBase.arg("/invalid-rule-method")
|
||||
<< 404
|
||||
<< "application/x-empty"
|
||||
<< "";
|
||||
|
||||
QTest::addRow("check custom type, data=1, ssl")
|
||||
<< sslUrlBase.arg("/check-custom-type/1")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "data = 1";
|
||||
|
||||
#endif // QT_CONFIG(ssl)
|
||||
}
|
||||
|
||||
void tst_QHttpServer::routeGet()
|
||||
{
|
||||
QFETCH(QString, url);
|
||||
QFETCH(int, code);
|
||||
QFETCH(QString, type);
|
||||
QFETCH(QString, body);
|
||||
|
||||
auto reply = networkAccessManager.get(QNetworkRequest(url));
|
||||
|
||||
QTRY_VERIFY(reply->isFinished());
|
||||
|
||||
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type);
|
||||
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code);
|
||||
QCOMPARE(reply->readAll().trimmed(), body);
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void tst_QHttpServer::routeKeepAlive()
|
||||
{
|
||||
httpserver.route("/keep-alive", [] (const QHttpServerRequest &req) -> QHttpServerResponse {
|
||||
if (req.headers()["Connection"] != "keep-alive")
|
||||
return QHttpServerResponse::StatusCode::NotFound;
|
||||
|
||||
return QString("header: %1, query: %2, body: %3, method: %4")
|
||||
.arg(req.value("CustomHeader"),
|
||||
req.url().query(),
|
||||
req.body())
|
||||
.arg(static_cast<int>(req.method()));
|
||||
});
|
||||
|
||||
QNetworkRequest request(urlBase.arg("/keep-alive"));
|
||||
request.setRawHeader(QByteArray("Connection"), QByteArray("keep-alive"));
|
||||
|
||||
checkReply(networkAccessManager.get(request),
|
||||
QString("header: , query: , body: , method: %1")
|
||||
.arg(static_cast<int>(QHttpServerRequest::Method::Get)));
|
||||
if (QTest::currentTestFailed())
|
||||
return;
|
||||
|
||||
request.setUrl(urlBase.arg("/keep-alive?po=98"));
|
||||
request.setRawHeader("CustomHeader", "1");
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||
QHttpServerLiterals::contentTypeTextHtml());
|
||||
|
||||
checkReply(networkAccessManager.post(request, QByteArray("test")),
|
||||
QString("header: 1, query: po=98, body: test, method: %1")
|
||||
.arg(static_cast<int>(QHttpServerRequest::Method::Post)));
|
||||
if (QTest::currentTestFailed())
|
||||
return;
|
||||
|
||||
request = QNetworkRequest(urlBase.arg("/keep-alive"));
|
||||
request.setRawHeader(QByteArray("Connection"), QByteArray("keep-alive"));
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||
QHttpServerLiterals::contentTypeTextHtml());
|
||||
|
||||
checkReply(networkAccessManager.post(request, QByteArray("")),
|
||||
QString("header: , query: , body: , method: %1")
|
||||
.arg(static_cast<int>(QHttpServerRequest::Method::Post)));
|
||||
if (QTest::currentTestFailed())
|
||||
return;
|
||||
|
||||
checkReply(networkAccessManager.get(request),
|
||||
QString("header: , query: , body: , method: %1")
|
||||
.arg(static_cast<int>(QHttpServerRequest::Method::Get)));
|
||||
if (QTest::currentTestFailed())
|
||||
return;
|
||||
}
|
||||
|
||||
void tst_QHttpServer::routePost_data()
|
||||
{
|
||||
QTest::addColumn<QString>("url");
|
||||
QTest::addColumn<int>("code");
|
||||
QTest::addColumn<QString>("type");
|
||||
QTest::addColumn<QString>("data");
|
||||
QTest::addColumn<QString>("body");
|
||||
|
||||
QTest::addRow("hello world")
|
||||
<< urlBase.arg("/")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< ""
|
||||
<< "Hello world post";
|
||||
|
||||
QTest::addRow("post-and-get, post")
|
||||
<< urlBase.arg("/post-and-get")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< ""
|
||||
<< "Hello world post";
|
||||
|
||||
QTest::addRow("any, post")
|
||||
<< urlBase.arg("/any")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< ""
|
||||
<< "Post";
|
||||
|
||||
QTest::addRow("post-body")
|
||||
<< urlBase.arg("/post-body")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "some post data"
|
||||
<< "some post data";
|
||||
|
||||
QString body;
|
||||
for (int i = 0; i < 10000; i++)
|
||||
body.append(QString::number(i));
|
||||
|
||||
QTest::addRow("post-body - huge body, chunk test")
|
||||
<< urlBase.arg("/post-body")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< body
|
||||
<< body;
|
||||
|
||||
QTest::addRow("req-and-resp")
|
||||
<< urlBase.arg("/req-and-resp")
|
||||
<< 200
|
||||
<< "text/html"
|
||||
<< "test"
|
||||
<< "test";
|
||||
|
||||
QTest::addRow("resp-and-req")
|
||||
<< urlBase.arg("/resp-and-req")
|
||||
<< 200
|
||||
<< "text/html"
|
||||
<< "test"
|
||||
<< "test";
|
||||
|
||||
#if QT_CONFIG(ssl)
|
||||
|
||||
QTest::addRow("post-and-get, post, ssl")
|
||||
<< sslUrlBase.arg("/post-and-get")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< ""
|
||||
<< "Hello world post";
|
||||
|
||||
QTest::addRow("any, post, ssl")
|
||||
<< sslUrlBase.arg("/any")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< ""
|
||||
<< "Post";
|
||||
|
||||
QTest::addRow("post-body, ssl")
|
||||
<< sslUrlBase.arg("/post-body")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "some post data"
|
||||
<< "some post data";
|
||||
|
||||
QTest::addRow("post-body - huge body, chunk test, ssl")
|
||||
<< sslUrlBase.arg("/post-body")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< body
|
||||
<< body;
|
||||
|
||||
#endif // QT_CONFIG(ssl)
|
||||
}
|
||||
|
||||
void tst_QHttpServer::routePost()
|
||||
{
|
||||
QFETCH(QString, url);
|
||||
QFETCH(int, code);
|
||||
QFETCH(QString, type);
|
||||
QFETCH(QString, data);
|
||||
QFETCH(QString, body);
|
||||
|
||||
QNetworkRequest request(url);
|
||||
if (data.size()) {
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||
QHttpServerLiterals::contentTypeTextHtml());
|
||||
}
|
||||
|
||||
auto reply = networkAccessManager.post(request, data.toUtf8());
|
||||
|
||||
QTRY_VERIFY(reply->isFinished());
|
||||
|
||||
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type);
|
||||
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code);
|
||||
QCOMPARE(reply->readAll(), body);
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void tst_QHttpServer::routeDelete_data()
|
||||
{
|
||||
QTest::addColumn<QString>("url");
|
||||
QTest::addColumn<int>("code");
|
||||
QTest::addColumn<QString>("type");
|
||||
QTest::addColumn<QString>("data");
|
||||
|
||||
QTest::addRow("post-and-get, delete")
|
||||
<< urlBase.arg("/post-and-get")
|
||||
<< 404
|
||||
<< "application/x-empty"
|
||||
<< "";
|
||||
|
||||
QTest::addRow("any, delete")
|
||||
<< urlBase.arg("/any")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "Delete";
|
||||
|
||||
#if QT_CONFIG(ssl)
|
||||
|
||||
QTest::addRow("post-and-get, delete, ssl")
|
||||
<< sslUrlBase.arg("/post-and-get")
|
||||
<< 404
|
||||
<< "application/x-empty"
|
||||
<< "";
|
||||
|
||||
QTest::addRow("any, delete, ssl")
|
||||
<< sslUrlBase.arg("/any")
|
||||
<< 200
|
||||
<< "text/plain"
|
||||
<< "Delete";
|
||||
|
||||
#endif // QT_CONFIG(ssl)
|
||||
}
|
||||
|
||||
void tst_QHttpServer::routeDelete()
|
||||
{
|
||||
QFETCH(QString, url);
|
||||
QFETCH(int, code);
|
||||
QFETCH(QString, type);
|
||||
QFETCH(QString, data);
|
||||
|
||||
auto reply = networkAccessManager.deleteResource(QNetworkRequest(url));
|
||||
|
||||
QTRY_VERIFY(reply->isFinished());
|
||||
|
||||
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type);
|
||||
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code);
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void tst_QHttpServer::routeExtraHeaders()
|
||||
{
|
||||
const QUrl requestUrl(urlBase.arg("/extra-headers"));
|
||||
auto reply = networkAccessManager.get(QNetworkRequest(requestUrl));
|
||||
|
||||
QTRY_VERIFY(reply->isFinished());
|
||||
|
||||
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), "application/x-empty");
|
||||
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
|
||||
QCOMPARE(reply->header(QNetworkRequest::ServerHeader), "test server");
|
||||
}
|
||||
|
||||
struct CustomType {
|
||||
CustomType() {}
|
||||
CustomType(const QString &) {}
|
||||
};
|
||||
|
||||
void tst_QHttpServer::invalidRouterArguments()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "Can not find converter for type: QDateTime");
|
||||
QCOMPARE(
|
||||
httpserver.route("/datetime/", [] (const QDateTime &datetime) {
|
||||
return QString("datetime: %1").arg(datetime.toString());
|
||||
}),
|
||||
false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "Can not convert GeT to QHttpServerRequest::Method");
|
||||
QCOMPARE(
|
||||
httpserver.route("/invalid-rule-method", "GeT", [] () {
|
||||
return "";
|
||||
}),
|
||||
false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "Can not convert Garbage to QHttpServerRequest::Method");
|
||||
QCOMPARE(
|
||||
httpserver.route("/invalid-rule-method", "Garbage", [] () {
|
||||
return "";
|
||||
}),
|
||||
false);
|
||||
|
||||
QCOMPARE(
|
||||
httpserver.route("/invalid-rule-method", "Unknown", [] () {
|
||||
return "";
|
||||
}),
|
||||
false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg,
|
||||
"CustomType has not registered a converter to QString. "
|
||||
"Use QHttpServerRouter::addConveter<Type>(converter).");
|
||||
QCOMPARE(
|
||||
httpserver.route("/implicit-conversion-to-qstring-has-no-registered/",
|
||||
[] (const CustomType &) {
|
||||
return "";
|
||||
}),
|
||||
false);
|
||||
}
|
||||
|
||||
void tst_QHttpServer::checkRouteLambdaCapture()
|
||||
{
|
||||
httpserver.route("/capture-this/", [this] () {
|
||||
return urlBase;
|
||||
});
|
||||
|
||||
QString msg = urlBase + "/pod";
|
||||
httpserver.route("/capture-non-pod-data/", [&msg] () {
|
||||
return msg;
|
||||
});
|
||||
|
||||
checkReply(networkAccessManager.get(
|
||||
QNetworkRequest(QUrl(urlBase.arg("/capture-this/")))),
|
||||
urlBase);
|
||||
if (QTest::currentTestFailed())
|
||||
return;
|
||||
|
||||
checkReply(networkAccessManager.get(
|
||||
QNetworkRequest(QUrl(urlBase.arg("/capture-non-pod-data/")))),
|
||||
msg);
|
||||
if (QTest::currentTestFailed())
|
||||
return;
|
||||
}
|
||||
|
||||
void tst_QHttpServer::afterRequest()
|
||||
{
|
||||
httpserver.afterRequest([] (QHttpServerResponse &&resp,
|
||||
const QHttpServerRequest &request) {
|
||||
if (request.url().path() == "/test-after-request")
|
||||
resp.setHeader("Arguments-Order-1", "resp, request");
|
||||
|
||||
return std::move(resp);
|
||||
});
|
||||
|
||||
httpserver.afterRequest([] (const QHttpServerRequest &request,
|
||||
QHttpServerResponse &&resp) {
|
||||
if (request.url().path() == "/test-after-request")
|
||||
resp.setHeader("Arguments-Order-2", "request, resp");
|
||||
|
||||
return std::move(resp);
|
||||
});
|
||||
|
||||
const QUrl requestUrl(urlBase.arg("/test-after-request"));
|
||||
auto reply = networkAccessManager.get(QNetworkRequest(requestUrl));
|
||||
|
||||
QTRY_VERIFY(reply->isFinished());
|
||||
|
||||
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), "application/x-empty");
|
||||
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 404);
|
||||
QCOMPARE(reply->rawHeader("Arguments-Order-1"), "resp, request");
|
||||
QCOMPARE(reply->rawHeader("Arguments-Order-2"), "request, resp");
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void tst_QHttpServer::checkReply(QNetworkReply *reply, const QString &response) {
|
||||
QTRY_VERIFY(reply->isFinished());
|
||||
|
||||
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), "text/plain");
|
||||
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
|
||||
QCOMPARE(reply->readAll(), response);
|
||||
|
||||
reply->deleteLater();
|
||||
};
|
||||
|
||||
void tst_QHttpServer::disconnectedInEventLoop()
|
||||
{
|
||||
httpserver.route("/event-loop/", [] () {
|
||||
QEventLoop loop;
|
||||
QTimer::singleShot(1000, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
return QHttpServerResponse::StatusCode::Ok;
|
||||
});
|
||||
|
||||
const QUrl requestUrl(urlBase.arg("/event-loop/"));
|
||||
auto reply = networkAccessManager.get(QNetworkRequest(requestUrl));
|
||||
QTimer::singleShot(500, reply, &QNetworkReply::abort); // cancel connection
|
||||
QEventLoop loop;
|
||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
Q_DECLARE_METATYPE(CustomArg);
|
||||
Q_DECLARE_METATYPE(CustomType);
|
||||
|
||||
QTEST_MAIN(tst_QHttpServer)
|
||||
|
||||
#include "tst_qhttpserver.moc"
|
||||
@@ -0,0 +1,17 @@
|
||||
# Generated from qhttpserverresponder.pro.
|
||||
|
||||
#####################################################################
|
||||
## tst_qhttpserverresponder Test:
|
||||
#####################################################################
|
||||
|
||||
# Collect test data
|
||||
list(APPEND test_data "data/")
|
||||
|
||||
qt_add_test(tst_qhttpserverresponder
|
||||
SOURCES
|
||||
tst_qhttpserverresponder.cpp
|
||||
PUBLIC_LIBRARIES
|
||||
Qt::HttpServer
|
||||
Qt::HttpServerPrivate
|
||||
TESTDATA ${test_data}
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
<html></html>
|
||||
@@ -0,0 +1,7 @@
|
||||
CONFIG += testcase
|
||||
TARGET = tst_qhttpserverresponder
|
||||
SOURCES += tst_qhttpserverresponder.cpp
|
||||
|
||||
QT = httpserver httpserver-private testlib
|
||||
|
||||
TESTDATA += data/
|
||||
@@ -0,0 +1,284 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtHttpServer/qhttpserverresponder.h>
|
||||
#include <QtHttpServer/qabstracthttpserver.h>
|
||||
|
||||
#include <private/qhttpserverliterals_p.h>
|
||||
|
||||
#include <QtCore/qjsondocument.h>
|
||||
#include <QtCore/qfile.h>
|
||||
#include <QtCore/qtemporaryfile.h>
|
||||
#include <QtTest/qsignalspy.h>
|
||||
#include <QtTest/qtest.h>
|
||||
#include <QtNetwork/qnetworkaccessmanager.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static const QByteArray headerServerString(QByteArrayLiteral("Server"));
|
||||
static const QByteArray headerServerValue(QByteArrayLiteral("Test server"));
|
||||
|
||||
class tst_QHttpServerResponder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
std::unique_ptr<QNetworkAccessManager> networkAccessManager;
|
||||
|
||||
private slots:
|
||||
void init() { networkAccessManager.reset(new QNetworkAccessManager); }
|
||||
void cleanup() { networkAccessManager.reset(); }
|
||||
|
||||
void defaultStatusCodeNoParameters();
|
||||
void defaultStatusCodeByteArray();
|
||||
void defaultStatusCodeJson();
|
||||
void writeStatusCode_data();
|
||||
void writeStatusCode();
|
||||
void writeStatusCodeExtraHeader();
|
||||
void writeJson();
|
||||
void writeJsonExtraHeader();
|
||||
void writeFile_data();
|
||||
void writeFile();
|
||||
void writeFileExtraHeader();
|
||||
void writeByteArrayExtraHeader();
|
||||
};
|
||||
|
||||
#define qWaitForFinished(REPLY) QVERIFY(QSignalSpy(REPLY, &QNetworkReply::finished).wait())
|
||||
|
||||
struct HttpServer : QAbstractHttpServer {
|
||||
std::function<void(QHttpServerResponder responder)> handleRequestFunction;
|
||||
QUrl url { QStringLiteral("http://localhost:%1").arg(listen()) };
|
||||
|
||||
HttpServer(decltype(handleRequestFunction) function) : handleRequestFunction(function) {}
|
||||
bool handleRequest(const QHttpServerRequest &request, QTcpSocket *socket) override;
|
||||
};
|
||||
|
||||
bool HttpServer::handleRequest(const QHttpServerRequest &request, QTcpSocket *socket)
|
||||
{
|
||||
handleRequestFunction(makeResponder(request, socket));
|
||||
return true;
|
||||
}
|
||||
|
||||
void tst_QHttpServerResponder::defaultStatusCodeNoParameters()
|
||||
{
|
||||
HttpServer server([](QHttpServerResponder responder) { responder.write(); });
|
||||
auto reply = networkAccessManager->get(QNetworkRequest(server.url));
|
||||
qWaitForFinished(reply);
|
||||
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
||||
}
|
||||
|
||||
void tst_QHttpServerResponder::defaultStatusCodeByteArray()
|
||||
{
|
||||
HttpServer server([](QHttpServerResponder responder) {
|
||||
responder.write(QByteArray(), QHttpServerLiterals::contentTypeXEmpty());
|
||||
});
|
||||
auto reply = networkAccessManager->get(QNetworkRequest(server.url));
|
||||
qWaitForFinished(reply);
|
||||
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
||||
}
|
||||
|
||||
void tst_QHttpServerResponder::defaultStatusCodeJson()
|
||||
{
|
||||
const auto json = QJsonDocument::fromJson(QByteArrayLiteral("{}"));
|
||||
HttpServer server([json](QHttpServerResponder responder) { responder.write(json); });
|
||||
auto reply = networkAccessManager->get(QNetworkRequest(server.url));
|
||||
qWaitForFinished(reply);
|
||||
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
||||
}
|
||||
|
||||
void tst_QHttpServerResponder::writeStatusCode_data()
|
||||
{
|
||||
using StatusCode = QHttpServerResponder::StatusCode;
|
||||
|
||||
QTest::addColumn<QHttpServerResponder::StatusCode>("statusCode");
|
||||
QTest::addColumn<QNetworkReply::NetworkError>("networkError");
|
||||
|
||||
QTest::addRow("OK") << StatusCode::Ok << QNetworkReply::NoError;
|
||||
QTest::addRow("Content Access Denied") << StatusCode::Forbidden
|
||||
<< QNetworkReply::ContentAccessDenied;
|
||||
QTest::addRow("Connection Refused") << StatusCode::NotFound
|
||||
<< QNetworkReply::ContentNotFoundError;
|
||||
}
|
||||
|
||||
void tst_QHttpServerResponder::writeStatusCode()
|
||||
{
|
||||
QFETCH(QHttpServerResponder::StatusCode, statusCode);
|
||||
QFETCH(QNetworkReply::NetworkError, networkError);
|
||||
HttpServer server([statusCode](QHttpServerResponder responder) {
|
||||
responder.write(statusCode);
|
||||
});
|
||||
auto reply = networkAccessManager->get(QNetworkRequest(server.url));
|
||||
qWaitForFinished(reply);
|
||||
QCOMPARE(reply->bytesAvailable(), 0);
|
||||
QCOMPARE(reply->error(), networkError);
|
||||
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader),
|
||||
QByteArrayLiteral("application/x-empty"));
|
||||
}
|
||||
|
||||
void tst_QHttpServerResponder::writeStatusCodeExtraHeader()
|
||||
{
|
||||
HttpServer server([=](QHttpServerResponder responder) {
|
||||
responder.write({{ headerServerString, headerServerValue }});
|
||||
});
|
||||
auto reply = networkAccessManager->get(QNetworkRequest(server.url));
|
||||
qWaitForFinished(reply);
|
||||
QCOMPARE(reply->bytesAvailable(), 0);
|
||||
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
||||
QCOMPARE(reply->header(QNetworkRequest::ServerHeader), headerServerValue);
|
||||
}
|
||||
|
||||
void tst_QHttpServerResponder::writeJson()
|
||||
{
|
||||
const auto json = QJsonDocument::fromJson(QByteArrayLiteral(R"JSON({ "key" : "value" })JSON"));
|
||||
HttpServer server([json](QHttpServerResponder responder) { responder.write(json); });
|
||||
auto reply = networkAccessManager->get(QNetworkRequest(server.url));
|
||||
qWaitForFinished(reply);
|
||||
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
||||
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader),
|
||||
QHttpServerLiterals::contentTypeJson());
|
||||
QCOMPARE(QJsonDocument::fromJson(reply->readAll()), json);
|
||||
}
|
||||
|
||||
void tst_QHttpServerResponder::writeJsonExtraHeader()
|
||||
{
|
||||
const auto json = QJsonDocument::fromJson(QByteArrayLiteral(R"JSON({ "key" : "value" })JSON"));
|
||||
HttpServer server([json](QHttpServerResponder responder) {
|
||||
responder.write(json, {{ headerServerString, headerServerValue }});
|
||||
});
|
||||
auto reply = networkAccessManager->get(QNetworkRequest(server.url));
|
||||
qWaitForFinished(reply);
|
||||
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
||||
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader),
|
||||
QHttpServerLiterals::contentTypeJson());
|
||||
QCOMPARE(reply->header(QNetworkRequest::ServerHeader), headerServerValue);
|
||||
QCOMPARE(QJsonDocument::fromJson(reply->readAll()), json);
|
||||
}
|
||||
|
||||
void tst_QHttpServerResponder::writeFile_data()
|
||||
{
|
||||
QTest::addColumn<QIODevice *>("iodevice");
|
||||
QTest::addColumn<int>("code");
|
||||
QTest::addColumn<QString>("type");
|
||||
QTest::addColumn<QString>("data");
|
||||
|
||||
QTest::addRow("index.html")
|
||||
<< qobject_cast<QIODevice *>(new QFile(QFINDTESTDATA("data/index.html"), this))
|
||||
<< 200
|
||||
<< "text/html"
|
||||
<< "<html></html>";
|
||||
|
||||
QTest::addRow("index1.html - not found")
|
||||
<< qobject_cast<QIODevice *>(new QFile("./index1.html", this))
|
||||
<< 500
|
||||
<< "application/x-empty"
|
||||
<< QString();
|
||||
|
||||
QTest::addRow("temporary file")
|
||||
<< qobject_cast<QIODevice *>(new QTemporaryFile(this))
|
||||
<< 200
|
||||
<< "text/html"
|
||||
<< QString();
|
||||
}
|
||||
|
||||
void tst_QHttpServerResponder::writeFile()
|
||||
{
|
||||
QFETCH(QIODevice *, iodevice);
|
||||
QFETCH(int, code);
|
||||
QFETCH(QString, type);
|
||||
QFETCH(QString, data);
|
||||
|
||||
QSignalSpy spyDestroyIoDevice(iodevice, &QObject::destroyed);
|
||||
|
||||
HttpServer server([&iodevice](QHttpServerResponder responder) {
|
||||
responder.write(iodevice, QHttpServerLiterals::contentTypeTextHtml());
|
||||
});
|
||||
auto reply = networkAccessManager->get(QNetworkRequest(server.url));
|
||||
QTRY_VERIFY(reply->isFinished());
|
||||
|
||||
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type);
|
||||
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code);
|
||||
QCOMPARE(reply->readAll().trimmed(), data);
|
||||
|
||||
QCOMPARE(spyDestroyIoDevice.count(), 1);
|
||||
}
|
||||
|
||||
void tst_QHttpServerResponder::writeFileExtraHeader()
|
||||
{
|
||||
auto file = new QFile(QFINDTESTDATA("data/index.html"), this);
|
||||
QSignalSpy spyDestroyIoDevice(file, &QObject::destroyed);
|
||||
|
||||
HttpServer server([=](QHttpServerResponder responder) {
|
||||
responder.write(
|
||||
file,
|
||||
{
|
||||
{
|
||||
QHttpServerLiterals::contentTypeHeader(),
|
||||
QHttpServerLiterals::contentTypeTextHtml()
|
||||
},
|
||||
{ headerServerString, headerServerValue }
|
||||
});
|
||||
});
|
||||
auto reply = networkAccessManager->get(QNetworkRequest(server.url));
|
||||
QTRY_VERIFY(reply->isFinished());
|
||||
|
||||
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader),
|
||||
QHttpServerLiterals::contentTypeTextHtml());
|
||||
QCOMPARE(reply->header(QNetworkRequest::ServerHeader), headerServerValue);
|
||||
QCOMPARE(reply->readAll().trimmed(), "<html></html>");
|
||||
|
||||
QCOMPARE(spyDestroyIoDevice.count(), 1);
|
||||
}
|
||||
|
||||
void tst_QHttpServerResponder::writeByteArrayExtraHeader()
|
||||
{
|
||||
const QByteArray data("test data");
|
||||
const QByteArray contentType("text/plain");
|
||||
|
||||
HttpServer server([=](QHttpServerResponder responder) {
|
||||
responder.write(
|
||||
data,
|
||||
{
|
||||
{ "Content-Type", contentType },
|
||||
{ headerServerString, headerServerValue }
|
||||
});
|
||||
});
|
||||
auto reply = networkAccessManager->get(QNetworkRequest(server.url));
|
||||
QTRY_VERIFY(reply->isFinished());
|
||||
|
||||
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), contentType);
|
||||
QCOMPARE(reply->header(QNetworkRequest::ServerHeader), headerServerValue);
|
||||
QCOMPARE(reply->readAll(), data);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QTEST_MAIN(tst_QHttpServerResponder)
|
||||
|
||||
#include "tst_qhttpserverresponder.moc"
|
||||
@@ -0,0 +1,17 @@
|
||||
# Generated from qhttpserverresponse.pro.
|
||||
|
||||
#####################################################################
|
||||
## tst_qhttpserverresponse Test:
|
||||
#####################################################################
|
||||
|
||||
# Collect test data
|
||||
list(APPEND test_data "data/")
|
||||
|
||||
qt_add_test(tst_qhttpserverresponse
|
||||
SOURCES
|
||||
tst_qhttpserverresponse.cpp
|
||||
PUBLIC_LIBRARIES
|
||||
Qt::HttpServer
|
||||
Qt::HttpServerPrivate
|
||||
TESTDATA ${test_data}
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
{ "key": "value" }
|
||||
BIN
src/qthttpserver/tests/auto/qhttpserverresponse/data/image.jpeg
Normal file
BIN
src/qthttpserver/tests/auto/qhttpserverresponse/data/image.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
BIN
src/qthttpserver/tests/auto/qhttpserverresponse/data/image.png
Normal file
BIN
src/qthttpserver/tests/auto/qhttpserverresponse/data/image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="462pt"
|
||||
height="339pt"
|
||||
viewBox="0 0 462 339"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="TheQtCompany_logo_2.svg">
|
||||
<metadata
|
||||
id="metadata20">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs18" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1536"
|
||||
inkscape:window-height="801"
|
||||
id="namedview16"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.1138643"
|
||||
inkscape:cx="270.58047"
|
||||
inkscape:cy="174.65092"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<path
|
||||
fill="#41cd52"
|
||||
d=" M 63.50 0.00 L 462.00 0.00 L 462.00 274.79 C 440.60 296.26 419.13 317.66 397.61 339.00 L 0.00 339.00 L 0.00 63.39 C 21.08 42.18 42.34 21.13 63.50 0.00 Z"
|
||||
id="path6" />
|
||||
<path
|
||||
d=" M 122.37 71.33 C 137.50 61.32 156.21 58.79 174.00 58.95 C 190.94 59.16 208.72 62.13 222.76 72.24 C 232.96 79.41 239.59 90.48 244.01 101.93 C 251.16 120.73 253.26 141.03 253.50 161.01 C 253.53 181.13 252.62 201.69 245.96 220.86 C 241.50 233.90 233.01 245.48 221.81 253.52 C 229.87 266.58 238.09 279.54 246.15 292.60 C 236.02 297.27 225.92 301.97 215.78 306.62 C 207.15 292.38 198.56 278.11 189.90 263.89 C 178.19 265.81 166.21 265.66 154.44 264.36 C 140.34 262.67 125.97 258.37 115.09 248.88 C 106.73 241.64 101.48 231.51 97.89 221.21 C 92.01 203.79 90.43 185.25 90.16 166.97 C 90.02 147.21 91.28 127.14 97.24 108.18 C 101.85 93.92 109.48 79.69 122.37 71.33 Z"
|
||||
id="path8"
|
||||
fill="#ffffff" />
|
||||
<path
|
||||
d=" M 294.13 70.69 C 304.73 70.68 315.33 70.68 325.93 70.69 C 325.96 84.71 325.92 98.72 325.95 112.74 C 339.50 112.76 353.05 112.74 366.60 112.75 C 366.37 121.85 366.12 130.95 365.86 140.05 C 352.32 140.08 338.79 140.04 325.25 140.07 C 325.28 163.05 325.18 186.03 325.30 209.01 C 325.56 215.30 325.42 221.94 328.19 227.75 C 330.21 232.23 335.65 233.38 340.08 233.53 C 348.43 233.50 356.77 233.01 365.12 232.86 C 365.63 241.22 366.12 249.59 366.60 257.95 C 349.99 260.74 332.56 264.08 316.06 258.86 C 309.11 256.80 302.63 252.19 299.81 245.32 C 294.76 233.63 294.35 220.62 294.13 208.07 C 294.11 185.40 294.13 162.74 294.12 140.07 C 286.73 140.05 279.34 140.08 271.95 140.05 C 271.93 130.96 271.93 121.86 271.95 112.76 C 279.34 112.73 286.72 112.77 294.11 112.74 C 294.14 98.72 294.10 84.71 294.13 70.69 Z"
|
||||
id="path10"
|
||||
fill="#ffffff" />
|
||||
<path
|
||||
fill="#41cd52"
|
||||
d=" M 160.51 87.70 C 170.80 86.36 181.60 86.72 191.34 90.61 C 199.23 93.73 205.93 99.84 209.47 107.58 C 214.90 119.31 216.98 132.26 218.03 145.05 C 219.17 162.07 219.01 179.25 216.66 196.17 C 215.01 206.24 212.66 216.85 205.84 224.79 C 198.92 232.76 188.25 236.18 178.01 236.98 C 167.21 237.77 155.82 236.98 146.07 231.87 C 140.38 228.84 135.55 224.09 132.73 218.27 C 129.31 211.30 127.43 203.69 126.11 196.07 C 122.13 171.91 121.17 146.91 126.61 122.89 C 128.85 113.83 132.11 104.53 138.73 97.70 C 144.49 91.85 152.51 88.83 160.51 87.70 Z"
|
||||
id="path12" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1 @@
|
||||
<html></html>
|
||||
@@ -0,0 +1 @@
|
||||
Hello World!
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user