[
  {
    "path": ".Doxyfile",
    "content": "# Doxyfile 1.8.16\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the configuration\n# file that follow. The default is UTF-8 which is also the encoding used for all\n# text before the first occurrence of this tag. Doxygen uses libiconv (or the\n# iconv built into libc) for the transcoding. See\n# https://www.gnu.org/software/libiconv/ for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = \"Drogon\"\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         =\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          =\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           =\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       =\n\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-\n# directories (in 2 levels) under the output directory of each output format and\n# will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,\n# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),\n# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,\n# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),\n# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,\n# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,\n# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,\n# Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all generated output in the proper direction.\n# Possible values are: None, LTR, RTL and Context.\n# The default value is: None.\n\nOUTPUT_TEXT_DIRECTION  = None\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       = \"The $name class\" \\\n                         \"The $name widget\" \\\n                         \"The $name file\" \\\n                         is \\\n                         provides \\\n                         specifies \\\n                         contains \\\n                         represents \\\n                         a \\\n                         an \\\n                         the\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = YES\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line\n# such as\n# /***************\n# as being the beginning of a Javadoc-style comment \"banner\". If set to NO, the\n# Javadoc-style will behave just like regular comments and it will not be\n# interpreted by doxygen.\n# The default value is: NO.\n\nJAVADOC_BANNER         = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:\\n\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". You can put \\n's in the value part of an alias to insert\n# newlines (in the resulting output). You can put ^^ in the value part of an\n# alias to insert a newline as if a physical newline was in the original file.\n# When you need a literal { or } or , in the value part of an alias you have to\n# escape them by means of a backslash (\\), this can lead to conflicts with the\n# commands \\{ and \\} for these it is advised to use the version @{ and @} or use\n# a double escape (\\\\{ and \\\\})\n\nALIASES                =\n\n# This tag can be used to specify a number of word-keyword mappings (TCL only).\n# A mapping has the form \"name=value\". For example adding \"class=itcl::class\"\n# will allow you to use the command class in the itcl::class meaning.\n\nTCL_SUBST              =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice\n# sources only. Doxygen will then generate output that is more tailored for that\n# language. For instance, namespaces will be presented as modules, types will be\n# separated into more groups, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_SLICE  = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by doxygen: IDL, Java, Javascript,\n# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,\n# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:\n# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser\n# tries to guess whether the code is fixed or free formatted code, this is the\n# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat\n# .inc files as Fortran files (default is PHP), and .f files as C (default is\n# Fortran), use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by doxygen.\n\nEXTENSION_MAPPING      =\n\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See https://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by doxygen, so you can\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up\n# to that level are automatically included in the table of contents, even if\n# they do not have an id attribute.\n# Note: This feature currently applies only to Markdown headings.\n# Minimum value: 0, maximum value: 99, default value: 5.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nTOC_INCLUDE_HEADINGS   = 5\n\n# When enabled doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = NO\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen\n# will parse them like normal C++ but will assume all classes use public instead\n# of private inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# If one adds a struct or class to a group and this option is enabled, then also\n# any nested class or struct is added to the same group. By default this option\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\n# The default value is: NO.\n\nGROUP_NESTED_COMPOUNDS = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = NO\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = NO\n\n# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual\n# methods of a class will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIV_VIRTUAL   = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = NO\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\n# (class|struct|union) declarations. If set to NO, these declarations will be\n# included in the documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file\n# names in lower-case letters. If set to YES, upper-case letters are also\n# allowed. This is useful if you have classes or files whose names only differ\n# in case and if your file system supports case sensitive file names. Windows\n# (including Cygwin) ands Mac users are advised to set this option to NO.\n# The default value is: system dependent.\n\nCASE_SENSE_NAMES       = NO\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents doxygen's defaults, run doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file.\n#\n# Note that if you run doxygen from a directory containing a file called\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = YES\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as not documenting some parameters\n# in a documented function, or documenting parameters that don't exist or using\n# markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, doxygen will only warn about wrong or incomplete\n# parameter documentation, but not about the absence of documentation. If\n# EXTRACT_ALL is set to YES then this flag will automatically be disabled.\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when\n# a warning is encountered.\n# The default value is: NO.\n\nWARN_AS_ERROR          = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = lib/inc \\\n                         orm_lib/inc\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see: https://www.gnu.org/software/libiconv/) for the list of\n# possible encodings.\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# read by doxygen.\n#\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,\n# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,\n# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,\n# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,\n# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.\n\nFILE_PATTERNS          = *.c \\\n                         *.cc \\\n                         *.cxx \\\n                         *.cpp \\\n                         *.c++ \\\n                         *.java \\\n                         *.ii \\\n                         *.ixx \\\n                         *.ipp \\\n                         *.i++ \\\n                         *.inl \\\n                         *.idl \\\n                         *.ddl \\\n                         *.odl \\\n                         *.h \\\n                         *.hh \\\n                         *.hxx \\\n                         *.hpp \\\n                         *.h++ \\\n                         *.cs \\\n                         *.d \\\n                         *.php \\\n                         *.php4 \\\n                         *.php5 \\\n                         *.phtml \\\n                         *.inc \\\n                         *.m \\\n                         *.markdown \\\n                         *.md \\\n                         *.mm \\\n                         *.dox \\\n                         *.py \\\n                         *.pyw \\\n                         *.f90 \\\n                         *.f95 \\\n                         *.f03 \\\n                         *.f08 \\\n                         *.f \\\n                         *.for \\\n                         *.tcl \\\n                         *.vhd \\\n                         *.vhdl \\\n                         *.ucf \\\n                         *.qsf \\\n                         *.ice\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which doxygen is\n# run.\n\nEXCLUDE                =\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# AClass::ANamespace, ANamespace::*Test\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories use the pattern */test/*\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       = *\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             =\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE =\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = NO\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# classes and enums directly into the documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# entity all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see https://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in\n# which the alphabetical index list will be split.\n# Minimum value: 1, maximum value: 20, default value: 5.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nCOLS_IN_ALPHA_INDEX    = 5\n\n# In case all classes in a project start with a common prefix, all classes will\n# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag\n# can be used to specify a prefix (or a list of prefixes) that should be ignored\n# while generating the index headers.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list). For an example see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a colorwheel, see\n# https://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use grayscales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML\n# page will contain the date and time when the page was generated. Setting this\n# to YES can help to show when doxygen was last run and thus if the\n# documentation is up to date.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_TIMESTAMP         = NO\n\n# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML\n# documentation will contain a main index with vertical navigation menus that\n# are dynamically created via Javascript. If disabled, the navigation index will\n# consists of multiple levels of tabs that are statically embedded in every HTML\n# page. Disable this option to support browsers that do not have Javascript,\n# like the Qt help browser.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_MENUS     = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see: https://developer.apple.com/xcode/), introduced with OSX\n# 10.5 (Leopard). To create a documentation set, doxygen will generate a\n# Makefile in the HTML output directory. Running make will produce the docset in\n# that directory and running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy\n# genXcode/_index.html for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on\n# Windows.\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the master .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-\n# folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-\n# filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-\n# filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location of Qt's\n# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the\n# generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine-tune the look of the index. As an example, the default style\n# sheet generated by doxygen has an example that shows how to put an image at\n# the root of the tree instead of the PROJECT_NAME. Since the tree basically has\n# the same information as the tab index, you could consider setting\n# DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# Use the FORMULA_TRANSPARENT tag to determine whether or not the images\n# generated for formulas are transparent PNGs. Transparent PNGs are not\n# supported properly for IE 6.0, but are supported on all modern browsers.\n#\n# Note that when changing this option you need to delete any form_*.png files in\n# the HTML output directory before the changes have effect.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_TRANSPARENT    = YES\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# https://www.mathjax.org) which uses client side Javascript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = NO\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. See the MathJax site (see:\n# http://docs.mathjax.org/en/latest/output.html) for more details.\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility), NativeMML (i.e. MathML) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from https://www.mathjax.org before deployment.\n# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\n# the HTML output. The underlying search engine uses javascript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the javascript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using Javascript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: https://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: https://xapian.org/). See the section \"External Indexing and\n# Searching\" for details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = YES\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when not enabling USE_PDFLATEX the default is latex when enabling\n# USE_PDFLATEX the default is pdflatex and when in the later case latex is\n# chosen this is overwritten by pdflatex. For specific output languages the\n# default can have been set differently, this depends on the implementation of\n# the output language.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         =\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# Note: This tag is used in the Makefile / make.bat.\n# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file\n# (.tex).\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to\n# generate index for LaTeX. In case there is no backslash (\\) as first character\n# it will be automatically added in the LaTeX code.\n# Note: This tag is used in the generated output file (.tex).\n# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.\n# The default value is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_MAKEINDEX_CMD    = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. The package can be specified just\n# by its name or with the correct syntax as to be used with the LaTeX\n# \\usepackage command. To get the times font for instance you can specify :\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\n# To use the option intlimits with the amsmath package you can specify:\n# EXTRA_PACKAGES=[intlimits]{amsmath}\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the\n# generated LaTeX document. The header should contain everything until the first\n# chapter. If it is left blank doxygen will generate a standard header. See\n# section \"Doxygen usage\" for information on how to let doxygen write the\n# default header to a separate file.\n#\n# Note: Only use a user-defined header if you know what you are doing! The\n# following commands have a special meaning inside the header: $title,\n# $datetime, $date, $doxygenversion, $projectname, $projectnumber,\n# $projectbrief, $projectlogo. Doxygen will replace $title with the empty\n# string, for the replacement values of the other commands the user is referred\n# to HTML_HEADER.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the\n# generated LaTeX document. The footer should contain everything after the last\n# chapter. If it is left blank doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer.\n#\n# Note: Only use a user-defined footer if you know what you are doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate\n# the PDF file directly from the LaTeX files. Set this option to YES, to get a\n# higher quality PDF documentation.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode\n# command to the generated LaTeX files. This will instruct LaTeX to keep running\n# if errors occur, instead of asking the user for help. This option is also used\n# when generating formulas in HTML.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source\n# code with syntax highlighting in the LaTeX output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_SOURCE_CODE      = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# https://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated\n# page will contain the date and time when the page was generated. Setting this\n# to NO can help when comparing the output of multiple runs.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_TIMESTAMP        = NO\n\n# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)\n# path from which the emoji images will be read. If a relative path is entered,\n# it will be relative to the LATEX_OUTPUT directory. If left blank the\n# LATEX_OUTPUT directory will be used.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EMOJI_DIRECTORY  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's\n# configuration file, i.e. a series of assignments. You only have to provide\n# replacements, missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to doxygen's configuration file. A template extensions file can be\n# generated using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code\n# with syntax highlighting in the RTF output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_SOURCE_CODE        = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = NO\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include\n# namespace members in file scope as well, matching the HTML output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_NS_MEMB_FILE_SCOPE = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the\n# program listings (including syntax highlighting and cross-referencing\n# information) to the DOCBOOK output. Note that enabling this will significantly\n# increase the size of the DOCBOOK output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_PROGRAMLISTING = NO\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\n# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures\n# the structure of the code including all documentation. Note that this feature\n# is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = NO\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = NO\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             =\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      =\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES, all external class will be listed in\n# the class index. If set to NO, only the inherited external classes will be\n# listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the modules index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\n\n# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram\n# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to\n# NO turns the diagrams off. Note that this option also works with HAVE_DOT\n# disabled, but it is recommended to install and use dot, since it yields more\n# powerful graphs.\n# The default value is: YES.\n\nCLASS_DIAGRAMS         = YES\n\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = NO\n\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\n# to run in parallel. When set to 0 doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# When you want a differently looking font in the dot files that doxygen\n# generates you can specify the font name using DOT_FONTNAME. You need to make\n# sure dot is able to find the font, which can be done by putting it in a\n# standard location or by setting the DOTFONTPATH environment variable or by\n# setting DOT_FONTPATH to the directory containing the font.\n# The default value is: Helvetica.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTNAME           = Helvetica\n\n# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of\n# dot graphs.\n# Minimum value: 4, maximum value: 24, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTSIZE           = 10\n\n# By default doxygen will tell dot to use the default font as specified with\n# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set\n# the path where dot can find it using this tag.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for\n# each documented class showing the direct and indirect inheritance relations.\n# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\n# groups, showing the direct groups dependencies.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command. Disabling a call graph can be\n# accomplished by means of the command \\hidecallgraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command. Disabling a caller graph can be\n# accomplished by means of the command \\hidecallergraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. For an explanation of the image formats see the section\n# output formats in the documentation of the dot tool (Graphviz (see:\n# http://www.graphviz.org/)).\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and\n# png:gdiplus:gdiplus.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = png\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file. If left blank, it is assumed\n# PlantUML is not used or called during a preprocessing step. Doxygen will\n# generate a warning when it encounters a \\startuml command in this case and\n# will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a\n# configuration file for plantuml.\n\nPLANTUML_CFG_FILE      =\n\n# When using plantuml, the specified paths are searched for files specified by\n# the !include statement in a plantuml block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that doxygen if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent\n# background. This is disabled by default, because dot on Windows does not seem\n# to support this out of the box.\n#\n# Warning: Depending on the platform used, enabling this option may lead to\n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to\n# read).\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_TRANSPARENT        = NO\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot\n# files that are used to generate the various graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": ".clang-format",
    "content": "---\nLanguage:        Cpp\n# BasedOnStyle:  Google\nAccessModifierOffset: -2\nAlignAfterOpenBracket: Align\nAlignConsecutiveAssignments: false\nAlignConsecutiveDeclarations: false\nAlignEscapedNewlines: Left\nAlignOperands:   true\nAlignTrailingComments: true\nAllowAllParametersOfDeclarationOnNextLine: false\nAllowShortBlocksOnASingleLine: false\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: None\nAllowShortIfStatementsOnASingleLine: false\nAllowShortLoopsOnASingleLine: false\nAlwaysBreakAfterDefinitionReturnType: None\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: true\nAlwaysBreakTemplateDeclarations: true\nBinPackArguments: false\nBinPackParameters: false \nBraceWrapping:   \n  AfterClass:      true\n  AfterControlStatement: Always\n  AfterEnum:       true\n  AfterFunction:   true\n  AfterNamespace:  true\n  AfterObjCDeclaration: false\n  AfterStruct:     true\n  AfterUnion:      true\n  AfterExternBlock: true\n  BeforeCatch:     true\n  BeforeElse:      true\n  IndentBraces:    false\n  SplitEmptyFunction: true\n  SplitEmptyRecord: true\n  SplitEmptyNamespace: true\n  AfterCaseLabel: true\nBreakBeforeBinaryOperators: None\nBreakBeforeBraces: Custom\nBreakBeforeInheritanceComma: false\nBreakInheritanceList: BeforeColon\nBreakBeforeTernaryOperators: true\nBreakConstructorInitializersBeforeComma: false\nBreakConstructorInitializers: BeforeColon\nBreakAfterJavaFieldAnnotations: false\nBreakStringLiterals: true\nColumnLimit:     80\nCommentPragmas:  '^ IWYU pragma:'\nCompactNamespaces: false\nConstructorInitializerAllOnOneLineOrOnePerLine: true\nConstructorInitializerIndentWidth: 4\nContinuationIndentWidth: 4\nCpp11BracedListStyle: true\nDerivePointerAlignment: false \nDisableFormat:   false\nFixNamespaceComments: true\nForEachMacros:   \n  - foreach\n  - Q_FOREACH\n  - BOOST_FOREACH\nIncludeBlocks:   Preserve\nIncludeCategories: \n  - Regex:           '^<ext/.*\\.h>'\n    Priority:        2\n  - Regex:           '^<.*\\.h>'\n    Priority:        1\n  - Regex:           '^<.*'\n    Priority:        2\n  - Regex:           '.*'\n    Priority:        3\nIncludeIsMainRegex: '([-_](test|unittest))?$'\nIndentCaseLabels: true\nIndentPPDirectives: None\nIndentWidth:    4 \nIndentWrappedFunctionNames: false\nInsertNewlineAtEOF: true\nJavaScriptQuotes: Leave\nJavaScriptWrapImports: true\nKeepEmptyLinesAtTheStartOfBlocks: false\nMacroBlockBegin: ''\nMacroBlockEnd:   ''\nMaxEmptyLinesToKeep: 1\nNamespaceIndentation: None\nObjCBinPackProtocolList: Never\nObjCBlockIndentWidth: 2\nObjCSpaceAfterProperty: false\nObjCSpaceBeforeProtocolList: true\nPenaltyBreakAssignment: 2\nPenaltyBreakBeforeFirstCallParameter: 100\nPenaltyBreakComment: 300\nPenaltyBreakFirstLessLess: 120\nPenaltyBreakString: 1000\nPenaltyBreakTemplateDeclaration: 10\nPenaltyExcessCharacter: 1000000\nPenaltyReturnTypeOnItsOwnLine: 2000\nPointerAlignment: Right \nReferenceAlignment: Right \nRawStringFormats: \n  - Language:        Cpp\n    Delimiters:      \n      - cc\n      - CC\n      - cpp\n      - Cpp\n      - CPP\n      - 'c++'\n      - 'C++'\n    CanonicalDelimiter: ''\n    BasedOnStyle:    google\n  - Language:        TextProto\n    Delimiters:      \n      - pb\n      - PB\n      - proto\n      - PROTO\n    EnclosingFunctions: \n      - EqualsProto\n      - EquivToProto\n      - PARSE_PARTIAL_TEXT_PROTO\n      - PARSE_TEST_PROTO\n      - PARSE_TEXT_PROTO\n      - ParseTextOrDie\n      - ParseTextProtoOrDie\n    CanonicalDelimiter: ''\n    BasedOnStyle:    google\nReflowComments:  true\nSeparateDefinitionBlocks: Always\nSortIncludes:    Never\nSortUsingDeclarations: true\nSpaceAfterCStyleCast: false\nSpaceAfterTemplateKeyword: true\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeCpp11BracedList: false\nSpaceBeforeCtorInitializerColon: true\nSpaceBeforeInheritanceColon: true\nSpaceBeforeParens: ControlStatements\nSpaceBeforeRangeBasedForLoopColon: true\nSpaceInEmptyParentheses: false\nSpacesBeforeTrailingComments: 2\nSpacesInAngles:  false\nSpacesInContainerLiterals: true\nSpacesInCStyleCastParentheses: false\nSpacesInParentheses: false\nSpacesInSquareBrackets: false\nStandard:        Auto\nStatementMacros: \n  - Q_UNUSED\n  - QT_REQUIRE_VERSION\nTabWidth:        8\nUseTab:          Never\n...\n\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: drogonframework \npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: # ['https://paypal.me/antao2019']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Notice**\nIf you need support or clarification regarding the usage of Drogon in your project, visit the official Drogon support channel at [gitter](https://gitter.im/drogon-web/community)\n\nPlease create a new issue only if you think you have found a bug or if have a feature request/enhancement.\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Desktop (please complete the following information):**\n - OS: [e.g. iOS]\n - Browser [e.g. chrome, safari]\n - Version [e.g. 22]\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser [e.g. stock browser, safari]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Notice**\nIf you need support or clarification regarding the usage of Drogon in your project, visit the official Drogon support channel at [gitter](https://gitter.im/drogon-web/community)\n\nPlease create a new issue only if you think you have found a bug or if have a feature request/enhancement.\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/workflows/cmake.yml",
    "content": "name: Build & Test\n\non:\n  push:\n    branches: [master]\n  pull_request:\n  workflow_dispatch:\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.event_name == 'pull_request' }}\n\nenv:\n  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)\n  BUILD_TYPE: Release\n\njobs:\n  windows:\n    name: windows/msvc - ${{ matrix.link }}\n    runs-on: windows-2022\n    strategy:\n      fail-fast: false\n      matrix:\n        link: [\"STATIC\", \"SHARED\"]\n    steps:\n      - name: Checkout Drogon source code\n        uses: actions/checkout@v4\n        with:\n          submodules: true\n          fetch-depth: 0\n\n      - name: Install dependencies\n        run: pip install conan\n\n      - name: Create build directory\n        run: mkdir build\n\n      - name: Install conan packages\n        working-directory: ./build\n        run: |\n          conan profile detect\n          conan install .. -s compiler=\"msvc\" -sbuild_type=Debug --build=missing -s compiler.cppstd=17\n\n      - name: Create Build Environment & Configure Cmake\n        shell: bash\n        working-directory: ./build\n        run: |\n          [[ ${{ matrix.link }} == \"SHARED\" ]] && shared=\"ON\" || shared=\"OFF\"\n          cmake ..                                         \\\n            -DCMAKE_BUILD_TYPE=Debug                       \\\n            -DBUILD_TESTING=on                             \\\n            -DBUILD_SHARED_LIBS=$shared                    \\\n            -DCMAKE_TOOLCHAIN_FILE=\"conan_toolchain.cmake\" \\\n            -DBUILD_CTL=ON                                 \\\n            -DBUILD_EXAMPLES=ON                            \\\n            -DUSE_SPDLOG=ON                                \\\n            -DCMAKE_INSTALL_PREFIX=../install              \\\n            -DCMAKE_POLICY_DEFAULT_CMP0091=NEW             \\\n            -DCMAKE_CXX_STANDARD=17\n\n      - name: Build\n        run: cmake --build build --target install --parallel\n\n      - name: Test\n        shell: bash\n        run: ./test.sh -w\n\n  macos:\n    runs-on: macos-${{ matrix.osver }}\n    strategy:\n      fail-fast: false\n      matrix:\n        osver: [14, 15]\n    steps:\n      - name: Checkout Drogon source code\n        uses: actions/checkout@v4\n        with:\n          submodules: true\n          fetch-depth: 0\n\n      - name: Install dependencies\n        # Already installed: brotli, zlib, lz4, sqlite3\n        run: brew install ninja jsoncpp mariadb hiredis redis spdlog postgresql@14\n\n      - name: Create Build Environment & Configure Cmake\n        # Some projects don't allow in-source building, so create a separate build directory\n        # We'll use this as our working directory for all subsequent commands\n        run: |\n          cmake -B build -G Ninja          \\\n            -DCMAKE_BUILD_TYPE=$BUILD_TYPE \\\n            -DBUILD_TESTING=on             \\\n            -DUSE_SPDLOG=ON                \\\n            -DBUILD_SHARED_LIBS=OFF\n\n      - name: Build\n        working-directory: ./build\n        # Execute the build.  You can specify a specific target with \"--target <NAME>\"\n        run: ninja && sudo ninja install\n\n      - name: Prepare for testing\n        run: |\n          brew services restart postgresql@14\n          brew services start mariadb\n          brew services start redis\n          sleep 4\n          mariadb -e \"SET PASSWORD FOR 'root'@'localhost' = PASSWORD('')\"\n          mariadb -e \"GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost'\"\n          mariadb -e \"FLUSH PRIVILEGES\"\n          brew services restart mariadb\n          sleep 4\n          psql -c 'create user postgres superuser;' postgres\n\n      - name: Test\n        # Execute tests defined by the CMake configuration.\n        # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail\n        run: ./test.sh -t\n\n  ubuntu:\n    runs-on: ubuntu-22.04\n    strategy:\n      fail-fast: false\n      matrix:\n        link: [SHARED, STATIC]\n        compiler:\n          - cxx: g++\n            ver: 9\n          - cxx: g++\n            ver: 10\n          - cxx: g++\n            ver: 11\n          - cxx: g++\n            ver: 12\n          - cxx: g++\n            ver: 13\n          - cxx: clang++\n            ver: 11\n          - cxx: clang++\n            ver: 12\n          - cxx: clang++\n            ver: 13\n          - cxx: clang++\n            ver: 14\n          - cxx: clang++\n            ver: 15\n          - cxx: clang++\n            ver: 16\n          - cxx: clang++\n            ver: 17\n        include:\n          - link: STATIC\n            compiler:\n              cxx: g++\n              ver: 13\n              feature: coroutines\n    env:\n      CXX: ${{ matrix.compiler.cxx }}-${{ matrix.compiler.ver }}\n    steps:\n      - name: Checkout Drogon source code\n        uses: actions/checkout@v4\n        with:\n          submodules: true\n          fetch-depth: 0\n\n      - name: Install dependencies\n        run: |\n          # Installing packages might fail as the github image becomes outdated\n          sudo apt update\n          # These aren't available or don't work well in vcpkg\n          sudo apt-get install -y libjsoncpp-dev uuid-dev libssl-dev zlib1g-dev libsqlite3-dev\n          sudo apt-get install -y ninja-build libbrotli-dev\n          sudo apt-get install -y libspdlog-dev\n\n      - name: Install postgresql\n        run: |\n          sudo apt-get --purge remove postgresql postgresql-doc postgresql-common postgresql-client-common\n          sudo apt-get -y install postgresql-all\n\n      - name: Install g++\n        if: startsWith(matrix.compiler.cxx, 'g++') && (matrix.compiler.ver == 13 || matrix.compiler.ver == 9)\n        run: |\n          sudo add-apt-repository ppa:ubuntu-toolchain-r/test\n          sudo apt-get install g++-${{ matrix.compiler.ver }}\n          sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-${{ matrix.compiler.ver }} ${{ matrix.compiler.ver }}\n\n      - name: Install Clang\n        if: startsWith(matrix.compiler.cxx, 'clang') && matrix.compiler.ver < 13\n        run: sudo apt-get install clang-${{ matrix.compiler.ver }}\n\n      - name: Install Clang\n        if: startsWith(matrix.compiler.cxx, 'clang') && matrix.compiler.ver >= 13\n        run: |\n          wget https://apt.llvm.org/llvm.sh\n          chmod +x ./llvm.sh\n          sudo ./llvm.sh ${{ matrix.compiler.ver }}\n\n      - name: Export `shared`\n        run: |\n          [[ ${{ matrix.link }} == \"SHARED\" ]] && shared=\"ON\" || shared=\"OFF\"\n          echo \"shared=$shared\" >> $GITHUB_ENV\n\n      - name: Create Build Environment & Configure Cmake\n        # Some projects don't allow in-source building, so create a separate build directory\n        # We'll use this as our working directory for all subsequent commands\n        if: matrix.compiler.feature != 'coroutines'\n        run: |\n          cmake -B build -G Ninja          \\\n            -DCMAKE_BUILD_TYPE=$BUILD_TYPE \\\n            -DBUILD_TESTING=on             \\\n            -DUSE_SPDLOG=ON                \\\n            -DBUILD_SHARED_LIBS=$shared\n      - name: Create Build Environment & Configure Cmake (coroutines)\n        # Some projects don't allow in-source building, so create a separate build directory\n        # We'll use this as our working directory for all subsequent commands\n        if: matrix.compiler.feature == 'coroutines'\n        run: |\n          cmake -B build -G Ninja            \\\n            -DCMAKE_BUILD_TYPE=$BUILD_TYPE   \\\n            -DBUILD_TESTING=on               \\\n            -DUSE_SPDLOG=ON                  \\\n            -DCMAKE_CXX_FLAGS=\"-fcoroutines\" \\\n            -DBUILD_SHARED_LIBS=$shared      \\\n\n      - name: Build\n        working-directory: ./build\n        # Execute the build.  You can specify a specific target with \"--target <NAME>\"\n        run: ninja && sudo ninja install\n\n      - name: Prepare for testing\n        run: |\n          sudo systemctl start postgresql\n          sleep 1\n          sudo -u postgres psql -c \"ALTER USER postgres WITH PASSWORD '12345'\" postgres\n\n      - name: Test\n        # Execute tests defined by the CMake configuration.\n        # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail\n        run: ./test.sh -t\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: \"CodeQL\"\n\non:\n  push:\n    branches: [ 'master' ]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [ 'master' ]\n  schedule:\n    - cron: '46 7 * * 5'\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.event_name == 'pull_request' }}\n\nenv:\n  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)\n  BUILD_TYPE: Release\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'cpp' ]\n        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]\n        # Use only 'java' to analyze code written in Java, Kotlin or both\n        # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both\n        # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support\n\n    env:\n      SHARED: ON\n\n    steps:\n    - name: Checkout Drogon source code\n      uses: actions/checkout@v4\n      with:\n        submodules: true\n        fetch-depth: 0\n\n    - name: Install dependencies\n      run: |\n        sudo apt update\n        sudo apt-get install -y libjsoncpp-dev uuid-dev libssl-dev zlib1g-dev libsqlite3-dev\n        sudo apt-get install -y ninja-build libbrotli-dev\n\n    - name: Create Build Environment & Configure Cmake\n      run: |\n        cmake -B build -G Ninja          \\\n          -DCMAKE_BUILD_TYPE=$BUILD_TYPE \\\n          -DBUILD_TESTING=on             \\\n          -DBUILD_SHARED_LIBS=$SHARED\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v4\n      with:\n        languages: ${{ matrix.language }}\n        # If you wish to specify custom queries, you can do so here or in a config file.\n        # By default, queries listed here will override any specified in a config file.\n        # Prefix the list here with \"+\" to use these queries and those in the config file.\n\n        # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs\n        # queries: security-extended,security-and-quality\n\n    - name: Build\n      working-directory: ./build\n      run: ninja && sudo ninja install\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v4\n      with:\n        category: \"/language:${{matrix.language}}\"\n"
  },
  {
    "path": ".github/workflows/codespell.yml",
    "content": "# Look for typos in the codebase using codespell.\n# https://github.com/codespell-project/codespell#readme\nname: codespell\non:\n  push:\n    branches: [master]\n  pull_request:\n    branches: [master]\njobs:\n  codespell:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v4\n    - run: sudo apt-get install -y codespell\n    - run: codespell --ignore-words-list=\"coo,folx,ot,statics,xwindows,NotIn,aNULL,\" --skip=\"*.csp\"\n"
  },
  {
    "path": ".github/workflows/cpp.yml",
    "content": "name: C++\n\non:\n  push:\n    branches: [master]\n  pull_request:\n\npermissions:\n  contents: read\n\njobs:\n  format:\n    runs-on: ubuntu-22.04\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Install dos2unix\n        run: sudo apt-get install -y dos2unix\n\n      - name: Install clang-format-17\n        run: |\n          wget https://apt.llvm.org/llvm.sh\n          chmod +x ./llvm.sh\n          sudo ./llvm.sh 17\n          sudo apt-get install -y clang-format-17\n\n      - name: Check formatting\n        run: ./format.sh && git diff --exit-code\n        env:\n          CLANG_FORMAT: clang-format-17\n\n  cpplint:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Install cpplint\n        run: pip install cpplint\n\n      - name: Run lint\n        run: cpplint --recursive .\n"
  },
  {
    "path": ".github/workflows/docker-publish.yml",
    "content": "name: Build and Push Docker Image\n\non:\n  release:\n    types: [created]  # 当新版本被创建时触发\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Check out code\n        uses: actions/checkout@v4\n\n      - name: Log in to Docker Hub\n        uses: docker/login-action@v3\n        with:\n          username: ${{ secrets.DOCKER_USERNAME }}\n          password: ${{ secrets.DOCKER_PASSWORD }}\n\n      - name: Build Docker image\n        run: |\n          cd docker/ubuntu\n          docker build -t drogonframework/drogon:latest .\n\n      - name: Push Docker image\n        run: |\n          docker push drogonframework/drogon:latest\n"
  },
  {
    "path": ".gitignore",
    "content": "# Prerequisites\n*.d\n\n# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n*.dll\n\n# Fortran module files\n*.mod\n*.smod\n\n# Compiled Static libraries\n*.lai\n*.la\n*.a\n*.lib\n\n# Executables\n*.exe\n*.out\n*.app\n\nbuild/\ncmake-build-debug/\ncmake-build-debug-visual-studio/\n.idea/\nhtml/\nlatex/\n.vscode\n*.kdev4\n.cproject\n.project\n.settings/\n.vs/\nCMakeSettings.json\ninstall\ntrace.json\n.cache/\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"trantor\"]\n\tpath = trantor\n\turl = https://github.com/an-tao/trantor.git\n\tbranch = master\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.5...3.31)\n\nproject(drogon)\n\nmessage(STATUS \"compiler: \" ${CMAKE_CXX_COMPILER_ID})\n\noption(BUILD_CTL \"Build drogon_ctl\" ON)\noption(BUILD_EXAMPLES \"Build examples\" ON)\noption(BUILD_ORM \"Build orm\" ON)\noption(COZ_PROFILING \"Use coz for profiling\" OFF)\noption(BUILD_SHARED_LIBS \"Build drogon as a shared lib\" OFF)\noption(BUILD_DOC \"Build Doxygen documentation\" OFF)\noption(BUILD_BROTLI \"Build Brotli\" ON)\noption(BUILD_YAML_CONFIG \"Build yaml config\" ON)\noption(USE_SUBMODULE \"Use trantor as a submodule\" ON)\noption(USE_STATIC_LIBS_ONLY \"Use only static libraries as dependencies\" OFF)\n\ninclude(CMakeDependentOption)\nCMAKE_DEPENDENT_OPTION(BUILD_POSTGRESQL \"Build with postgresql support\" ON \"BUILD_ORM\" OFF)\nCMAKE_DEPENDENT_OPTION(LIBPQ_BATCH_MODE \"Use batch mode for libpq\" ON \"BUILD_POSTGRESQL\" OFF)\nCMAKE_DEPENDENT_OPTION(BUILD_MYSQL \"Build with mysql support\" ON \"BUILD_ORM\" OFF)\nCMAKE_DEPENDENT_OPTION(BUILD_SQLITE \"Build with sqlite3 support\" ON \"BUILD_ORM\" OFF)\nCMAKE_DEPENDENT_OPTION(BUILD_REDIS \"Build with redis support\" ON \"BUILD_ORM\" OFF)\nCMAKE_DEPENDENT_OPTION(USE_SPDLOG \"Allow using the spdlog logging library\" OFF \"USE_SUBMODULE\" OFF)\n\nset(DROGON_MAJOR_VERSION 1)\nset(DROGON_MINOR_VERSION 9)\nset(DROGON_PATCH_VERSION 12)\nset(DROGON_VERSION\n    ${DROGON_MAJOR_VERSION}.${DROGON_MINOR_VERSION}.${DROGON_PATCH_VERSION})\nset(DROGON_VERSION_STRING \"${DROGON_VERSION}\")\n\ninclude(GNUInstallDirs)\n# Offer the user the choice of overriding the installation directories\nset(INSTALL_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE PATH \"Installation directory for libraries\")\nset(INSTALL_BIN_DIR ${CMAKE_INSTALL_BINDIR} CACHE PATH \"Installation directory for executables\")\nset(INSTALL_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE PATH \"Installation directory for header files\")\nset(DEF_INSTALL_DROGON_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/Drogon)\nset(INSTALL_DROGON_CMAKE_DIR ${DEF_INSTALL_DROGON_CMAKE_DIR}\n    CACHE PATH \"Installation directory for cmake files\")\n\nif (CMAKE_CXX_COMPILER_ID MATCHES \"MSVC\")\n  # Force MSVC to use UTF-8 because that's what we use. Otherwise it uses\n  # the default of whatever Windows sets and causes encoding issues.\n  message(STATUS \"You are using MSVC. Forcing to use UTF-8\")\n  add_compile_options(\"$<$<C_COMPILER_ID:MSVC>:/utf-8>\")\n  add_compile_options(\"$<$<CXX_COMPILER_ID:MSVC>:/utf-8>\")\n  if (MSVC_VERSION GREATER_EQUAL 1914)\n    # Tells Visual Studio 2017 (15.7+) and newer to correctly set the value of the standard __cplusplus macro,\n    # instead of leaving it to 199711L and settings the effective c++ version in _MSVC_LANG\n    # Dropping support for older versions of VS would allow to only rely on __cplusplus\n    add_compile_options(\"/Zc:__cplusplus\")\n  endif()\nendif ()\n\nadd_library(${PROJECT_NAME})\nif (BUILD_SHARED_LIBS)\n    find_package(Threads)\n    # set(BUILD_EXAMPLES FALSE)\n    list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES\n        \"${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}\" isSystemDir)\n    if (\"${isSystemDir}\" STREQUAL \"-1\")\n        set(CMAKE_INSTALL_RPATH \"${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}\")\n    endif (\"${isSystemDir}\" STREQUAL \"-1\")\n    set_target_properties(${PROJECT_NAME} PROPERTIES\n        VERSION ${DROGON_VERSION}\n        SOVERSION ${DROGON_MAJOR_VERSION})\n    target_link_libraries(${PROJECT_NAME} PUBLIC Threads::Threads)\n    if (WIN32)\n        target_link_libraries(${PROJECT_NAME} PUBLIC rpcrt4 crypt32 advapi32 ws2_32)\n        if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)\n            # Ignore MSVC C4251 and C4275 warning of exporting std objects with no dll export\n            # We export class to facilitate maintenance, thus if you compile\n            # drogon on windows as a shared library, you will need to use\n            # exact same compiler for drogon and your app.\n            target_compile_options(${PROJECT_NAME} PUBLIC /wd4251 /wd4275)\n        endif ()\n    endif ()\nendif (BUILD_SHARED_LIBS)\n\nif(USE_STATIC_LIBS_ONLY)\n    set(CMAKE_FIND_LIBRARY_SUFFIXES \"${CMAKE_STATIC_LIBRARY_SUFFIX}\")\nendif(USE_STATIC_LIBS_ONLY)\n\nif(USE_SPDLOG)\n  find_package(spdlog CONFIG)\n  if(spdlog_FOUND)\n    message(STATUS \"spdlog found!\")\n    target_link_libraries(${PROJECT_NAME} PUBLIC spdlog::spdlog_header_only)\n    target_compile_definitions(${PROJECT_NAME} PUBLIC DROGON_SPDLOG_SUPPORT SPDLOG_FMT_EXTERNAL FMT_HEADER_ONLY)\n  endif(spdlog_FOUND)\nendif(USE_SPDLOG)\n\nif (NOT ${CMAKE_PLATFORM_NAME} STREQUAL \"Windows\" AND CMAKE_CXX_COMPILER_ID MATCHES GNU)\n    target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Werror)\nendif ()\n\ninclude(GenerateExportHeader)\ngenerate_export_header(${PROJECT_NAME} EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/exports/drogon/exports.h)\n\ninclude(cmake/DrogonUtilities.cmake)\ninclude(cmake/ParseAndAddDrogonTests.cmake)\ninclude(CheckIncludeFileCXX)\n\ncheck_include_file_cxx(any HAS_ANY)\ncheck_include_file_cxx(string_view HAS_STRING_VIEW)\ncheck_include_file_cxx(coroutine HAS_COROUTINE)\nif (NOT \"${CMAKE_CXX_STANDARD}\" STREQUAL \"\")\n    set(DROGON_CXX_STANDARD ${CMAKE_CXX_STANDARD})\nelseif (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)\n    set(DROGON_CXX_STANDARD 20)\nelse ()\n    set(DROGON_CXX_STANDARD 17)\nendif ()\nif(USE_SUBMODULE)\n    target_include_directories(\n            ${PROJECT_NAME}\n            PUBLIC\n            $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/trantor>)\nendif()\ntarget_include_directories(\n    ${PROJECT_NAME}\n    PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib/inc>\n    $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/lib/inc>\n    $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/orm_lib/inc>\n    $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/nosql_lib/redis/inc>\n    $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>\n    $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/exports>\n    $<INSTALL_INTERFACE:${INSTALL_INCLUDE_DIR}>)\n\nif (WIN32)\n    target_include_directories(\n        ${PROJECT_NAME}\n        PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/third_party/mman-win32>)\nendif (WIN32)\n\nadd_library(Drogon::Drogon ALIAS ${PROJECT_NAME})\n\nif(USE_SUBMODULE)\nadd_subdirectory(trantor)\ntarget_link_libraries(${PROJECT_NAME} PUBLIC trantor)\nelse()\nfind_package(Trantor CONFIG REQUIRED)\ntarget_link_libraries(${PROJECT_NAME} PUBLIC Trantor::Trantor)\nendif()\n\nif(${CMAKE_SYSTEM_NAME} STREQUAL \"Haiku\")\n    target_link_libraries(${PROJECT_NAME} PRIVATE network)\nelseif (NOT WIN32 AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL \"OpenBSD\")\n    target_link_libraries(${PROJECT_NAME} PRIVATE dl)\nelseif (WIN32)\n    target_link_libraries(${PROJECT_NAME} PRIVATE shlwapi ws2_32 iphlpapi)\nendif ()\n\nlist(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules/)\n\nfind_package(Filesystem COMPONENTS Final)\nif(CXX_FILESYSTEM_HAVE_FS)\n    message(STATUS \"Found std::filesystem\")\nelse ()\n    message(FATAL_ERROR \"Not found std::filesystem, please use a newer compiler\")\nendif()\n\nif (DROGON_CXX_STANDARD EQUAL 17)\n    message(STATUS \"use c++17\")\n    # Check for partial implementation of c++17 (Windows/OSX only?)\n    set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)\n    try_compile(check_filesystem_path ${CMAKE_BINARY_DIR}/cmaketest\n        ${PROJECT_SOURCE_DIR}/cmake/tests/check_has_std_filesystem_path.cc\n        CXX_STANDARD 17)\n    set(CMAKE_TRY_COMPILE_TARGET_TYPE)\n    if (NOT check_filesystem_path)\n      message(FATAL_ERROR \"The std::filesystem seems to be a partial implementation,\"\n        \" please use a newer compiler\")\n    endif()\nelse ()\n    message(STATUS \"use c++20\")\nendif ()\n\n\noption(HAS_STD_FILESYSTEM_PATH \"use std::filesystem\" ON)\n# HACK: Needed to be compiled on Yocto Linux\nif(TARGET std::filesystem)\n  get_property(CAN_LINK_FS TARGET std::filesystem PROPERTY INTERFACE_LINK_LIBRARIES SET)\n  if ( CAN_LINK_FS )\n    target_link_libraries(${PROJECT_NAME} PUBLIC std::filesystem)\n  endif()\nendif()\n\n\n# jsoncpp\nfind_package(Jsoncpp REQUIRED)\ntarget_link_libraries(${PROJECT_NAME} PUBLIC Jsoncpp_lib)\nlist(APPEND INCLUDE_DIRS_FOR_DYNAMIC_VIEW ${JSONCPP_INCLUDE_DIRS})\n\n# yamlcpp\nif(BUILD_YAML_CONFIG)\n    find_package(yaml-cpp QUIET)\n    if(yaml-cpp_FOUND)\n        if (NOT YAML_CPP_LIBRARIES)\n            find_library(YAML_CPP_LINK_LIBRARY \"yaml-cpp\")\n            if(NOT YAML_CPP_LINK_LIBRARY)\n                message(STATUS \"yaml-cpp not used\")\n            else()\n                message(STATUS \"yaml-cpp found \")\n                target_link_libraries(${PROJECT_NAME} PUBLIC ${YAML_CPP_LINK_LIBRARY})\n                target_compile_definitions(${PROJECT_NAME} PUBLIC HAS_YAML_CPP)\n            endif()\n        else()\n            message(STATUS \"yaml-cpp found \")\n            target_link_libraries(${PROJECT_NAME} PUBLIC ${YAML_CPP_LIBRARIES})\n            target_compile_definitions(${PROJECT_NAME} PUBLIC HAS_YAML_CPP)\n        endif()\n    else()\n        message(STATUS \"yaml-cpp not used\")\n    endif()\nelse()\n    message(STATUS \"yaml-cpp not used\")\nendif(BUILD_YAML_CONFIG)\n\nif (NOT ${CMAKE_SYSTEM_NAME} STREQUAL \"FreeBSD\"\n    AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL \"OpenBSD\"\n    AND NOT WIN32)\n    find_package(UUID REQUIRED)\n    target_link_libraries(${PROJECT_NAME} PRIVATE UUID_lib)\n\n    try_compile(normal_uuid ${CMAKE_BINARY_DIR}/cmaketest\n        ${PROJECT_SOURCE_DIR}/cmake/tests/normal_uuid_lib_test.cc\n        LINK_LIBRARIES UUID_lib\n        OUTPUT_VARIABLE NORMAL_UUID_COMPILE_OUTPUT)\n    try_compile(ossp_uuid ${CMAKE_BINARY_DIR}/cmaketest\n        ${PROJECT_SOURCE_DIR}/cmake/tests/ossp_uuid_lib_test.cc\n        LINK_LIBRARIES UUID_lib\n        OUTPUT_VARIABLE OSSP_UUID_COMPILE_OUTPUT)\n    if (normal_uuid)\n        add_definitions(-DUSE_OSSP_UUID=0)\n    elseif (ossp_uuid)\n        add_definitions(-DUSE_OSSP_UUID=1)\n    else ()\n        message(FATAL_ERROR \"uuid lib error:\\n${NORMAL_UUID_COMPILE_OUTPUT}\\n${OSSP_UUID_COMPILE_OUTPUT}\")\n    endif ()\nendif (NOT ${CMAKE_SYSTEM_NAME} STREQUAL \"FreeBSD\"\n    AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL \"OpenBSD\"\n    AND NOT WIN32)\n\nif (BUILD_BROTLI)\n    find_package(Brotli)\n    if (Brotli_FOUND)\n        message(STATUS \"Brotli found\")\n        add_definitions(-DUSE_BROTLI)\n        target_link_libraries(${PROJECT_NAME} PRIVATE Brotli_lib)\n    endif (Brotli_FOUND)\nendif (BUILD_BROTLI)\n\nset(DROGON_SOURCES\n    lib/src/AOPAdvice.cc\n    lib/src/AccessLogger.cc\n    lib/src/CacheFile.cc\n    lib/src/ConfigAdapterManager.cc\n    lib/src/ConfigLoader.cc\n    lib/src/Cookie.cc\n    lib/src/DrClassMap.cc\n    lib/src/DrTemplateBase.cc\n    lib/src/MiddlewaresFunction.cc\n    lib/src/FixedWindowRateLimiter.cc\n    lib/src/GlobalFilters.cc\n    lib/src/Histogram.cc\n    lib/src/Hodor.cc\n    lib/src/HttpAppFrameworkImpl.cc\n    lib/src/HttpBinder.cc\n    lib/src/HttpClientImpl.cc\n    lib/src/HttpConnectionLimit.cc\n    lib/src/HttpControllerBinder.cc\n    lib/src/HttpControllersRouter.cc\n    lib/src/HttpFileImpl.cc\n    lib/src/HttpFileUploadRequest.cc\n    lib/src/HttpRequestImpl.cc\n    lib/src/HttpRequestParser.cc\n    lib/src/RequestStream.cc\n    lib/src/HttpResponseImpl.cc\n    lib/src/HttpResponseParser.cc\n    lib/src/HttpServer.cc\n    lib/src/HttpUtils.cc\n    lib/src/HttpViewData.cc\n    lib/src/IntranetIpFilter.cc\n    lib/src/JsonConfigAdapter.cc\n    lib/src/ListenerManager.cc\n    lib/src/LocalHostFilter.cc\n    lib/src/MultiPart.cc\n    lib/src/MultipartStreamParser.cc\n    lib/src/NotFound.cc\n    lib/src/PluginsManager.cc\n    lib/src/PromExporter.cc\n    lib/src/RangeParser.cc\n    lib/src/RateLimiter.cc\n    lib/src/RealIpResolver.cc\n    lib/src/SecureSSLRedirector.cc\n    lib/src/Redirector.cc\n    lib/src/SessionManager.cc\n    lib/src/SlashRemover.cc\n    lib/src/SlidingWindowRateLimiter.cc\n    lib/src/StaticFileRouter.cc\n    lib/src/TaskTimeoutFlag.cc\n    lib/src/TokenBucketRateLimiter.cc\n    lib/src/Utilities.cc\n    lib/src/WebSocketClientImpl.cc\n    lib/src/WebSocketConnectionImpl.cc\n    lib/src/YamlConfigAdapter.cc\n    lib/src/drogon_test.cc)\nset(private_headers\n    lib/src/AOPAdvice.h\n    lib/src/CacheFile.h\n    lib/src/ConfigLoader.h\n    lib/src/ControllerBinderBase.h\n    lib/src/MiddlewaresFunction.h\n    lib/src/HttpAppFrameworkImpl.h\n    lib/src/HttpClientImpl.h\n    lib/src/HttpConnectionLimit.h\n    lib/src/HttpControllerBinder.h\n    lib/src/HttpControllersRouter.h\n    lib/src/HttpFileImpl.h\n    lib/src/HttpFileUploadRequest.h\n    lib/src/HttpMessageBody.h\n    lib/src/HttpRequestImpl.h\n    lib/src/HttpRequestParser.h\n    lib/src/HttpResponseImpl.h\n    lib/src/HttpResponseParser.h\n    lib/src/HttpServer.h\n    lib/src/HttpUtils.h\n    lib/src/impl_forwards.h\n    lib/src/ListenerManager.h\n    lib/src/PluginsManager.h\n    lib/src/SessionManager.h\n    lib/src/SpinLock.h\n    lib/src/StaticFileRouter.h\n    lib/src/TaskTimeoutFlag.h\n    lib/src/WebSocketClientImpl.h\n    lib/src/WebSocketConnectionImpl.h\n    lib/src/FixedWindowRateLimiter.h\n    lib/src/SlidingWindowRateLimiter.h\n    lib/src/TokenBucketRateLimiter.h\n    lib/src/ConfigAdapterManager.h\n    lib/src/JsonConfigAdapter.h\n    lib/src/YamlConfigAdapter.h\n    lib/src/ConfigAdapter.h\n    lib/src/MultipartStreamParser.h)\n\nif (NOT WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL \"iOS\")\n    set(DROGON_SOURCES\n        ${DROGON_SOURCES}\n        lib/src/SharedLibManager.cc)\n    set(private_headers\n        ${private_headers}\n        lib/src/SharedLibManager.h)\nelseif(WIN32)\n    set(DROGON_SOURCES\n        ${DROGON_SOURCES}\n        third_party/mman-win32/mman.c)\n    set(private_headers\n        ${private_headers}\n        third_party/mman-win32/mman.h)\nendif()\n\nif (BUILD_POSTGRESQL)\n    # find postgres\n    find_package(pg)\n    if (pg_FOUND)\n        message(STATUS \"libpq inc path:\" ${PG_INCLUDE_DIRS})\n        message(STATUS \"libpq lib:\" ${PG_LIBRARIES})\n        target_link_libraries(${PROJECT_NAME} PRIVATE pg_lib)\n        set(DROGON_SOURCES\n            ${DROGON_SOURCES}\n            orm_lib/src/postgresql_impl/PostgreSQLResultImpl.cc\n            orm_lib/src/postgresql_impl/PgListener.cc)\n        set(private_headers\n            ${private_headers}\n            orm_lib/src/postgresql_impl/PostgreSQLResultImpl.h\n            orm_lib/src/postgresql_impl/PgListener.h)\n        if (LIBPQ_BATCH_MODE)\n            try_compile(libpq_supports_batch ${CMAKE_BINARY_DIR}/cmaketest\n                ${PROJECT_SOURCE_DIR}/cmake/tests/test_libpq_batch_mode.cc\n                LINK_LIBRARIES ${PostgreSQL_LIBRARIES}\n                CMAKE_FLAGS \"-DINCLUDE_DIRECTORIES=${PostgreSQL_INCLUDE_DIR}\")\n        endif (LIBPQ_BATCH_MODE)\n        if (libpq_supports_batch)\n            message(STATUS \"The libpq supports batch mode\")\n            option(LIBPQ_SUPPORTS_BATCH_MODE \"libpq batch mode\" ON)\n            set(DROGON_SOURCES\n                ${DROGON_SOURCES}\n                orm_lib/src/postgresql_impl/PgBatchConnection.cc)\n        else (libpq_supports_batch)\n            option(LIBPQ_SUPPORTS_BATCH_MODE \"libpq batch mode\" OFF)\n            set(DROGON_SOURCES\n                ${DROGON_SOURCES}\n                orm_lib/src/postgresql_impl/PgConnection.cc)\n            set(private_headers\n                ${private_headers}\n                orm_lib/src/postgresql_impl/PgConnection.h)\n        endif (libpq_supports_batch)\n    endif (pg_FOUND)\nendif (BUILD_POSTGRESQL)\n\nif (BUILD_MYSQL)\n    # Find mysql, only mariadb client library is supported\n    find_package(MySQL QUIET)\n    find_package(unofficial-libmariadb QUIET)\n    if (MySQL_FOUND)\n        target_link_libraries(${PROJECT_NAME} PRIVATE MySQL_lib)\n        set(DROGON_FOUND_MYSQL TRUE)\n        set(MYSQL_LIB_NAME MySQL_lib)\n    elseif (unofficial-libmariadb_FOUND)\n        target_link_libraries(${PROJECT_NAME} PRIVATE unofficial::libmariadb)\n        set(DROGON_FOUND_MYSQL TRUE)\n        set(MYSQL_LIB_NAME unofficial::libmariadb)\n    endif ()\n\n    if (DROGON_FOUND_MYSQL)\n        message(STATUS \"Ok! We find mariadb!\")\n        include(CheckLibraryExists)\n        check_library_exists(${MYSQL_LIB_NAME} mysql_optionsv \"\" HAS_MYSQL_OPTIONSV)\n        if (HAS_MYSQL_OPTIONSV)\n            message(STATUS \"Mariadb support mysql_optionsv\")\n            add_definitions(-DHAS_MYSQL_OPTIONSV)\n        endif(HAS_MYSQL_OPTIONSV)\n\n        set(DROGON_SOURCES\n            ${DROGON_SOURCES}\n            orm_lib/src/mysql_impl/MysqlConnection.cc\n            orm_lib/src/mysql_impl/MysqlResultImpl.cc)\n        set(private_headers\n            ${private_headers}\n            orm_lib/src/mysql_impl/MysqlConnection.h\n            orm_lib/src/mysql_impl/MysqlResultImpl.h)\n    else (DROGON_FOUND_MYSQL)\n        message(STATUS \"MySql was not found.\")\n    endif (DROGON_FOUND_MYSQL)\nendif (BUILD_MYSQL)\n\nif (BUILD_SQLITE)\n    # Find sqlite3.\n    find_package(SQLite3 QUIET)\n    find_package(unofficial-sqlite3 QUIET)\n    if (SQLite3_FOUND)\n        target_link_libraries(${PROJECT_NAME} PRIVATE SQLite3_lib)\n        set(DROGON_FOUND_SQLite3 TRUE)\n    elseif (unofficial-sqlite3_FOUND)\n        target_link_libraries(${PROJECT_NAME} PRIVATE unofficial::sqlite3::sqlite3)\n        set(DROGON_FOUND_SQLite3 TRUE)\n    endif ()\n\n    if (DROGON_FOUND_SQLite3)\n        message(STATUS \"Ok! We find sqlite3!\")\n        set(DROGON_SOURCES\n            ${DROGON_SOURCES}\n            orm_lib/src/sqlite3_impl/Sqlite3Connection.cc\n            orm_lib/src/sqlite3_impl/Sqlite3ResultImpl.cc)\n        set(private_headers\n            ${private_headers}\n            orm_lib/src/sqlite3_impl/Sqlite3Connection.h\n            orm_lib/src/sqlite3_impl/Sqlite3ResultImpl.h)\n    else (DROGON_FOUND_SQLite3)\n        message(STATUS \"sqlite3 was not found.\")\n    endif (DROGON_FOUND_SQLite3)\nendif (BUILD_SQLITE)\n\nif (BUILD_REDIS)\n    find_package(Hiredis)\n    if (Hiredis_FOUND)\n        add_definitions(-DUSE_REDIS)\n        target_link_libraries(${PROJECT_NAME} PRIVATE Hiredis_lib)\n        set(DROGON_SOURCES\n            ${DROGON_SOURCES}\n            nosql_lib/redis/src/RedisClientImpl.cc\n            nosql_lib/redis/src/RedisClientLockFree.cc\n            nosql_lib/redis/src/RedisClientManager.cc\n            nosql_lib/redis/src/RedisConnection.cc\n            nosql_lib/redis/src/RedisResult.cc\n            nosql_lib/redis/src/RedisTransactionImpl.cc\n            nosql_lib/redis/src/SubscribeContext.cc\n            nosql_lib/redis/src/RedisSubscriberImpl.cc)\n        set(private_headers\n            ${private_headers}\n            nosql_lib/redis/src/RedisClientImpl.h\n            nosql_lib/redis/src/RedisClientLockFree.h\n            nosql_lib/redis/src/RedisConnection.h\n            nosql_lib/redis/src/RedisTransactionImpl.h\n            nosql_lib/redis/src/SubscribeContext.h\n            nosql_lib/redis/src/RedisSubscriberImpl.h)\n\n    endif (Hiredis_FOUND)\nendif (BUILD_REDIS)\n\nif (NOT Hiredis_FOUND)\n    set(DROGON_SOURCES\n        ${DROGON_SOURCES}\n        lib/src/RedisClientSkipped.cc\n        lib/src/RedisResultSkipped.cc\n        lib/src/RedisClientManagerSkipped.cc)\n    set(private_headers\n        ${private_headers}\n        lib/src/RedisClientManager.h)\nendif (NOT Hiredis_FOUND)\n\nif (BUILD_TESTING)\n    add_subdirectory(nosql_lib/redis/tests)\nendif (BUILD_TESTING)\n\nfind_package(ZLIB REQUIRED)\ntarget_link_libraries(${PROJECT_NAME} PRIVATE ZLIB::ZLIB)\n\nexecute_process(COMMAND \"git\" rev-parse HEAD\n    WORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\"\n    OUTPUT_VARIABLE GIT_SHA1\n    ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)\nconfigure_file(\"${PROJECT_SOURCE_DIR}/cmake/templates/version.h.in\"\n    \"${CMAKE_CURRENT_BINARY_DIR}/lib/inc/drogon/version.h\" @ONLY)\n\nif (DROGON_CXX_STANDARD EQUAL 20)\n    option(USE_COROUTINE \"Enable C++20 coroutine support\" ON)\nelse (DROGON_CXX_STANDARD EQUAL 20)\n    option(USE_COROUTINE \"Enable C++20 coroutine support\" OFF)\nendif (DROGON_CXX_STANDARD EQUAL 20)\n\nif (BUILD_EXAMPLES)\n    add_subdirectory(examples)\nendif (BUILD_EXAMPLES)\n\nif (BUILD_CTL)\n    if (CMAKE_CROSSCOMPILING)\n        find_program(HOST_DROGON_CTL _drogon_ctl)\n        if (NOT HOST_DROGON_CTL)\n            message(FATAL_ERROR \"_drogon_ctl not found on host system\")\n        endif()\n        set(DROGON_CTL \"${HOST_DROGON_CTL}\")\n    else (CMAKE_CROSSCOMPILING)\n        set(DROGON_CTL \"$<TARGET_FILE:_drogon_ctl>\")\n    endif (CMAKE_CROSSCOMPILING)\n    add_subdirectory(drogon_ctl)\nendif (BUILD_CTL)\n\nif (COZ_PROFILING)\n    find_package(coz-profiler REQUIRED)\n    target_compile_definitions(${PROJECT_NAME} PRIVATE -DCOZ_PROFILING=1)\n    # If linked will not need to be ran with `coz run --- [executable]` to run the\n    # profiler, but drogon_ctl currently won't build because it doesn't find debug\n    # information while trying to generate it's own sources\n    # target_link_libraries(${PROJECT_NAME} PUBLIC coz::coz)\n    target_include_directories(${PROJECT_NAME} PUBLIC ${COZ_INCLUDE_DIRS})\nendif (COZ_PROFILING)\n\nset(DROGON_SOURCES\n    ${DROGON_SOURCES}\n    orm_lib/src/ArrayParser.cc\n    orm_lib/src/Criteria.cc\n    orm_lib/src/DbClient.cc\n    orm_lib/src/DbClientImpl.cc\n    orm_lib/src/DbClientLockFree.cc\n    orm_lib/src/DbConnection.cc\n    orm_lib/src/DbListener.cc\n    orm_lib/src/Exception.cc\n    orm_lib/src/Field.cc\n    orm_lib/src/Result.cc\n    orm_lib/src/Row.cc\n    orm_lib/src/SqlBinder.cc\n    orm_lib/src/TransactionImpl.cc\n    orm_lib/src/RestfulController.cc)\nset(DROGON_HEADERS\n    lib/inc/drogon/Attribute.h\n    lib/inc/drogon/CacheMap.h\n    lib/inc/drogon/Cookie.h\n    lib/inc/drogon/DrClassMap.h\n    lib/inc/drogon/DrObject.h\n    lib/inc/drogon/DrTemplate.h\n    lib/inc/drogon/DrTemplateBase.h\n    lib/inc/drogon/HttpAppFramework.h\n    lib/inc/drogon/HttpBinder.h\n    lib/inc/drogon/HttpClient.h\n    lib/inc/drogon/HttpController.h\n    lib/inc/drogon/HttpFilter.h\n    lib/inc/drogon/HttpMiddleware.h\n    lib/inc/drogon/HttpRequest.h\n    lib/inc/drogon/RequestStream.h\n    lib/inc/drogon/HttpResponse.h\n    lib/inc/drogon/HttpSimpleController.h\n    lib/inc/drogon/HttpTypes.h\n    lib/inc/drogon/HttpViewData.h\n    lib/inc/drogon/IntranetIpFilter.h\n    lib/inc/drogon/IOThreadStorage.h\n    lib/inc/drogon/LocalHostFilter.h\n    lib/inc/drogon/MultiPart.h\n    lib/inc/drogon/NotFound.h\n    lib/inc/drogon/Session.h\n    lib/inc/drogon/UploadFile.h\n    lib/inc/drogon/WebSocketClient.h\n    lib/inc/drogon/WebSocketConnection.h\n    lib/inc/drogon/WebSocketController.h\n    lib/inc/drogon/drogon.h\n    ${CMAKE_CURRENT_BINARY_DIR}/lib/inc/drogon/version.h\n    lib/inc/drogon/drogon_callbacks.h\n    lib/inc/drogon/PubSubService.h\n    lib/inc/drogon/drogon_test.h\n    lib/inc/drogon/RateLimiter.h\n    ${CMAKE_CURRENT_BINARY_DIR}/exports/drogon/exports.h)\nset(private_headers\n    ${private_headers}\n    lib/src/DbClientManager.h\n    orm_lib/src/DbClientImpl.h\n    orm_lib/src/DbConnection.h\n    orm_lib/src/ResultImpl.h\n    orm_lib/src/TransactionImpl.h)\nif (pg_FOUND OR DROGON_FOUND_MYSQL OR DROGON_FOUND_SQLite3)\n    set(DROGON_SOURCES\n        ${DROGON_SOURCES}\n        orm_lib/src/DbClientManager.cc)\nelse (pg_FOUND OR DROGON_FOUND_MYSQL OR DROGON_FOUND_SQLite3)\n    set(DROGON_SOURCES\n        ${DROGON_SOURCES}\n        lib/src/DbClientManagerSkipped.cc)\nendif (pg_FOUND OR DROGON_FOUND_MYSQL OR DROGON_FOUND_SQLite3)\n\nset_target_properties(${PROJECT_NAME}\n    PROPERTIES CXX_STANDARD ${DROGON_CXX_STANDARD})\nset_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD_REQUIRED ON)\nset_target_properties(${PROJECT_NAME} PROPERTIES CXX_EXTENSIONS OFF)\nset_target_properties(${PROJECT_NAME} PROPERTIES EXPORT_NAME Drogon)\n\nif (pg_FOUND OR DROGON_FOUND_MYSQL OR DROGON_FOUND_SQLite3)\n    if (pg_FOUND)\n        option(USE_POSTGRESQL \"Enable PostgreSQL\" ON)\n    else (pg_FOUND)\n        option(USE_POSTGRESQL \"Disable PostgreSQL\" OFF)\n    endif (pg_FOUND)\n\n    if (DROGON_FOUND_MYSQL)\n        option(USE_MYSQL \"Enable Mysql\" ON)\n    else (DROGON_FOUND_MYSQL)\n        option(USE_MYSQL \"Disable Mysql\" OFF)\n    endif (DROGON_FOUND_MYSQL)\n\n    if (DROGON_FOUND_SQLite3)\n        option(USE_SQLITE3 \"Enable Sqlite3\" ON)\n    else (DROGON_FOUND_SQLite3)\n        option(USE_SQLITE3 \"Disable Sqlite3\" OFF)\n    endif (DROGON_FOUND_SQLite3)\nendif (pg_FOUND OR DROGON_FOUND_MYSQL OR DROGON_FOUND_SQLite3)\n\nget_filename_component(COMPILER_COMMAND ${CMAKE_CXX_COMPILER} NAME)\nset(COMPILER_ID ${CMAKE_CXX_COMPILER_ID})\n\nif (CMAKE_BUILD_TYPE)\n    string(TOLOWER ${CMAKE_BUILD_TYPE} _type)\n    if (_type STREQUAL release)\n        set(COMPILATION_FLAGS \"${CMAKE_CXX_FLAGS_RELEASE} -std=c++\")\n    elseif (_type STREQUAL debug)\n        set(COMPILATION_FLAGS \"${CMAKE_CXX_FLAGS_DEBUG} -std=c++\")\n    else ()\n        set(COMPILATION_FLAGS \"-std=c++\")\n    endif ()\nelse (CMAKE_BUILD_TYPE)\n    set(COMPILATION_FLAGS \"-std=c++\")\nendif (CMAKE_BUILD_TYPE)\n\nlist(APPEND INCLUDE_DIRS_FOR_DYNAMIC_VIEW\n    \"${CMAKE_INSTALL_PREFIX}/${INSTALL_INCLUDE_DIR}\")\nlist(REMOVE_DUPLICATES INCLUDE_DIRS_FOR_DYNAMIC_VIEW)\nset(INS_STRING \"\")\nforeach (loop_var ${INCLUDE_DIRS_FOR_DYNAMIC_VIEW})\n    set(INS_STRING \"${INS_STRING} -I${loop_var}\")\nendforeach (loop_var)\n\nset(INCLUDING_DIRS ${INS_STRING})\n\nconfigure_file(${PROJECT_SOURCE_DIR}/cmake/templates/config.h.in\n    ${PROJECT_BINARY_DIR}/drogon/config.h @ONLY)\n\nif (BUILD_TESTING)\n    message(STATUS \"Building tests\")\n    enable_testing()\n    add_subdirectory(lib/tests)\n    if (pg_FOUND)\n        add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/src/postgresql_impl/test)\n    endif (pg_FOUND)\n    if (DROGON_FOUND_MYSQL)\n        add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/src/mysql_impl/test)\n    endif (DROGON_FOUND_MYSQL)\n    if (DROGON_FOUND_SQLite3)\n        add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/src/sqlite3_impl/test)\n    endif (DROGON_FOUND_SQLite3)\n    add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/tests)\nendif (BUILD_TESTING)\n\n# Installation\n\ninstall(TARGETS ${PROJECT_NAME}\n    EXPORT DrogonTargets\n    RUNTIME DESTINATION \"${INSTALL_BIN_DIR}\" COMPONENT bin\n    ARCHIVE DESTINATION \"${INSTALL_LIB_DIR}\" COMPONENT lib\n    LIBRARY DESTINATION \"${INSTALL_LIB_DIR}\" COMPONENT lib)\n\ninstall(FILES ${DROGON_HEADERS} DESTINATION ${INSTALL_INCLUDE_DIR}/drogon)\n\nset(ORM_HEADERS\n    orm_lib/inc/drogon/orm/ArrayParser.h\n    orm_lib/inc/drogon/orm/BaseBuilder.h\n    orm_lib/inc/drogon/orm/Criteria.h\n    orm_lib/inc/drogon/orm/DbClient.h\n    orm_lib/inc/drogon/orm/DbConfig.h\n    orm_lib/inc/drogon/orm/DbListener.h\n    orm_lib/inc/drogon/orm/DbTypes.h\n    orm_lib/inc/drogon/orm/Exception.h\n    orm_lib/inc/drogon/orm/Field.h\n    orm_lib/inc/drogon/orm/FunctionTraits.h\n    orm_lib/inc/drogon/orm/Mapper.h\n    orm_lib/inc/drogon/orm/CoroMapper.h\n    orm_lib/inc/drogon/orm/Result.h\n    orm_lib/inc/drogon/orm/ResultIterator.h\n    orm_lib/inc/drogon/orm/Row.h\n    orm_lib/inc/drogon/orm/RowIterator.h\n    orm_lib/inc/drogon/orm/SqlBinder.h\n    orm_lib/inc/drogon/orm/RestfulController.h)\ninstall(FILES ${ORM_HEADERS} DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/orm)\n\nset(NOSQL_HEADERS\n    nosql_lib/redis/inc/drogon/nosql/RedisClient.h\n    nosql_lib/redis/inc/drogon/nosql/RedisResult.h\n    nosql_lib/redis/inc/drogon/nosql/RedisSubscriber.h\n    nosql_lib/redis/inc/drogon/nosql/RedisException.h)\ninstall(FILES ${NOSQL_HEADERS} DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/nosql)\n\nset(DROGON_UTIL_HEADERS\n    lib/inc/drogon/utils/coroutine.h\n    lib/inc/drogon/utils/FunctionTraits.h\n    lib/inc/drogon/utils/HttpConstraint.h\n    lib/inc/drogon/utils/OStringStream.h\n    lib/inc/drogon/utils/Utilities.h\n    lib/inc/drogon/utils/monitoring.h)\ninstall(FILES ${DROGON_UTIL_HEADERS}\n    DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/utils)\n\nset(DROGON_MONITORING_HEADERS\n    lib/inc/drogon/utils/monitoring/Counter.h\n    lib/inc/drogon/utils/monitoring/Metric.h\n    lib/inc/drogon/utils/monitoring/Registry.h\n    lib/inc/drogon/utils/monitoring/Collector.h\n    lib/inc/drogon/utils/monitoring/Sample.h\n    lib/inc/drogon/utils/monitoring/Gauge.h\n    lib/inc/drogon/utils/monitoring/Histogram.h)\n\ninstall(FILES ${DROGON_MONITORING_HEADERS}\n    DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/utils/monitoring)\n\nset(DROGON_PLUGIN_HEADERS\n    lib/inc/drogon/plugins/Plugin.h\n    lib/inc/drogon/plugins/Redirector.h\n    lib/inc/drogon/plugins/SecureSSLRedirector.h\n    lib/inc/drogon/plugins/AccessLogger.h\n    lib/inc/drogon/plugins/RealIpResolver.h\n    lib/inc/drogon/plugins/Hodor.h\n    lib/inc/drogon/plugins/SlashRemover.h\n    lib/inc/drogon/plugins/GlobalFilters.h\n    lib/inc/drogon/plugins/PromExporter.h)\n\ninstall(FILES ${DROGON_PLUGIN_HEADERS}\n    DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/plugins)\n\ntarget_sources(${PROJECT_NAME} PRIVATE\n               ${DROGON_SOURCES}\n               ${private_headers}\n               ${DROGON_HEADERS}\n               ${ORM_HEADERS}\n               ${DROGON_UTIL_HEADERS}\n               ${DROGON_PLUGIN_HEADERS}\n               ${NOSQL_HEADERS}\n               ${DROGON_MONITORING_HEADERS})\n\nsource_group(\"Public API\"\n    FILES\n    ${DROGON_HEADERS}\n    ${ORM_HEADERS}\n    ${DROGON_UTIL_HEADERS}\n    ${DROGON_PLUGIN_HEADERS}\n    ${NOSQL_HEADERS}\n    ${DROGON_MONITORING_HEADERS})\nsource_group(\"Private Headers\"\n             FILES\n             ${private_headers})\n\n# Export the package for use from the build-tree (this registers the build-tree\n# with a global cmake-registry) export(PACKAGE Drogon)\n\ninclude(CMakePackageConfigHelpers)\n# ... for the install tree\nconfigure_package_config_file(\n    cmake/templates/DrogonConfig.cmake.in\n    ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/DrogonConfig.cmake\n    INSTALL_DESTINATION\n    ${INSTALL_DROGON_CMAKE_DIR})\n\n# version\nwrite_basic_package_version_file(\n    ${CMAKE_CURRENT_BINARY_DIR}/DrogonConfigVersion.cmake\n    VERSION ${DROGON_VERSION}\n    COMPATIBILITY SameMajorVersion)\n\n# Install the DrogonConfig.cmake and DrogonConfigVersion.cmake\ninstall(FILES\n    \"${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/DrogonConfig.cmake\"\n    \"${CMAKE_CURRENT_BINARY_DIR}/DrogonConfigVersion.cmake\"\n    \"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/FindUUID.cmake\"\n    \"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/FindJsoncpp.cmake\"\n    \"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/FindSQLite3.cmake\"\n    \"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/FindMySQL.cmake\"\n    \"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/Findpg.cmake\"\n    \"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/FindBrotli.cmake\"\n    \"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/Findcoz-profiler.cmake\"\n    \"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/FindHiredis.cmake\"\n    \"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/FindFilesystem.cmake\"\n    \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/DrogonUtilities.cmake\"\n    \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/ParseAndAddDrogonTests.cmake\"\n    DESTINATION \"${INSTALL_DROGON_CMAKE_DIR}\"\n    COMPONENT dev)\n\n# Install the export set for use with the install-tree\ninstall(EXPORT DrogonTargets\n    DESTINATION \"${INSTALL_DROGON_CMAKE_DIR}\"\n    NAMESPACE Drogon::\n    COMPONENT dev)\n\n# Doxygen documentation\nfind_package(Doxygen OPTIONAL_COMPONENTS dot dia)\nif(DOXYGEN_FOUND)\n  set(DOXYGEN_PROJECT_BRIEF \"C++14/17-based HTTP application framework\")\n  set(DOXYGEN_OUTPUT_DIRECTORY docs/${PROJECT_NAME})\n  set(DOXYGEN_GENERATE_LATEX NO)\n  set(DOXYGEN_BUILTIN_STL_SUPPORT YES)\n  set(DOXYGEN_USE_MDFILE_AS_MAINPAGE README.md)\n  set(DOXYGEN_STRIP_FROM_INC_PATH ${PROJECT_SOURCE_DIR}/lib/inc\n                                  ${PROJECT_SOURCE_DIR}/orm_lib/inc\n                                  ${CMAKE_CURRENT_BINARY_DIR}/exports)\n  set(DOXYGEN_EXAMPLE_PATTERNS *)\n  if(WIN32)\n    set(DOXYGEN_PREDEFINED _WIN32)\n  endif(WIN32)\n  doxygen_add_docs(doc_${PROJECT_NAME}\n                   README.md\n                   README.zh-CN.md\n                   README.zh-TW.md\n                   ChangeLog.md\n                   CONTRIBUTING.md\n                   ${DROGON_HEADERS}\n                   ${DROGON_UTIL_HEADERS}\n                   ${DROGON_PLUGIN_HEADERS}\n                   ${ORM_HEADERS}\n                   COMMENT \"Generate documentation\")\n  if(NOT TARGET doc)\n    add_custom_target(doc)\n  endif()\n  add_dependencies(doc doc_${PROJECT_NAME})\n  if (BUILD_DOC)\n    add_dependencies(${PROJECT_NAME} doc_${PROJECT_NAME})\n    # Don't install twice, so limit to Debug (assume developer)\n    install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/docs/${PROJECT_NAME}\n            TYPE DOC\n            CONFIGURATIONS Debug)\n  endif(BUILD_DOC)\nendif(DOXYGEN_FOUND)\n\ninclude(cmake/Packages.cmake)\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n**Drogon** is an open source project at its heart and every contribution is\nwelcome. By participating in this project you agree to stick to common sense and\ncontribute in an overall positive way.\n\n## Getting Started\n\n1. Fork, then clone the repository: `git clone\n   git@github.com:your-username/drogon.git`\n1. Follow the [official installation steps on\n   Github](https://drogonframework.github.io/drogon-docs/#/ENG-02-Installation). It’s best to\n   make sure to have the `drogon_ctl` executable in your shell’s `PATH`\n   environment variable in case you use a terminal.\n\nNow you can create branches, start adding features & bugfixes to the code, and\n[create pull requests](https://github.com/an-tao/drogon/compare).\n\n## Pull Requests\n\nFeel free to [create a pull request](https://github.com/an-tao/drogon/compare)\nif you think you can contribute to the project. You will be listed as a\n[contributor](https://github.com/an-tao/drogon/graphs/contributors), agree to\nthis document, and the\n[LICENSE](https://github.com/an-tao/drogon/blob/master/LICENSE).\n\nThere are also some recommendations you can follow. These aren’t requirements,\nbut they will make the development more straightforward:\n\n1. If you are unsure about a specific change, have questions, or want to get\n   feedback about a feature you want to introduce, [open a new\n   issue](https://github.com/an-tao/drogon/issues) (please make sure that there\n   is no previous issue about a similar topic).\n1. You should branch off the current state of the `master` branch, and also\n   merge it into your local branch before creating a pull request if there were\n   other changes introduced in the meantime.\n1. You can use the following branch names to make your intent clearer:\n    * `bugfix/123-fix-template-parser` when you want to fix a bug in the\n      template parser.\n    * `feature/123-add-l10n-and-i18n` if you want to add localization (l10n) and\n      internationalization (i18n) as a new feature to the project.\n    * If there’s no open issue and no need to open one you can skip the number,\n      and just use the descriptive part: `bugfix/fix-typo-in-docs`.\n1. Write a brief, but good, and descriptive commit message / pull request title in English,\n   e. g. “Added Internationalization and Localization”.\n\nIf you follow these recommendations your pull request will have more success:\n\n1. Keep the style consistent to the project, when in doubt refer to the [Google\n   C++ Style Guide](https://google.github.io/styleguide/cppguide.html#C++_Version).\n1. Please write all comments in English. Comments for new public API introduced by\n   your pull request must be added and written in [Doxygen](http://www.doxygen.nl/) format.\n1. Format the code with `clang-format` (>= 8.0.0). The configuration is already\n   provided in the `.clang-format` file, just run the `./format.sh` script\n   before submitting your pull request.\n1. Install [Google Test](https://github.com/google/googletest), and write a test\n   case.\n    1. In case it is a bugfix, it’s best to write a test that breaks in the old\n       version, but works in the new one. This way regressions can be tracked\n       over time.\n    1. If you add a feature, it is best to write the test as if it would be an\n       example how to use the newly introduced feature and to test all major,\n       newly introduced code.\n\n## Project Maintainers & Collaborators\n\nIn addition to the guidelines mentioned above, collaborators with write access\nto the repository should also follow these guidelines:\n\n1. If there are new tests as part of the pull request, you should make sure that\n   they succeed.\n1. When merging **Pull Requests** you should use the option *Squash & Merge* and\n   chose a descriptive commit message for the bugfix / feature (if not already\n   done by the individual contributor).\n\n    This way the history in the `master` branch will be free of small\n    corrections and easier to follow for people who aren’t engaged in the\n    project on a day-to-day basis.\n"
  },
  {
    "path": "CPPLINT.cfg",
    "content": "# Stop searching for additional config files.\nset noparent\n\nexclude_files=trantor\nexclude_files=build\n\n# Use non-const reference rather than a pointer.\nfilter=-runtime/references\n\n# CHECK macros are from Drogon, not Google Test.\nfilter=-readability/check\n\n# Don't warn about the use of C++11 or C++17 features.\nfilter=-build/c++11\nfilter=-build/c++17\n\nfilter=-build/include_subdir\n\n# We prioritize clang-format for now.\nfilter=-whitespace\n\n# We don't require a username in TODO comments.\nfilter=-readability/todo\n\n# TODO: Fix these.\nfilter=-legal/copyright\nfilter=-build/namespaces\nfilter=-build/include\nfilter=-build/include_what_you_use\nfilter=-runtime/explicit\nfilter=-runtime/string\nfilter=-runtime/int\nfilter=-readability/casting\nfilter=-readability/braces\nfilter=-readability/fn_size\nfilter=-runtime/threadsafe_fn\n"
  },
  {
    "path": "ChangeLog.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n## [Unreleased]\n\n### Added\n\n- dg_ctl create models new command line option --clear-output\n\n\n## [1.9.12] - 2026-01-26\n\n### API changes list\n\n- Implement when_all coroutine gate.\n\n### Changed\n\n- Models: add toString method.\n\n- changing std::strlen to a string comparison consistent with multibyte chars on model generation templates.\n\n- Enhance error handling in CacheFile methods.\n\n- Mysql exception forward errorNo to SqlError.\n\n- Support unsigned integer types in MySQL ORM.\n\n### Fixed\n\n- Fix errors after database connection interruption.\n\n- Use redisFreeCommand instead of free() function.\n\n- Fix the missing openssl dependency in FindMysql.\n\n- Fix warnings - for range loop no copies, unsigned int always >= 0.\n\n- Fix cross compiling.\n\n- Fix linker errors on mingw64.\n\n- Fix SQL syntax error when UPDATE has no fields to update.\n\n- Fix unused variable warnings.\n\n## [1.9.11] - 2025-06-20\n\n### API changes list\n\n- Add a new overload for execSqlCoro.\n\n### Changed\n\n- Do not write to source directory during build.\n\n- Improve Postgres connection stability.\n\n- Add handleFatalError in handleClosed.\n\n- Add -o|--output option to drogon_ctl create models.\n\n- Add qrcode for WeChat official account to the README file.\n\n- Support for iOS compiling.\n\n- Add cors example to demonstrate cross-origin support in drogon.\n\n- Add support for continuation frame in WebSocketMessageParser.\n\n- Add RawParameter API to pass raw SQL parameters.\n\n- Upgrade Windows image and re-enable tests on Windows.\n\n### Fixed\n\n- Fix a bug in isAutoCreationClass<T>.\n\n- Fix CI on MacOS.\n\n- Fix issue with precision loss of double-type parameters in ORM inputs.\n\n\n## [1.9.10] - 2025-02-20\n\n### API changes list\n\n- Add setConnectionCallback.\n\n### Changed\n\n- ORM:Avoid unnecessary copies when returning search results.\n\n- Improve the zh-TW README translation.\n\n- Make quit function thread safe.\n\n- Added path_exempt in AccessLogger plugin config to exclude desired paths.\n\n### Fixed\n\n- Fix the issue in view generation by including the missing header file.\n\n- Fix ci: codespell.\n\n## [1.9.9] - 2025-01-01\n\n### API changes list\n\n- Added Partitioned flag for cookies.\n\n### Changed\n\n- Update FindFilesystem.cmake to check for GNU instead of GCC for CMAKE_CXX_COMPILER_ID.\n\n- Update README.\n\n- Chore(workflow/cmake.yml): upgrade macos runner.\n\n- Add emptiness check to the LogStream &operator<< with std::string_view.\n\n### Fixed\n\n- Fix a bug in plugin Redirector.\n\n- Fix CMAKE issues mentioned in #2144 and a linking problem which manifest with gcc12.3 when building with shared libs.\n\n- Fix: Remove dependency on locales being installed on the system.\n\n## [1.9.8] - 2024-10-27\n\n### API changes list\n\n- Add in-place base64 encode and decode.\n\n- Add check the client connection status.\n\n### Changed\n\n- Add Hodor whitelists.\n\n- Include exception header for std::exception_ptr.\n\n- Add support for escaped identifiers in Postgresql.\n\n- Remove content-length header from 101 Switching Protocols response.\n\n- Remove websocketResponseTest from windows shared library env.\n\n- Optimize query params and allow for empty values.\n\n- Replace rejection sampling and remove use of rand().\n\n- Add sending customized http requests to drogon_ctl.\n\n### Fixed\n\n- Fix coroutine continuation handle.\n\n- Fix some bugs in plugin PromExporter.\n\n- Fix a bug after removing content-length header in some responses.\n\n## [1.9.7] - 2024-09-10\n\n### API changes list\n\n- Add coroutine mutex.\n\n- Add requestsBufferSize function.\n\n- Refine SQLite3 error types with new exception handling.\n\n- Add a new method to reload SSL files on the fly.\n\n### Changed\n\n- Allow MultiPartParser to be movable.\n\n- Add quotes to the table name in the ORM generator.\n\n- Change stoi to stoul in the Field class.\n\n- Modernize cookies.\n\n- Change a log level.\n\n### Fixed\n\n- Use correct libraries when compiling statically.\n\n## [1.9.6] - 2024-07-20\n\n### API changes list\n\n- Add setsockopt to HttpServer.\n\n- Support request stream.\n\n### Changed\n\n- Allow MultiPartParser to parse PATCH requests.\n\n- Add an example of prometheus.\n\n- Delay parsing json for HttpClient.\n\n- Update README.md.\n\n### Fixed\n\n- Fix some compilation warnings.\n\n- Fix typo in yaml config.\n\n## [1.9.5] - 2024-06-08\n\n### API changes list\n\n- Fix an error in the yaml format config file.\n\n- Support postgresql connection options.\n\n- Add regex support for websocket controller.\n\n- Add the registerMiddleware method.\n\n### Changed\n\n- Add the conan badge to readme files.\n\n- Install gcc in ci.\n\n- Intention to present an alternative to improve the performance of a method in models.\n\n### Fixed\n\n- Fix an error in the yaml format config file.\n\n- Fix CI on Windows.\n\n- Fix some spelling errors.\n\n## [1.9.4] - 2024-05-04\n\n### API changes list\n\n- Add client cert support for websocket.\n\n- Add JSON send overloads for WebSocket connections.\n\n### Changed\n\n- Minor enhancement: move some smart pointers around instead of copying them.\n\n- Remove the request shared_ptr from the multipart parser.\n\n- Fix typo in HttpAppFrameworkImpl.cc.\n\n- Avoid string copy and lowercasing on every request.\n\n- Implemented database reconnection loop.\n\n### Fixed\n\n- Bypass clang thread_local error.\n\n## [1.9.3] - 2024-02-09\n\n### API changes list\n\n- Added getParameter() and getOptionalParameter().\n\n- Change drogon::MultiPartParser's parameters data type.\n\n- Use std::string_view for WebSockets.\n\n### Changed\n\n- Add support for gentoo linux, dev-db/mariadb contains mysql.\n\n- Introduce cpplint to the CI.\n\n- Enable readability/alt_tokens for cpplint.\n\n- Use clang-format-17.\n\n- Add newline at EOF.\n\n- Enable readability/inheritance for cpplint.\n\n- Enable build/explicit_make_pair for cpplint.\n\n- Enable build/include_order for cpplint.\n\n- Enable build/header_guard for cpplint.\n\n- Enable build/storage_class for cpplint.\n\n- Enable readability/multiline_string for cpplint.\n\n- Alias the safe hashmap template.\n\n- Simplify traits in utils.\n\n- Enhancement: extend drogon::ContentType for file handling.\n\n### Fixed\n\n- Fix a wrong place of return.\n\n- Fix drogon::util::fromString().\n\n## [1.9.2] - 2024-01-18\n\n### API changes list\n\n- Feature: Integrate spdlog as logging backend.\n\n- Support asynchronous sending of chunked responses.\n\n### Changed\n\n- Modify the configuration file templates in drogon_ctl.\n\n- Use execute_process instead of exec_program in FindJsoncpp.cmake.\n\n- GitHub Action to find typos in the codebase using codespell.\n\n- add discord link to readme.\n\n- Add -k option to the drogon_ctl when running the press command.\n\n- Refine request routing process.\n\n- Add CI tests with more compilers.\n\n- Avoid a race condition in database listener tests.\n\n- Remove macos-11 CI; not supported by Homebrew.\n\n- Bump github/codeql-action from 2 to 3.\n\n- Move the RealIpResolver plugin to the PreRouting join point.\n\n### Fixed\n\n- Fix: typo on Mapper method.\n\n- Fix a error of coroutines on Windows.\n\n- Fix ORM: The original way did not handle exceptions correctly.\n\n- Remove the default ctor of the Row class in ORM.\n\n- Set the url of trantor to the official repository.\n\n- Fix htonll/ntohll redefinition.\n\n- Fix building with MSYS2.\n\n- Fix name issue when cross-compiling.\n\n## [1.9.1] - 2023-11-27\n\n### API changes list\n\n- Pass HttpRequestPtr to custom error handlers.\n\n- Provide some functions for incrementing the value of given columns.\n\n- Return HttpAppFramework by setExceptionHandler.\n\n### Changed\n\n- Custom sessions.\n\n- Use the constexpr if instead of std::enable_if.\n\n- Make id generator consistent.\n\n- Update test_cmake.csp.\n\n- Simplify drogon test with c++17.\n\n- Remove unused and undefined overloads of isBase64.\n\n### Fixed\n\n- Fix build due to trantor commit out of date and address warnings.\n\n- Fix a bug of the GlobalFilters plugin.\n\n- Fix: uuid formatting.\n\n## [1.9.0] - 2023-10-29\n\n### API changes list\n\n- Added isTopicEmpty function;\n\n### Changed\n\n- Update the ubuntu Dockerfile;\n\n- Add optional Criteria && || operator support;\n\n- Bump actions/checkout from 3 to 4;\n\n- Make & and * directly adjacent to variable names;\n\n- Use wss://echo.websocket.events/.ws in WebSocket client example;\n\n- Change logs in the AccessLogger plugin to TRACE level;\n\n### Fixed\n\n- Fix an error in the secureRandomString function;\n\n- FIX int mapping to int64_t instead of uint64_t;\n\n\n## [1.9.0-rc.1] - 2023-09-23\n\n### API changes list\n\n- Drop cpp14 build support.\n\n- Add isHead() method to HttpRequest, to preserve information about the original method for use in the controller.\n\n- Allow omitting template parameter in execCommandSync.\n\n- Add a method to HttpRequest to access the matched routing parameters.\n\n### Changed\n\n- Update readme files.\n\n- Allow sync advice to be callable on websocket requests.\n\n- Set concurrency to prevent blocking CI queue.\n\n- Validate clang-format version & Customize clang-format path.\n\n- Extract format action into distinct job.\n\n- Split macOS and Ubuntu CIs for readability.\n\n- Set concurrency for CodeQL.\n\n- Add dependabot.yml for GH actions.\n\n- Replace sprintf with snprintf.\n\n- Use ninja to build faster.\n\n- Avoid using well-known ports for demoMain.\n\n- Simplify coroutine implementation.\n\n- Add a plugin for prometheus.\n\n- Optimize plugins with redirection functions.\n\n- Optimize regex generator.\n\n- Add override keyword to setSockOptCallback.\n\n- SlashRemover optimization.\n\n### Fixed\n\n- Fix race condition when setting the secure flag during test.\n\n## [1.8.6] - 2023-08-23\n\n### Changed\n\n- Show outputs of try_compile for UUID libs.\n\n- Update Trantor to fix a serious bug when sending files.\n\n## [1.8.5] - 2023-08-19\n\n### API changes list\n\n- Add the forwardCoro method to HttpAppFramework.\n\n- Possibility to add plugins without config file.\n\n- Implement QueryBuilder.\n\n- Add getOriginalPath function.\n\n- Add setsockopt to HttpClient.\n\n- Add overload function of newHttpResponse.\n\n- Add getConnectionCount method.\n\n### Changed\n\n- Add synchronization interface to model's associated query.\n\n- Use syncAdvices.empty() to check.\n\n- Remove the deprecated Json::Reader.\n\n- Adapt Drogon to take advitange of Trantor TLS refactor.\n\n- Add avatars of all contributors to the readme file.\n\n- Remove docsforge from readme.\n\n- Adapt to gcc13.\n\n- Modify the HttpMessageBody class.\n\n- Add the GlobalFilters plugin.\n\n- Add filters and plugins to the drogon.h header file.\n\n- Add webp and svg to the default file_types.\n\n- Update SqlBinder.h.\n\n- Remove path from COMPILER_COMMAND.\n\n- Hodor plugin - Use IP bytes for user IP identification.\n\n- Add SlashRemover plugin.\n\n- Support setting max_files in loggers.\n\n- Use shared_ptr to store plugins.\n\n- Throw custom exception in HttpClient.\n\n- Base64 improvements.\n\n- Add CT_TEXT_JAVASCRIPT.\n\n- Fix typo in newHttpJsonResponse documentation.\n\n- Add CodeQL workflow.\n\n- constexpr base64 length calculators.\n\n- Make isBase64() and isInteger() take string_view.\n\n- Chore: add package.xml.\n\n- Add an example of yaml config file.\n\n- Remove some unused functions.\n\n- Use unsigned char to call the std::isspace function.\n\n- Change options BUILD_CTL and BUILD_EXAMPLES and update readme with Building options.\n\n- Remove unused CI files and Jekyll config.\n\n- Ensure that all filters, AOP advice, and handlers are executed within the IO threads.\n\n- Update test.sh and build.sh by appending prefix \"X\" to string variable comparisons.\n\n- Recognize URI in request lines.\n\n- Address warnings on macOS CI.\n\n- Perform insensitive string compare of cookie SameSite attribute.\n\n### Fixed\n\n- Temporary fix dead HttpClient.\n\n- Fix a configuration error when building drogon_ctl.\n\n- Fix generation of JSON field validation for short type.\n\n- Fix bug on Mac M1 with redis chat example code.\n\n- Fix error: conversion from ‘long int’ to ‘Json::Value’ is ambiguous.\n\n- Bugfix: PgBatchConnection did not report error message.\n\n- Fix typo in config file.\n\n- Fix a hanging bug when writing large string to text field.\n\n- Fixed logic error of limiterExpireTime_.\n\n- Fix Missing Header BaseBuilder.h in CMakeLists.\n\n- Fix: Coroutine handle should be captured by value.\n\n- Fix issues in the ListParaView and content types.\n\n- Fix target link libraries of yaml-cpp to comaptiable with vcpkg.\n\n- Fix build error on win32/mingw.\n\n- Fix CI in MacOS.\n\n- Fix broken link in CONTRIBUTING.md.\n\n## [1.8.4] - 2023-03-19\n\n### API Changes list\n\n- Add a configuration option to display local time in logs.\n\n- Add the stackLimit option for jsoncpp.\n\n- Add coroutine to wait until event loop ends.\n\n- Add switchThreadCoro().\n\n- Add queueInLoopCoro function.\n\n### Changed\n\n- Reduce overhead of constructing string from request status.\n\n- Add coroutine parameter binding test and make tests not blocking.\n\n- Use weakptr instead of shared_ptr in HttpClient.\n\n- Refactor HttpServer codes.\n\n- Add an option in CMakeLists.txt to set how to use trantor.\n\n- Add support to yaml config file.\n\n- Changes to free http client if no request is pending.\n\n- Trigger error message in drogon test when faced with bad parameter.\n\n### Fixed\n\n- Make the token bucket full when it is initialized\n\n- Fix file r/w race condition in integration test.\n\n- Postgresql: don't handle events anymore after connection closing.\n\n- Fix multipart boundary with charset.\n\n- Fix a conan issue in github actions.\n\n- Hold shared pointer from the very beginning in WebSocketConnectionImpl.\n\n## [1.8.3] - 2023-01-23\n\n### API changes list\n\n- Add onSessionStart() and onSessionDestroy() events\n\n- Support postgresql asynchronous notification (LISTEN/NOTIFY).\n\n### Changed\n\n- Update Utilities.cc\n\n- Drogon test refactor\n\n- Enable json entry as string\n\n- Mark awaiters as non-copyable\n\n### Fixed\n\n- Fix HttpController regex path matching\n\n- Fix a memory leak in the redis example\n\n- Partial fix for MSYS2 compat\n\n## [1.8.2] - 2022-11-11\n\n### API changes list\n\n- Add the queueInLoopCoro function.\n\n- Avoid HashDoS attacks via random per-session hash initial state.\n\n### Changed\n\n- Support the mediumint column when generate the mysql model.\n\n- Set Hiredis_FOUND to true when finding Hiredis library.\n\n- Add rate limiter.\n\n- Add some test cases for the sqlite datetime type.\n\n### Fixed\n\n- Fix typo in drogon_test.h.\n\n- Fix a date race in drogon_test.\n\n- Fix a deadlock bug when closing all database connections.\n\n## [1.8.1] - 2022-09-25\n\n### API changes list\n\n- Support redis subscription.\n\n### Changed\n\n- Remove redundant member functions of drogon::Task.\n\n- Small patches on orm_lib.\n\n- Add support for the string_view type to SqlBinder in orm.\n\n### Fixed\n\n- Fix a conflict of ssize_t type with hiredis.\n\n- Fix a test bug when clients start before servers.\n\n- Fix model template file Unreachable code.\n\n- Use the mysql_library_end() function to avoid memory leaks.\n\n## [1.8.0] - 2022-09-01\n\n### API changes list\n\n- Add ‘not like‘ criteria.\n\n- Add HttpResponse::newStreamResponse().\n\n- Add the same site option for session cookie.\n\n- Add support for custom SQL query.\n\n### Changed\n\n- Update issue templates.\n\n- Enable automatic reconnect in mysql.\n\n- Add typename for clang-14.\n\n- A workaround for redis sync exec.\n\n- Resolve redis server hostname from config file.\n\n- Add username option to redis databases.\n\n- Return nullptr if a plugin is not loaded when getting it.\n\n- Support controller registration in plugin.\n\n- Check mysql-optionsv support in cmake.\n\n- Check if host header is set before setting.\n\n- Clear all database connections before quitting.\n\n- Add namespace to views when using drogon_ctl.\n\n- Support pipeline mode on PostgreSQL 14+.\n\n- Add content type to multipart file upload.\n\n- Make orm::Result compatible with C++ STL container concepts.\n\n- Throw exceptions instead of exiting when loading configuration fails.\n\n- Rename BUILD_TRANTOR_SHARED to BUILD_SHARED_LIBS.\n\n- Support compressed request.\n\n- Prevent sending multiple responses for a single request.\n\n- Remove the virtual specifier from functions marked with override.\n\n- Remove redundancies from the CMake action.\n\n- Ensure requiring a semi-colon after macros.\n\n- Omit redundant virtual specifiers.\n\n- Refactor orm::SqlBinder.\n\n- Implement toJson to convert std::vector to Json::Value.\n\n- Resolve real ip from HttpRequest.\n\n- Delete the unmaintained test script.\n\n- Change the listener port of the cookie test.\n\n- Use a raw string literal for the drogon banner.\n\n- Change timeout of pipeline test for the CI environment.\n\n- Accept \"postgres\" for DbClient type as well.\n\n- Log remote real address in AccessLogger.\n\n- Support coroutine filter.\n\n- Refactor db_test.cc.\n\n- Use nullopt instead of the no-argument constructor.\n\n- Set the running flag to false after calling the quit() method.\n\n- Fix doc link in README files.\n\n### Fixed\n\n- Fix XXXControllers created on MSVC even if specified not to do so.\n\n- To avoid accessing a null point, make sure `result == OK` before accessing the response ptr.\n\n- Fix a bug when stopping redis service.\n\n- Fix mutex lock missing.\n\n- Fix tolower with cfi sanitizer.\n\n- Add move constructor to fix clang-14 compile error.\n\n- Fix HttpClient dns cache.\n\n- Fix bug when resolving redis server hostname.\n\n- Reset timer afters relaunching on Linux.\n\n- Fix some configuration file issues.\n\n- Fix HttpFile unittest error on Windows.\n\n- Fix core dump causing by logging in destructor.\n\n- Fixing link error when linking with static c-ares.\n\n- Remove redundant resource release.\n\n- Install missing header file apply.h.\n\n- Fix deleteFutureByPrimaryKey compile fail.\n\n- Fix compilation failure without database support.\n\n- Fix Mapper::updateBy() async api.\n\n- Fix no BUILD_CTL with tests.\n\n- Fix some bugs in RedisClient.\n\n- Fix a misuse of std::move.\n\n- Fix a bug when creating models with composite keys in sqlite3.\n\n- Fix a bug when converting the content-length string to size_t.\n\n- Fix a bug when parsing multipart/form-data.\n\n- Export the getVersion method for Windows.\n\n- Add a pre-compilation macro in the pg pipeline test code.\n\n## [1.7.5] - 2022-02-19\n\n### API changes list\n\n- Add toString for drogon::ReqResult.\n\n- Add max-age, samesite options to Cookie.\n\n- Enable setup output of logs to files at any time.\n\n### Changed\n\n- Use operator<< to convert ReqResult to string.\n\n- Remove sudo from build.sh.\n\n- Remove sudo from dependencies in Dockerfile.\n\n- Avoid attempt linking to std::fs when target does not exist.\n\n- Destroy fastdb client on quit.\n\n- Check HTTP client is not sending requests in sync mode on the same event loop.\n\n- Start listening after beginning advice.\n\n- Allow using json_cpp in other sublibraries.\n\n- Accept system lib for uuid on macOS.\n\n- Add `Not In` to ORM Criteria.\n\n### Fixed\n\n- Fix WS test potentially can get stuck.\n\n- Fix a bug in model generation.\n\n- Prevent malformed upload path causing arbitrary write.\n\n- Fix missing \"using namespace drogon::orm;\" at autogenerated restful controllers.\n\n## [1.7.4] - 2021-12-11\n\n### API Change List\n\n- Support setting client certificate and SSL options on HTTP client\n\n- Add more method for mapper\n\n- Add overloads for SqlBinder::operator<< with non-const ref parameter\n\n### Changes\n\n- Use decay_t instead of remove_cvref_t\n\n- Prevent `drogon_ctl create_view` appending empty new lines to resulting\n\n- Add an example for using coroutines of redis clients\n\n- Export some symbols for Windows\n\n- Mark all awaiters as nodiscard\n\n- Handle SIGINT too\n\n- Support CoroMapper method chaining\n\n- Remove setting c++17 in FindFilesystem\n\n### Fixed\n\n- Fix Drogon not building caused by FindFilesystem\n\n- Fix deprecated warning when using openssl 3\n\n- Fix coroutine object destructing before coroutine ends in async_run\n\n- Fix build fail on CentOS8\n\n- Fix some compiler warnings\n\n- Fix the error with multiple results when calling a procedure in mysql\n\n- Fix an error when binding a function pointer to SqlBinder\n\n- Fix orm tests\n\n- Fix CI to actually build in C++14\n\n- Fix a race condition when resetting ws\n\n- Fix an error of std::bad_function_call\n\n- Update Trantor (fix sending partial files)\n\n## [1.7.3] - 2021-10-17\n\n### API Change List\n\n- Support sending files by range\n\n- Allow outside access to the file path of an HTTP response\n\n- Support custom MIME types and extensions\n\n- Add the getOptionalParameter method\n\n- Add async_run\n\n### Changes\n\n- Experimental HaikuOS Support\n\n- Improve AccessLogger\n\n- Add Alpine Dockerfile\n\n- Add option to disable brotli if desired by the builder\n\n### Fixed\n\n- Fix a bug in the getIOLoop method\n\n- Return on redis connection errors\n\n- Fix(MultiPart): Does not respect quotes in Content-Disposition header\n\n- Fix(cmake): error in FindFilesystem\n\n- Fix(style): Change the NotFound page text color\n\n- Fix a race condition in testing\n\n## [1.7.2] - 2021-08-24\n\n### API Change List\n\n- Add port() and host() to HttpClient\n\n- Add stop() method to the WebSocketClient class\n\n### Changes\n\n- Enables higher level of warnings when building on UNIX with GCC\n\n- Generic optimizations\n\n- Add redis example\n\n- Added support for paths containing unicode characters on Windows\n\n- Load ParseAndAddDrogonTests in DrogonConfig\n\n- Add BUILD_DOC to cmake options\n\n- Add websocket server example\n\n- CMake: Add CPack for .deb and .rpm package generation\n\n- cmake: Use GNUInstallDirs to figure out install dirs.\n\n### Fixed\n\n- Fix WS client example not working with integration_test\n\n- Fix WS client example error when encountering bad IP addresses\n\n- CacheFile supports >2GB files on 64-bit Windows\n\n- `drogon_ctl` now emits error on failing to create view from CSP\n\n- Added the `make` program to Ubuntu docker environment\n\n- Correctly check the case-insensitive value of the upgrade header of responses in websocket connections\n\n- Fix incorrect MD5 hash when using internal MD5 implementation when input size == block size+1\n\n- Fix test success message incorrectly shown for a failed test when -s is flag present\n\n- Force using boost::filesystem when building for Android\n\n- Escape connection string in drogon_ctl create model\n\n- Fix some memory leak and race conditions in WebSocketClient\n\n## [1.7.1] - 2021-06-24\n\n### Changes\n\n- Updated Dockerfile to Ubuntu 20.04 & Fixed Timezone Hangup.\n\n- Add jsonstore example.\n\n- Fix some typos.\n\n### Fixed\n\n- Fix single layer directory traversal in StaticFileRouter.\n\n## [1.7.0] - 2021-06-18\n\n### API changes list\n\n- Add the PreSendingAdvice to AOP.\n\n- Make Json::Value as a SQL parameters type.\n\n- Add the int type for the Row index parameter.\n\n- Add SSL_CONF_cmd support.\n\n- Add the setCustomStatusCode method.\n\n### Changes\n\n- Fix sync_wait/co_future use-after-free.\n\n- Add the AccessLogger plugin.\n\n- Make AsyncTask only destruct when the coroutine reaches end of executions.\n\n- Add Drogon test framework.\n\n- Improve WebSocket mask handling.\n\n- Add minimal server side examples.\n\n- Optimize HttpControllersRouter for cases where regex is not needed.\n\n- Create controller instances after running instead of after being called.\n\n### Fixed\n\n- Move resolverPtr when destroying an HttpClientImpl object.\n\n- Modify the way to create sqlite3 client.\n\n- Fix a bug when a network failure occurs on Redis connections.\n\n- Fix a bug of string_view for MSVC.\n\n- Fix 'build.sh -tshared'.\n\n- Fix compiler warnings.\n\n- Fix CacheMap crash in CI tests.\n\n## [1.6.0] - 2021-05-15\n\n### API changes list\n\n- Add option to set default handler.\n\n- Add the setTimeout() method to the DbClient class and the RedisClient class.\n\n- Add the validateCert parameter to the newWebSocketClient method.\n\n### Changed\n\n- A few mini changes to drogon_ctl command.\n\n- Improve the MultiPartParser class.\n\n- Add GNU -Werror & fix warnings.\n\n- Enhancements on files part.\n\n- Add version/soversion to shared library.\n\n- Disallow coroutines to be resolved as plain subroutine handlers.\n\n- Send the content-length header even if the body(POST,PUT,OPTIONS,PATCH) is empty.\n\n- Use make_exception_ptr instead of throw/catch when possible.\n\n- Remove duplicated inclusion.\n\n- Print error before terminating in AsyncTask.\n\n- Allow users to override drogon Find modules.\n\n- Use two-phase construction for the DbClientImpl and the RedisClientImpl.\n\n- Add support 'select &lt;db&gt;' for redis.\n\n### Fixed\n\n- Fix a bug of the Transaction class.\n\n- Copy CoroMapper.h to installation location.\n\n- Remove the related request from the buffer if it's not sent after the timeout.\n\n- Fix ORM with SQLite3 not compiling on Arch Linux.\n\n- Fix an error when constructing RedisClientImpl objects.\n\n- Fix coroutine frame leak upon assigning to awaitable.\n\n- Set running flag to true before installing plugins.\n\n- Fix double free in coroutine exception handling.\n\n## [1.5.1] - 2021-04-10\n\n### Fixed\n\n- Fix a bug of reflection failure.\n\n## [1.5.0] - 2021-04-10\n\n### API changes list\n\n- Add option to disable signal handling.\n\n- Added newFileResponse Support for buffers in memory.\n\n- Add a method to HttpRequest to set the user_agent header.\n\n- Catch exceptions thrown by handlers.\n\n### Changed\n\n- Add convert method to models.\n\n- Add Arch Dockerfile.\n\n- Add Redis support.\n\n- Print error and exit when IP parsing failed in server startup.\n\n- Use a canonical way of calling max() function on Windows.\n\n- Remove an assertion statement in the HttpClientImpl class.\n\n- Send ping messages by default for WebSockets.\n\n- Use canonical cmake logic for cross-compilation.\n\n- set make job count to the number of threads in GitHub Actions workflow.\n\n- Use lambda instead of std::bind in HttpServer.\n\n- Add exports macro to allow Shared Library with hidden symbols by default.\n\n- Remove repeated class names on relationships from the model generator.\n\n### Fixed\n\n- Fix compile warnings in SQL client.\n\n- Fix compilation errors for the TimeFilter example.\n\n- Fix build.sh missing nproc error in build for macOS.\n\n- Fix a bug when creating sqlite3 models.\n\n- Fix two building corner cases, CMake quality of life improvements.\n\n- Add CoroMapper to models' friends.\n\n## [1.4.1] - 2021-03-07\n\n### Fixed\n\n- Fix a bug of DbClientImpl class that can lead to a crash when database connections are breaking.\n\n## [1.4.0] - 2021-03-05\n\n### API changes list\n\n- Add coroutine support.\n\n- Add default value interface to SqlBinder for MySQL and PostgreSQL.\n\n- Support SNI in the HttpClient class.\n\n- Validate certificate in HttpClient.\n\n- HttpRequest: add a feature to avoid URL encoding of the path.\n\n### Changed\n\n- Handle cross-compiling properly.\n\n- Lowercase all HTTP headers, add webp and avif types.\n\n- Modify FindMySQL.cmake\n\n### Fixed\n\n- Fix an error in the HttpClient class when a response has no content-length.\n\n- Return 404 or 405 responses correctly.\n\n- Fix compilation errors on vs2019.\n\n- Fix stack use after scope error in client_example.\n\n- Fix the error when the SSL handshake fails.\n\n## [1.3.0] - 2021-01-16\n\n### API changes list\n\n- Add an option for setting float precision in Json string.\n\n### Fixed\n\n- Fix brotli link order.\n\n- Fix cmake with drogonctl cross-compilation.\n\n- sqlite3: Insert into stmtsMap_ as string_view.\n\n- Fix some bugs when creating models via drogon_ctl.\n\n- Fix an error in sqlite3 ORM generator.\n\n- Fix an error with missing composite key to sqlite3 ORM generator.\n\n### Changed\n\n- Remove the use of std::filesystem to adapt to old compilers.\n\n- Add github actions.\n\n- Serve wasm files with the correct MIME type.\n\n## [1.2.0] - 2020-12-12\n\n### Fixed\n\n- Fix error when receiving response without content-length header.\n\n- Fix a stack-overflow error when high concurrency happening on sqlite3.\n\n- Fix MinGW ORM building by enabling htonll and ntohll.\n\n### Changed\n\n- Modify the WebSocketTest controller to create a simple chat room.\n\n- Add support for OpenBSD.\n\n- Return 400 if the content-length is invalid.\n\n- Don't send content type in a 304 response.\n\n- Add the reuse_port option to app() interface.\n\n- Add the 'std::optional' support in the SqlBinder class and the Session class.\n\n- Add implicit page resolving capability.\n\n## [1.1.0] - 2020-10-31\n\n### Fixed\n\n- Fix failing to connect to DB if parameters contains spaces.\n\n- Fix a CMAKE bug when SHARED and EXAMPLES are on.\n\n- Fix the HttpServer::isWebSocket method.\n\n- Find MariaDB client library correctly on Ubuntu 20.04.\n\n- Fix a bug when creating sqlite3 database models.\n\n- Fix a bug in the Mapper::insertFuture method.\n\n### Changed\n\n- Disable TLS1.0/1.1 on HTTPS by default.\n\n- Use explicit lambda capture lists.\n\n- Modify the procedure of the app().run() method.\n\n- Support namespaces when creating view source files.\n\n- Add --path-to-namespace option to drogon_ctl for creating views.\n\n- Add the Host and Sec-WebSocket-Version headers when connecting to a websocket server.\n\n## [1.0.0] - 2020-09-27\n\n### Fixed\n\n- Fix an issue of simple_reverse_proxy when handling chunked transfer-encoding.\n\n- Fix a bug when losting connection to MySQL server during query.\n\n- Remove the expired std::iterator template.\n\n- Fix a bug when creating models in some special cases.\n\n### API changes list\n\n- Modify methods related to headers.\n\n- Remove the expired std::iterator template.\n\n- Add getListeners() method to the HttpAppFramework class.\n\n- Remove the useless method stat() from the PluginBase class.\n\n- Add ConfigLoader::ConfigLoader(const Json::Value &data).\n\n### Changed\n\n- Add support for status code 418.\n\n- Modify session handling.\n\n- Modify the FileUpload.csp in simple_example to avoid CORS.\n\n- remove execution permission on /tmp/drogon.lock.\n\n## [1.0.0-beta21] - 2020-08-19\n\n### Changed\n\n- Modify the Result class in ORM.\n\n### Fixed\n\n- Fix zlib link error on Windows for the latest vcpkg.\n\n## [1.0.0-beta20] - 2020-08-15\n\n### API changes list\n\n- Provide users with a method to change the session ID of a session.\n\n### Changed\n\n- Modify parseContentType function.\n\n- Modify the docker file to build release version in docker.\n\n- Set session to requests for websockets.\n\n- Modify parseContentType function.\n\n- Change the return value type of the mktime() function in models.\n\n- Fix compilation warning of sprintf function.\n\n### Fixed\n\n- Fix a bug when saving uploaded files on Windows.\n\n- Fix a MySQL issue when connections are lost.\n\n- Resolve an issue when sending big files (>=2GB) on Windows.\n\n- Fix boost::string_view compilation error of MysqlConnection class.\n\n- Set the response Access-Control-Allow-Headers header correctly for CORS.\n\n- Fix a bug in drogon_ctl when creating a model, that causes to write source files multiple times.\n\n## [1.0.0-beta19] - 2020-07-16\n\n### API changes list\n\n- Add a method to disable unicode escaping in json string.\n\n- Add a timeout parameter when sending HTTP requests.\n\n- Add the getJsonError method.\n\n### Changed\n\n- Remove the restriction on the location of layout tags in views.\n\n- Add a way to set the character set when creating DbClient objects.\n\n- Make `GET` as the only method for accessing static files.\n\n- Modify the 404 pages generator.\n\n- Modify the DbClient class.\n\n- Optimize the HttpResponse class.\n\n### Fixed\n\n- Properly handle chunked encoding requests.\n\n- Destroy DNS resolver of HttpClient in the correct thread.\n\n- Add the header &lt;cctype&gt; to resolve build errors in VS2017.\n\n## [1.0.0-beta18] - 2020-06-14\n\n### API changes list\n\n- Add a new joinpoint of AOP for modification on each HTTP response.\n\n- Add a method for the TERM signal handling.\n\n- Add getContextRef method to the WebSocketConnection class.\n\n### Changed\n\n- Create a class template for publish subscribe pattern.\n\n- Add contribution recommendations.\n\n- Send a close message when closing a web socket connection.\n\n- Add additional formats for getHttpDate function.\n\n- Make app().run() method callable on a non-main thread.\n\n- Add digest filter in examples.\n\n- Use string_view to parse multipart/form-data requests.\n\n### Fixed\n\n- Fix building of ORM on FreeBSD.\n\n- Fix a Mysql connection error on Windows.\n\n- Fix a bug in ListenerManager::getIOLoop().\n\n- Fix the count() method of Mysql ORM.\n\n- Fix a compilation issue on windows.\n\n- Fix model generation for PostgreSQL primary keys.\n\n- Fix a bug with quoted column names in sqlite3 databases.\n\n## [1.0.0-beta17] - 2020-05-22\n\n### API changes list\n\n- Add methods to get DbClient connection status\n\n### Changed\n\n- Add causal profiling with coz\n\n- Add filters on static file locations\n\n- Pass data from view to its layout container\n\n- Add additional HttpStatusCodes and implement a custom error handler\n\n- Modify drogon_ctl to show more compilation information\n\n### Fixed\n\n- Fix a bug in drogon_ctl (when size of a line is larger than buffer size)\n\n- Fix a connection bug of MariaDB clients\n\n## [1.0.0-beta16] - 2020-04-27\n\n### API changes list\n\n- Standardize Row and Result api in ORM\n\n### Changed\n\n- Add support for brotli compression\n\n- Parse content-type of HTTP requests\n\n- Remove non standard macros\n\n- Support url safe base64 codec\n\n## [1.0.0-beta15] - 2020-03-28\n\n### API changes list\n\n- Modify the Attributes interface of the HttpRequest class\n\n- Add the getHomePage() method to HttpAppFramework\n\n### Changed\n\n- Support br compression files\n\n- Update Content-Type support for PDF\n\n- Add support for MSVC 2015\n\n- Optimize the rendering of HTTP responses\n\n- Update the Dynamic Views Loading, add the `layout` tag\n\n- Graceful shutdown\n\n### Fixed\n\n- Fix error when finding the jsoncpp library\n\n- Fix the 'many to many' relationship in ORM\n\n- Fix a bug when creating json responses\n\n- Fix a bug on filters with WebSocketControllers\n\n- Fix a fatal bug in the MysqlConnection class\n\n- Fix crash with partial matched url\n\n- Fix null jsonObject from newHttpJsonRequest\n\n## [1.0.0-beta14] - 2020-02-17\n\n### API changes list\n\n- None\n\n### Added\n\n- Add IOLoop access function\n\n### Changed\n\n- Add support for regular expressions when routing\n\n- Add location configuration for static resources\n\n- Port drogon to Windows\n\n- Support 'password' keyword in configuration files\n\n- Remove get_version.sh\n\n- Modify dynamic view loading algorithm, add 'layout' tag for view generation.\n\n### Fixed\n\n- Fix an issue of out-of-range (#334)\n\n- Fix a bug in views generation (#341)\n\n## [1.0.0-beta13] - 2020-01-04\n\n### API changes list\n\n- None\n\n### Changed\n\n- Add some unit tests (based on gtest)\n\n- Add a reverse proxy example\n\n- Make a patch to support the ossp UUID library\n\n- Make shared linking possible\n\n- Add the drogon::OStringStream class\n\n- Optimize ORM\n\n- Modify singleton logic of DrClassMap\n\n### Fixed\n\n- Fix an error in the batch mode of libpq\n\n- Fix an error when clients use HTTP1.0\n\n## [1.0.0-beta12] - 2019-11-30\n\n### Changed\n\n- Make dg_ctl a symlink\n\n- Modify some code styles\n\n- Explicitly set path to '/' for JSESSIONID cookie\n\n- Handle gzip errors safely\n\n- Add the SecureSSLRedirector plugin\n\n### Fixed\n\n- Fix a bug in dg_ctl for creating models of sqlite3\n\n- Reset the flag used to parse json to false before recycling HttpRequest objects\n\n## [1.0.0-beta11] - 2019-11-06\n\n### Changed\n\n- Delete useless log output\n\n## [1.0.0-beta10] - 2019-11-04\n\n### API changes list\n\n- None\n\n### Changed\n\n- Add the headers configuration option for static files\n\n### Fixed\n\n- Fix(compilation on alpine): Replace u_short alias.\n\n## [1.0.0-beta9] - 2019-10-28\n\n### API changes list\n\n- Add interfaces for accessing content of attachments.\n\n- Add option to disable setting the 404 status code of the custom 404 page.\n\n- Make user can use any string as a placeholder's name in routing patterns.\n\n- Add type conversion methods to the HttpRequest and HttpResponse classes.\n\n### Changed\n\n- Modify cmake configuration.\n\n- Modify the quit() method.\n\n- Implement relationships in ORM.\n\n### Fixed\n\n- Fix size_t underflow of drogon_ctl.\n\n- Fix some race conditions.\n\n- Fix a busy loop bug when connections to MySQL server are timeout.\n\n## [1.0.0-beta8] - 2019-10-03\n\n### API changes list\n\n- Add length() method to the Field class.\n\n- Add `as<bool>()` function template specialization to the Field class.\n\n- Add add attribute store methods to the HttpRequest class.\n\n- Add the setCustomContentTypeString() method to the HttpRequest class.\n\n- Add thread storage.\n\n### Changed\n\n- Use .find('x') instead of .find(\"x\") in a string search.\n\n- Add the ability to create restful API controllers.\n\n### Fixed\n\n- Fix a bug of creating models for MySQL.\n\n- Fix a bug when HTTP method is PUT.\n\n- Fix a bug when using 'is null' substatement in ORM.\n\n- Fix a sqlite3 bug when some SQL errors occur.\n\n- Fix bug with parsing json.\n\n- Fix url decode.\n\n- Fix a error in HttpClient.\n\n- Fix a error in setThreadNum method.\n\n- Fix some race conditions.\n\n## [1.0.0-beta7] - 2019-08-31\n\n### API changes list\n\n- Remove the default value parameter of some methods (#220)\n\n### Changed\n\n- Optimize DNS in HttpClient and WebSocketClient (support c-ares library).\n\n- Reduce dependencies between declarations.\n\n- Add database tests in the travis CI and add test cases to database tests.\n\n- Reduce size of docker image.\n\n- Make the framework API support chained calls.\n\n- Add a synchronous join point for AOP.\n\n- Modify the CMakeLists to modern cmake style.\n\n### Fixed\n\n- Fix bugs in default return values of functions(#220),\n\n- Fix a bug in the cmake configuration file when there's '+' in the building path.\n\n- Fix a bug in drogon_ctl (when creating orm models)\n\n## [1.0.0-beta6] - 2019-08-08\n\n### API changes list\n\n- None\n\n### Changed\n\n- Modify the 'create view' sub-command of drogon_ctl\n\n- Optimize the transmission of pipelining responses.\n\n- Add the DrogonConfig.cmake file so that users can use drogon with the `find_package(Drogon)` command.\n\n## [1.0.0-beta5] - 2019-08-01\n\n### API changes list\n\n- None\n\n### Added\n\n- Add two methods to control if the Server header or the Date header is sent to clients with HTTP responses.\n    * void HttpAppFramework::enableServerHeader(bool);\n    * void HttpAppFramework::enableDateHeader(bool);\n\n### Changed\n\n- Support high performance batch mode of libpq.\n\n### Fixed\n\n- None\n\n## [1.0.0-beta4] - 2019-07-30\n\n### API changes list\n\n- HttpRequest::query() returns a const reference of std::string instead of a string_view\n- WebSocketConnection::setContext(), WebSocketConnection::getContext(), etc.\n- Remove the config.h from public API.\n\n### Added\n\n- None\n\n### Changed\n\n- Modify the CMakeLists.txt\n- Modify the get_version.sh\n\n### Fixed\n\n- None\n\n## [1.0.0-beta3] - 2019-07-28\n\n### API changes list\n\n- None\n\n### Added\n\n- Add a README file for examples.\n- Add some managers to reduce the size of the HttpAppFrameworkImpl code.\n- Add missing wasm ContentType.\n\n### Changed\n\n- Update the submodule - trantor.\n- Optimize processing of HTTP pipelining.\n\n### Fixed\n\n- Fix an error in the HttpClient class when sending a request using the HEAD method.\n\n## [1.0.0-beta2] - 2019-07-10\n\n### API changes list\n\n- Add setBody methods to the HttpRequest class.\n- Add the setContentTypeCodeAndCustomString method to the HttpResponse class.\n\n### Added\n\n- Add stress testing command to drogon_ctl.\n- Add -v, -h parameters to drogon_ctl.\n\n### Changed\n\n- Update the submodule - trantor.\n- Modify the handling of CORS.\n- Optimize the htmlTranslate method and the Field class.\n- Make all listeners share IO threads in the MacOS/Unix system.\n\n### Fixed\n\n- Fix a bug of the IsPlugin class.\n- Use default constructor of string_view to reset _statusMessage to fix a warning on GCC 9.1 on Arch Linux.\n\n## [1.0.0-beta1] - 2019-06-11\n\n[Unreleased]: https://github.com/an-tao/drogon/compare/v1.9.12...HEAD\n\n[1.9.12]: https://github.com/an-tao/drogon/compare/v1.9.11...v1.9.12\n\n[1.9.11]: https://github.com/an-tao/drogon/compare/v1.9.10...v1.9.11\n\n[1.9.10]: https://github.com/an-tao/drogon/compare/v1.9.9...v1.9.10\n\n[1.9.9]: https://github.com/an-tao/drogon/compare/v1.9.8...v1.9.9\n\n[1.9.8]: https://github.com/an-tao/drogon/compare/v1.9.7...v1.9.8\n\n[1.9.7]: https://github.com/an-tao/drogon/compare/v1.9.6...v1.9.7\n\n[1.9.6]: https://github.com/an-tao/drogon/compare/v1.9.5...v1.9.6\n\n[1.9.5]: https://github.com/an-tao/drogon/compare/v1.9.4...v1.9.5\n\n[1.9.4]: https://github.com/an-tao/drogon/compare/v1.9.3...v1.9.4\n\n[1.9.3]: https://github.com/an-tao/drogon/compare/v1.9.2...v1.9.3\n\n[1.9.2]: https://github.com/an-tao/drogon/compare/v1.9.1...v1.9.2\n\n[1.9.1]: https://github.com/an-tao/drogon/compare/v1.9.0...v1.9.1\n\n[1.9.0]: https://github.com/an-tao/drogon/compare/v1.9.0-rc.1...v1.9.0\n\n[1.9.0-rc.1]: https://github.com/an-tao/drogon/compare/v1.8.6...v1.9.0-rc.1\n\n[1.8.6]: https://github.com/an-tao/drogon/compare/v1.8.5...v1.8.6\n\n[1.8.5]: https://github.com/an-tao/drogon/compare/v1.8.4...v1.8.5\n\n[1.8.4]: https://github.com/an-tao/drogon/compare/v1.8.3...v1.8.4\n\n[1.8.3]: https://github.com/an-tao/drogon/compare/v1.8.2...v1.8.3\n\n[1.8.2]: https://github.com/an-tao/drogon/compare/v1.8.1...v1.8.2\n\n[1.8.1]: https://github.com/an-tao/drogon/compare/v1.8.0...v1.8.1\n\n[1.8.0]: https://github.com/an-tao/drogon/compare/v1.7.5...v1.8.0\n\n[1.7.5]: https://github.com/an-tao/drogon/compare/v1.7.4...v1.7.5\n\n[1.7.4]: https://github.com/an-tao/drogon/compare/v1.7.3...v1.7.4\n\n[1.7.3]: https://github.com/an-tao/drogon/compare/v1.7.2...v1.7.3\n\n[1.7.2]: https://github.com/an-tao/drogon/compare/v1.7.1...v1.7.2\n\n[1.7.1]: https://github.com/an-tao/drogon/compare/v1.7.0...v1.7.1\n\n[1.7.0]: https://github.com/an-tao/drogon/compare/v1.6.0...v1.7.0\n\n[1.6.0]: https://github.com/an-tao/drogon/compare/v1.5.1...v1.6.0\n\n[1.5.1]: https://github.com/an-tao/drogon/compare/v1.5.0...v1.5.1\n\n[1.5.0]: https://github.com/an-tao/drogon/compare/v1.4.1...v1.5.0\n\n[1.4.1]: https://github.com/an-tao/drogon/compare/v1.4.0...v1.4.1\n\n[1.4.0]: https://github.com/an-tao/drogon/compare/v1.3.0...v1.4.0\n\n[1.3.0]: https://github.com/an-tao/drogon/compare/v1.2.0...v1.3.0\n\n[1.2.0]: https://github.com/an-tao/drogon/compare/v1.1.0...v1.2.0\n\n[1.1.0]: https://github.com/an-tao/drogon/compare/v1.0.0...v1.1.0\n\n[1.0.0]: https://github.com/an-tao/drogon/compare/v1.0.0-beta21...v1.0.0\n\n[1.0.0-beta21]: https://github.com/an-tao/drogon/compare/v1.0.0-beta20...v1.0.0-beta21\n\n[1.0.0-beta20]: https://github.com/an-tao/drogon/compare/v1.0.0-beta19...v1.0.0-beta20\n\n[1.0.0-beta19]: https://github.com/an-tao/drogon/compare/v1.0.0-beta18...v1.0.0-beta19\n\n[1.0.0-beta18]: https://github.com/an-tao/drogon/compare/v1.0.0-beta17...v1.0.0-beta18\n\n[1.0.0-beta17]: https://github.com/an-tao/drogon/compare/v1.0.0-beta16...v1.0.0-beta17\n\n[1.0.0-beta16]: https://github.com/an-tao/drogon/compare/v1.0.0-beta15...v1.0.0-beta16\n\n[1.0.0-beta15]: https://github.com/an-tao/drogon/compare/v1.0.0-beta14...v1.0.0-beta15\n\n[1.0.0-beta14]: https://github.com/an-tao/drogon/compare/v1.0.0-beta13...v1.0.0-beta14\n\n[1.0.0-beta13]: https://github.com/an-tao/drogon/compare/v1.0.0-beta12...v1.0.0-beta13\n\n[1.0.0-beta12]: https://github.com/an-tao/drogon/compare/v1.0.0-beta11...v1.0.0-beta12\n\n[1.0.0-beta11]: https://github.com/an-tao/drogon/compare/v1.0.0-beta10...v1.0.0-beta11\n\n[1.0.0-beta10]: https://github.com/an-tao/drogon/compare/v1.0.0-beta9...v1.0.0-beta10\n\n[1.0.0-beta9]: https://github.com/an-tao/drogon/compare/v1.0.0-beta8...v1.0.0-beta9\n\n[1.0.0-beta8]: https://github.com/an-tao/drogon/compare/v1.0.0-beta7...v1.0.0-beta8\n\n[1.0.0-beta7]: https://github.com/an-tao/drogon/compare/v1.0.0-beta6...v1.0.0-beta7\n\n[1.0.0-beta6]: https://github.com/an-tao/drogon/compare/v1.0.0-beta5...v1.0.0-beta6\n\n[1.0.0-beta5]: https://github.com/an-tao/drogon/compare/v1.0.0-beta4...v1.0.0-beta5\n\n[1.0.0-beta4]: https://github.com/an-tao/drogon/compare/v1.0.0-beta3...v1.0.0-beta4\n\n[1.0.0-beta3]: https://github.com/an-tao/drogon/compare/v1.0.0-beta2...v1.0.0-beta3\n\n[1.0.0-beta2]: https://github.com/an-tao/drogon/compare/v1.0.0-beta1...v1.0.0-beta2\n\n[1.0.0-beta1]: https://github.com/an-tao/drogon/releases/tag/v1.0.0-beta1\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019-2023 An Tao\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "![](https://github.com/an-tao/drogon/wiki/images/drogon-white17.jpg)\n\n[![Build Status](https://github.com/drogonframework/drogon/actions/workflows/cmake.yml/badge.svg?branch=master)](https://github.com/drogonframework/drogon/actions)\n[![Conan Center](https://img.shields.io/conan/v/drogon)](https://conan.io/center/recipes/drogon)\n[![Join the telegram group at https://t.me/joinchat/_mMNGv0748ZkMDAx](https://img.shields.io/badge/Telegram-2CA5E0?style=flat&logo=telegram&logoColor=white)](https://t.me/joinchat/_mMNGv0748ZkMDAx)\n[![Join our Discord](https://dcbadge.vercel.app/api/server/3DvHY6Ewuj?style=flat)](https://discord.gg/3DvHY6Ewuj)\n[![Docker image](https://img.shields.io/badge/Docker-image-blue.svg)](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon)\n\nEnglish | [简体中文](./README.zh-CN.md) | [繁體中文](./README.zh-TW.md)\n### Overview\n**Drogon** is a C++17/20 based HTTP application framework. Drogon can be used to easily build various types of web application server programs using C++. **Drogon** is the name of a dragon from the American TV series *Game of Thrones*, which I really enjoy.\n\nDrogon is a cross-platform framework, It supports Linux, macOS, FreeBSD, OpenBSD, HaikuOS, and Windows. Its main features are as follows:\n\n* Use a non-blocking I/O network lib based on epoll (kqueue under macOS/FreeBSD) to provide high-concurrency, high-performance network IO, please visit the [TFB Tests Results](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite) for more details;\n* Provide a completely asynchronous programming mode;\n* Support Http1.0/1.1 (server side and client side);\n* Based on template, a simple reflection mechanism is implemented to completely decouple the main program framework, controllers and views.\n* Support cookies and built-in sessions;\n* Support back-end rendering, the controller generates the data to the view to generate the Html page. Views are described by CSP template files, C++ codes are embedded into Html pages through CSP tags. And the drogon command-line tool automatically generates the C++ code files for compilation;\n* Support view page dynamic loading (dynamic compilation and loading at runtime);\n* Provide a convenient and flexible routing solution from the path to the controller handler;\n* Support filter chains to facilitate the execution of unified logic (such as login verification, Http Method constraint verification, etc.) before handling HTTP requests;\n* Support https (based on OpenSSL);\n* Support WebSocket (server side and client side);\n* Support JSON format request and response, very friendly to the Restful API application development;\n* Support file download and upload;\n* Support gzip, brotli compression transmission;\n* Support pipelining;\n* Provide a lightweight command line tool, drogon_ctl, to simplify the creation of various classes in Drogon and the generation of view code;\n* Support non-blocking I/O based asynchronously reading and writing database (PostgreSQL and MySQL(MariaDB) database);\n* Support asynchronously reading and writing sqlite3 database based on thread pool;\n* Support Redis with asynchronous reading and writing;\n* Support ARM Architecture;\n* Provide a convenient lightweight ORM implementation that supports for regular object-to-database bidirectional mapping;\n* Support plugins which can be installed by the configuration file at load time;\n* Support AOP with built-in joinpoints.\n* Support C++ coroutines\n\n## A very simple example\n\nUnlike most C++ frameworks, the main program of the drogon application can be kept clean and simple. Drogon uses a few tricks to decouple controllers from the main program. The routing settings of controllers can be done through macros or configuration file.\n\nBelow is the main program of a typical drogon application:\n\n```c++\n#include <drogon/drogon.h>\nusing namespace drogon;\nint main()\n{\n    app().setLogPath(\"./\")\n         .setLogLevel(trantor::Logger::kWarn)\n         .addListener(\"0.0.0.0\", 80)\n         .setThreadNum(16)\n         .enableRunAsDaemon()\n         .run();\n}\n```\n\nIt can be further simplified by using configuration file as follows:\n\n```c++\n#include <drogon/drogon.h>\nusing namespace drogon;\nint main()\n{\n    app().loadConfigFile(\"./config.json\").run();\n}\n```\n\nDrogon provides some interfaces for adding controller logic directly in the main() function, for example, user can register a handler like this in Drogon:\n\n```c++\napp().registerHandler(\"/test?username={name}\",\n                    [](const HttpRequestPtr& req,\n                       std::function<void (const HttpResponsePtr &)> &&callback,\n                       const std::string &name)\n                    {\n                        Json::Value json;\n                        json[\"result\"]=\"ok\";\n                        json[\"message\"]=std::string(\"hello,\")+name;\n                        auto resp=HttpResponse::newHttpJsonResponse(json);\n                        callback(resp);\n                    },\n                    {Get,\"LoginFilter\"});\n```\n\nWhile such interfaces look intuitive, they are not suitable for complex business logic scenarios. Assuming there are tens or even hundreds of handlers that need to be registered in the framework, isn't it a better practice to implement them separately in their respective classes? So unless your logic is very simple, we don't recommend using above interfaces. Instead, we can create an HttpSimpleController as follows:\n\n```c++\n/// The TestCtrl.h file\n#pragma once\n#include <drogon/HttpSimpleController.h>\nusing namespace drogon;\nclass TestCtrl:public drogon::HttpSimpleController<TestCtrl>\n{\npublic:\n    void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;\n    PATH_LIST_BEGIN\n    PATH_ADD(\"/test\",Get);\n    PATH_LIST_END\n};\n\n/// The TestCtrl.cc file\n#include \"TestCtrl.h\"\nvoid TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr& req,\n                                      std::function<void (const HttpResponsePtr &)> &&callback)\n{\n    //write your application logic here\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setBody(\"<p>Hello, world!</p>\");\n    resp->setExpiredTime(0);\n    callback(resp);\n}\n```\n\n**Most of the above programs can be automatically generated by the command line tool `drogon_ctl` provided by drogon** (The command is `drogon_ctl create controller TestCtrl`). All the user needs to do is add their own business logic. In the example, the controller returns a `Hello, world!` string when the client accesses the `http://ip/test` URL.\n\nFor JSON format response, we create the controller as follows:\n\n```c++\n/// The header file\n#pragma once\n#include <drogon/HttpSimpleController.h>\nusing namespace drogon;\nclass JsonCtrl : public drogon::HttpSimpleController<JsonCtrl>\n{\n  public:\n    void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;\n    PATH_LIST_BEGIN\n    //list path definitions here;\n    PATH_ADD(\"/json\", Get);\n    PATH_LIST_END\n};\n\n/// The source file\n#include \"JsonCtrl.h\"\nvoid JsonCtrl::asyncHandleHttpRequest(const HttpRequestPtr &req,\n                                      std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    Json::Value ret;\n    ret[\"message\"] = \"Hello, World!\";\n    auto resp = HttpResponse::newHttpJsonResponse(ret);\n    callback(resp);\n}\n```\n\nLet's go a step further and create a demo RESTful API with the HttpController class, as shown below (Omit the source file):\n\n```c++\n/// The header file\n#pragma once\n#include <drogon/HttpController.h>\nusing namespace drogon;\nnamespace api\n{\nnamespace v1\n{\nclass User : public drogon::HttpController<User>\n{\n  public:\n    METHOD_LIST_BEGIN\n    //use METHOD_ADD to add your custom processing function here;\n    METHOD_ADD(User::getInfo, \"/{id}\", Get);                  //path is /api/v1/User/{arg1}\n    METHOD_ADD(User::getDetailInfo, \"/{id}/detailinfo\", Get);  //path is /api/v1/User/{arg1}/detailinfo\n    METHOD_ADD(User::newUser, \"/{name}\", Post);                 //path is /api/v1/User/{arg1}\n    METHOD_LIST_END\n    //your declaration of processing function maybe like this:\n    void getInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;\n    void getDetailInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;\n    void newUser(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, std::string &&userName);\n  public:\n    User()\n    {\n        LOG_DEBUG << \"User constructor!\";\n    }\n};\n} // namespace v1\n} // namespace api\n```\n\nAs you can see, users can use the `HttpController` to map paths and parameters at the same time. This is a very convenient way to create a RESTful API application.\n\nIn addition, you can also find that all handler interfaces are in asynchronous mode, where the response is returned by a callback object. This design is for performance reasons because in asynchronous mode the drogon application can handle a large number of concurrent requests with a small number of threads.\n\nAfter compiling all of the above source files, we get a very simple web application. This is a good start. **For more information, please visit the [documentation](https://drogonframework.github.io/drogon-docs/#/) on GitHub**.\n\n## Cross-compilation\n\nDrogon supports cross-compilation, you should define the `CMAKE_SYSTEM_NAME` in toolchain file, for example:\n    \n```cmake\nset(CMAKE_SYSTEM_NAME Linux)\nset(CMAKE_SYSTEM_PROCESSOR arm)\n```\n\nYou can disable building options for examples and drogon_ctl by settings `BUILD_EXAMPLES` and `BUILD_CTL` to `OFF` in the toolchain file.\n\n## Building options\n\nDrogon provides some building options, you can enable or disable them by setting the corresponding variables to `ON` or `OFF` in the cmake command line, cmake file etc...\n\n| Option name | Description | Default value |\n| :--- | :--- | :--- |\n| BUILD_CTL | Build drogon_ctl | ON |\n| BUILD_EXAMPLES | Build examples | ON |\n| BUILD_ORM | Build orm | ON |\n| COZ_PROFILING | Use coz for profiling | OFF |\n| BUILD_SHARED_LIBS | Build drogon as a shared lib | OFF |\n| BUILD_DOC | Build Doxygen documentation | OFF |\n| BUILD_BROTLI | Build Brotli | ON |\n| BUILD_YAML_CONFIG | Build yaml config | ON |\n| USE_SUBMODULE | Use trantor as a submodule | ON |\n\n\n## Contributions\n\nThis project exists thanks to all the people who contribute code. \n\n<a href=\"https://github.com/drogonframework/drogon/graphs/contributors\"><img src=\"https://contributors-svg.opencollective.com/drogon/contributors.svg?width=890&button=false\" alt=\"Code contributors\" /></a>\n\nEvery contribution is welcome. Please refer to the [contribution guidelines](CONTRIBUTING.md) for more information.\n"
  },
  {
    "path": "README.zh-CN.md",
    "content": "![](https://github.com/an-tao/drogon/wiki/images/drogon-white17.jpg)\n\n[![Build Status](https://github.com/drogonframework/drogon/actions/workflows/cmake.yml/badge.svg?branch=master)](https://github.com/drogonframework/drogon/actions)\n[![Conan Center](https://img.shields.io/conan/v/drogon)](https://conan.io/center/recipes/drogon)\n[![Join the telegram group at https://t.me/joinchat/_mMNGv0748ZkMDAx](https://img.shields.io/badge/Telegram-2CA5E0?style=flat&logo=telegram&logoColor=white)](https://t.me/joinchat/_mMNGv0748ZkMDAx)\n[![Join our Discord](https://dcbadge.vercel.app/api/server/3DvHY6Ewuj?style=flat)](https://discord.gg/3DvHY6Ewuj)\n[![Docker image](https://img.shields.io/badge/Docker-image-blue.svg)](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon)\n\n[English](./README.md) | 简体中文 | [繁體中文](./README.zh-TW.md)\n\n**Drogon**是一个基于C++17/20的Http应用框架，使用Drogon可以方便的使用C++构建各种类型的Web应用服务端程序。\n本版本库是github上[Drogon工程](https://github.com/an-tao/drogon)的镜像库。**Drogon**是作者非常喜欢的美剧《权力的游戏》中的一条龙的名字(汉译作卓耿)，和龙有关但并不是dragon的误写，为了不至于引起不必要的误会这里说明一下。\n\nDrogon是一个跨平台框架，它支持Linux，也支持macOS、FreeBSD，OpenBSD，HaikuOS，和Windows。它的主要特点如下：\n\n* 网络层使用基于epoll(macOS/FreeBSD下是kqueue)的非阻塞IO框架，提供高并发、高性能的网络IO。详细请见[TFB Tests Results](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite)；\n* 全异步编程模式；\n* 支持Http1.0/1.1(server端和client端)；\n* 基于template实现了简单的反射机制，使主程序框架、控制器(controller)和视图(view)完全解耦；\n* 支持cookies和内建的session；\n* 支持后端渲染，把控制器生成的数据交给视图生成Html页面，视图由CSP模板文件描述，通过CSP标签把C++代码嵌入到Html页面，由drogon的命令行工具在编译阶段自动生成C++代码并编译；\n* 支持运行期的视图页面动态加载(动态编译和加载so文件)；\n* 非常方便灵活的路径(path)到控制器处理函数(handler)的映射方案；\n* 支持过滤器(filter)链，方便在控制器之前执行统一的逻辑(如登录验证、Http Method约束验证等)；\n* 支持https(基于OpenSSL实现);\n* 支持websocket(server端和client端);\n* 支持Json格式请求和应答, 对Restful API应用开发非常友好;\n* 支持文件下载和上传,支持sendfile系统调用；\n* 支持gzip/brotli压缩传输；\n* 支持pipelining；\n* 提供一个轻量的命令行工具drogon_ctl，帮助简化各种类的创建和视图代码的生成过程；\n* 基于非阻塞IO实现的异步数据库读写，目前支持PostgreSQL和MySQL(MariaDB)数据库；\n* 基于线程池实现sqlite3数据库的异步读写，提供与上文数据库相同的接口；\n* 支持Redis异步读写；\n* 支持ARM架构；\n* 方便的轻量级ORM实现，支持常规的对象到数据库的双向映射操作；\n* 支持插件，可通过配置文件在加载期动态拆装；\n* 支持内建插入点的AOP\n* 支持C++协程\n\n## 一个非常简单的例子\n\n不像大多数C++框架那样，drogon的主程序可以保持非常简单。 Drogon使用了一些小技巧使主程序和控制器解耦合. 控制器的路由设置可以在控制器类中定义或者配置文件中完成.\n\n下面是一个典型的主程序的样子:\n\n```c++\n#include <drogon/drogon.h>\nusing namespace drogon;\nint main()\n{\n    app().setLogPath(\"./\")\n         .setLogLevel(trantor::Logger::kWarn)\n         .addListener(\"0.0.0.0\", 80)\n         .setThreadNum(16)\n         .enableRunAsDaemon()\n         .run();\n}\n```\n\n如果使用配置文件，可以进一步简化成如下的样子:\n\n```c++\n#include <drogon/drogon.h>\nusing namespace drogon;\nint main()\n{\n    app().loadConfigFile(\"./config.json\").run();\n}\n```\n\n当然，Drogon也提供了一些接口，使用户可以在main()函数中直接添加控制器逻辑，比如，用户可以注册一个lambda处理器到drogon框架中，如下所示：\n\n```c++\napp().registerHandler(\"/test?username={name}\",\n                    [](const HttpRequestPtr& req,\n                       std::function<void (const HttpResponsePtr &)> &&callback,\n                       const std::string &name)\n                    {\n                        Json::Value json;\n                        json[\"result\"]=\"ok\";\n                        json[\"message\"]=std::string(\"hello,\")+name;\n                        auto resp=HttpResponse::newHttpJsonResponse(json);\n                        callback(resp);\n                    },\n                    {Get,\"LoginFilter\"});\n```\n\n\n这看起来是很方便，但是这并不适用于复杂的应用，试想假如有数十个或者数百个处理函数要注册进框架，main()函数将膨胀到不可读的程度。显然，让每个包含处理函数的类在自己的定义中完成注册是更好的选择。所以，除非你的应用逻辑非常简单，我们不推荐使用上述接口，更好的实践是，我们可以创建一个HttpSimpleController对象，如下：\n\n\n```c++\n/// The TestCtrl.h file\n#pragma once\n#include <drogon/HttpSimpleController.h>\nusing namespace drogon;\nclass TestCtrl:public drogon::HttpSimpleController<TestCtrl>\n{\npublic:\n    void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;\n    PATH_LIST_BEGIN\n    PATH_ADD(\"/test\",Get);\n    PATH_LIST_END\n};\n\n/// The TestCtrl.cc file\n#include \"TestCtrl.h\"\nvoid TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr& req,\n                                      std::function<void (const HttpResponsePtr &)> &&callback)\n{\n    //write your application logic here\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setBody(\"<p>Hello, world!</p>\");\n    resp->setExpiredTime(0);\n    callback(resp);\n}\n```\n\n**上面程序的大部分代码都可以由`drogon_ctl`命令创建**（这个命令是`drogon_ctl create controller TestCtr`）。用户所需做的就是添加自己的业务逻辑。在这个例子中，当客户端访问URL`http://ip/test`时，控制器简单的返回了一个`Hello, world!`页面。\n\n对于JSON格式的响应，我们可以像下面这样创建控制器：\n\n```c++\n/// The header file\n#pragma once\n#include <drogon/HttpSimpleController.h>\nusing namespace drogon;\nclass JsonCtrl : public drogon::HttpSimpleController<JsonCtrl>\n{\n  public:\n    void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;\n    PATH_LIST_BEGIN\n    //list path definitions here;\n    PATH_ADD(\"/json\", Get);\n    PATH_LIST_END\n};\n\n/// The source file\n#include \"JsonCtrl.h\"\nvoid JsonCtrl::asyncHandleHttpRequest(const HttpRequestPtr &req,\n                                      std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    Json::Value ret;\n    ret[\"message\"] = \"Hello, World!\";\n    auto resp = HttpResponse::newHttpJsonResponse(ret);\n    callback(resp);\n}\n```\n\n让我们更进一步，通过HttpController类创建一个RESTful API的例子，如下所示（忽略了实现文件）：\n\n```c++\n/// The header file\n#pragma once\n#include <drogon/HttpController.h>\nusing namespace drogon;\nnamespace api\n{\nnamespace v1\n{\nclass User : public drogon::HttpController<User>\n{\n  public:\n    METHOD_LIST_BEGIN\n    //use METHOD_ADD to add your custom processing function here;\n    METHOD_ADD(User::getInfo, \"/{id}\", Get);                  //path is /api/v1/User/{arg1}\n    METHOD_ADD(User::getDetailInfo, \"/{id}/detailinfo\", Get);  //path is /api/v1/User/{arg1}/detailinfo\n    METHOD_ADD(User::newUser, \"/{name}\", Post);                 //path is /api/v1/User/{arg1}\n    METHOD_LIST_END\n    //your declaration of processing function maybe like this:\n    void getInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;\n    void getDetailInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;\n    void newUser(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, std::string &&userName);\n  public:\n    User()\n    {\n        LOG_DEBUG << \"User constructor!\";\n    }\n};\n} // namespace v1\n} // namespace api\n```\n\n如你所见，通过`HttpController`类，用户可以同时映射路径和路径参数，这对RESTful API应用来说非常方便。\n\n另外，你可以发现前面所有的处理函数接口都是异步的，处理器的响应是通过回调对象返回的。这种设计是出于对高性能的考虑，因为在异步模式下，可以使用少量的线程（比如和处理器核心数相等的线程）处理大量的并发请求。\n\n编译上述的所有源文件后，我们得到了一个非常简单的web应用程序，这是一个不错的开始。**请访问GitHub上的[文档](https://drogonframework.github.io/drogon-docs/#/CHN/CHN-01-%E6%A6%82%E8%BF%B0)**\n\n## 贡献方式\n\n欢迎您的贡献。 请阅读[贡献指南](CONTRIBUTING.md)以获取更多的信息。\n\n<a href=\"https://github.com/drogonframework/drogon/graphs/contributors\"><img src=\"https://contributors-svg.opencollective.com/drogon/contributors.svg?width=890&button=false\" alt=\"Code contributors\" /></a>\n\n## QQ交流群：1137909452\n\n欢迎交流探讨。\n\n## 微信公众号：\n\n![](https://github.com/an-tao/drogon/wiki/images/qrcode_wechat.jpg)\n\n会不定期推送一些Drogon的使用技巧和更新信息，欢迎关注。"
  },
  {
    "path": "README.zh-TW.md",
    "content": "![](https://github.com/an-tao/drogon/wiki/images/drogon-white17.jpg)\n\n[![Build Status](https://github.com/drogonframework/drogon/actions/workflows/cmake.yml/badge.svg?branch=master)](https://github.com/drogonframework/drogon/actions)\n[![Conan Center](https://img.shields.io/conan/v/drogon)](https://conan.io/center/recipes/drogon)\n[![Join the telegram group at https://t.me/joinchat/_mMNGv0748ZkMDAx](https://img.shields.io/badge/Telegram-2CA5E0?style=flat&logo=telegram&logoColor=white)](https://t.me/joinchat/_mMNGv0748ZkMDAx)\n[![Join our Discord](https://dcbadge.vercel.app/api/server/3DvHY6Ewuj?style=flat)](https://discord.gg/3DvHY6Ewuj)\n[![Docker image](https://img.shields.io/badge/Docker-image-blue.svg)](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon)\n\n[English](./README.md) | [简体中文](./README.zh-CN.md) | 繁體中文\n\n**Drogon** 是一個基於 C++17/20 的 HTTP 應用程式框架，使用 Drogon 可以方便地用 C++ 建立各種類型的 Web App 伺服器端程式。\n\n這個版本庫是 GitHub 上 [Drogon](https://github.com/an-tao/drogon) 的鏡像庫。**Drogon** 是作者非常喜歡的美劇《冰與火之歌：權力遊戲》中的一條龍的名字（中文譯作卓耿），和龍有關但並不是 dragon 的誤寫，為了避免不必要的誤會在此說明。\n\nDrogon 是一個跨平台框架，支援 Linux、macOS、FreeBSD/OpenBSD、HaikuOS 和 Windows。主要特點如下：\n\n* 網路層使用基於 epoll（macOS/FreeBSD 下是 kqueue）的非阻塞 IO 框架，提供高並行、高效能的網路 IO。詳細請見 [TFB Tests Results](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite)；\n* 完全非同步的程式撰寫邏輯；\n* 支援 HTTP 1.0/1.1（伺服器端和用戶端）；\n* 基於樣板（template）實作的簡單反射機制，使主程式框架、控制器（controller）和視圖（view）完全解耦；\n* 支援 cookies 和內建的 session；\n* 支援後端算繪，將控制器產生的資料交給視圖產生 HTML 頁面，視圖由 CSP 樣板檔案描述，透過 CSP 標籤將 C++ 程式碼嵌入 HTML 頁面，由 drogon 的命令列工具在編譯階段自動產生 C++ 程式碼並編譯；\n* 支援執行期的視圖頁面動態載入（動態編譯和載入 so 檔案）；\n* 非常方便靈活的路徑（path）到控制器處理函式（handler）的對應方案；\n* 支援過濾器（filter）鏈，方便在控制器之前執行統一的邏輯（如登入驗證、HTTP Method 限制驗證等）；\n* 支援 HTTPS（基於 OpenSSL）；\n* 支援 WebSocket（伺服器端和用戶端）；\n* 支援 JSON 格式的請求和回應，方便開發 RESTful API；\n* 支援檔案下載和上傳，支援 `sendfile` 系統呼叫；\n* 支援 Gzip/Brotli 壓縮傳輸；\n* 支援 pipelining；\n* 提供輕量的命令列工具 `drogon_ctl`，幫助簡化各種類別的建立和視圖程式碼的產生過程；\n* 非同步的讀寫資料庫，目前支援 PostgreSQL 和 MySQL（MariaDB）資料庫；\n* 支援非同步讀寫 Redis；\n* 基於執行緒池實作 sqlite3 資料庫的非同步讀寫，提供與上述資料庫相同的介面；\n* 支援 ARM 架構；\n* 方便的輕量級 ORM 實現，一般物件到資料庫的雙向對應；\n* 支援外掛，可透過設定檔案在載入時動態載入；\n* 支援內建插入點的 AOP；\n* 支援 C++ coroutine。\n\n## 一個非常簡單的例子\n\n不像大多數 C++ 框架，drogon 的主程式可以非常簡單。Drogon 使用了一些小技巧使主程式和控制器解耦。控制器的路由設定可以在控制器類別中定義或在設定檔案中完成。\n\n下面是一個典型主程式的樣子：\n\n```c++\n#include <drogon/drogon.h>\nusing namespace drogon;\nint main()\n{\n    app().setLogPath(\"./\")\n         .setLogLevel(trantor::Logger::kWarn)\n         .addListener(\"0.0.0.0\", 80)\n         .setThreadNum(16)\n         .enableRunAsDaemon()\n         .run();\n}\n```\n\n如果使用設定檔案，可以進一步簡化成：\n\n```c++\n#include <drogon/drogon.h>\nusing namespace drogon;\nint main()\n{\n    app().loadConfigFile(\"./config.json\").run();\n}\n```\n\n當然，Drogon 也提供了一些函式，讓使用者可以在 `main()` 函式中直接加入控制器邏輯，例如，使用者可以註冊一個 lambda 處理常式到 drogon 框架中，如下所示：\n\n```c++\napp().registerHandler(\"/test?username={name}\",\n                    [](const HttpRequestPtr& req,\n                       std::function<void (const HttpResponsePtr &)> &&callback,\n                       const std::string &name)\n                    {\n                        Json::Value json;\n                        json[\"result\"]=\"ok\";\n                        json[\"message\"]=std::string(\"hello,\")+name;\n                        auto resp=HttpResponse::newHttpJsonResponse(json);\n                        callback(resp);\n                    },\n                    {Get,\"LoginFilter\"});\n```\n\n這看起來很方便，但不適用於複雜的場景，試想如果有數十個或數百個處理函式要註冊進框架，`main()` 函式將變得難以閱讀。顯然，讓每個包含處理函式的類別在自己的定義中完成註冊是更好的選擇。所以，除非你的應用邏輯非常簡單，我們不建議使用上述介面，更好的做法是建立一個 HttpSimpleController 類別，如下：\n\n```c++\n/// The TestCtrl.h file\n#pragma once\n#include <drogon/HttpSimpleController.h>\nusing namespace drogon;\nclass TestCtrl:public drogon::HttpSimpleController<TestCtrl>\n{\npublic:\n    void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;\n    PATH_LIST_BEGIN\n    PATH_ADD(\"/test\",Get);\n    PATH_LIST_END\n};\n\n/// The TestCtrl.cc file\n#include \"TestCtrl.h\"\nvoid TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr& req,\n                                      std::function<void (const HttpResponsePtr &)> &&callback)\n{\n    //write your application logic here\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setBody(\"<p>Hello, world!</p>\");\n    resp->setExpiredTime(0);\n    callback(resp);\n}\n```\n\n**上述程式的大部分程式碼都可以由 `drogon_ctl` 指令產生**（使用指令 `drogon_ctl create controller TestCtr`）。使用者只需要加入自己的業務邏輯。在這個範例中，當用戶端存取 URL `http://ip/test` 時，控制器簡單地回傳一個 `Hello, world!` 頁面。\n\n對於 JSON 格式的回應，我們可以這樣建立控制器：\n\n```c++\n/// The header file\n#pragma once\n#include <drogon/HttpSimpleController.h>\nusing namespace drogon;\nclass JsonCtrl : public drogon::HttpSimpleController<JsonCtrl>\n{\n  public:\n    void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;\n    PATH_LIST_BEGIN\n    //list path definitions here;\n    PATH_ADD(\"/json\", Get);\n    PATH_LIST_END\n};\n\n/// The source file\n#include \"JsonCtrl.h\"\nvoid JsonCtrl::asyncHandleHttpRequest(const HttpRequestPtr &req,\n                                      std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    Json::Value ret;\n    ret[\"message\"] = \"Hello, World!\";\n    auto resp = HttpResponse::newHttpJsonResponse(ret);\n    callback(resp);\n}\n```\n\n讓我們更進一步，透過 HttpController 類別建立一個 RESTful API 的範例，如下所示（省略實作檔案）：\n\n```c++\n/// The header file\n#pragma once\n#include <drogon/HttpController.h>\nusing namespace drogon;\nnamespace api\n{\nnamespace v1\n{\nclass User : public drogon::HttpController<User>\n{\n  public:\n    METHOD_LIST_BEGIN\n    //use METHOD_ADD to add your custom processing function here;\n    METHOD_ADD(User::getInfo, \"/{id}\", Get);                  //path is /api/v1/User/{arg1}\n    METHOD_ADD(User::getDetailInfo, \"/{id}/detailinfo\", Get);  //path is /api/v1/User/{arg1}/detailinfo\n    METHOD_ADD(User::newUser, \"/{name}\", Post);                 //path is /api/v1/User/{arg1}\n    METHOD_LIST_END\n    //your declaration of processing function maybe like this:\n    void getInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;\n    void getDetailInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;\n    void newUser(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, std::string &&userName);\n  public:\n    User()\n    {\n        LOG_DEBUG << \"User constructor!\";\n    }\n};\n} // namespace v1\n} // namespace api\n```\n\n如你所見，透過 `HttpController` 類別，使用者可以同時對應路徑和路徑參數，這對 RESTful API 應用來說非常方便。\n\n另外，你可以發現前面所有的處理函式介面都是非同步的，處理器的回應是透過回呼物件回傳的。這種設計是考慮到效能，因為在非同步模式下，可以使用少量的執行緒（例如和處理器核心數相等的執行緒）處理大量的並行請求。\n\n編譯上述所有原始檔案後，我們得到了一個非常簡單的網頁應用程式，這是一個不錯的開始。**請瀏覽 GitHub 上的[文件](https://drogonframework.github.io/drogon-docs/#/CHN/CHN-01-%E6%A6%82%E8%BF%B0)**\n\n## 貢獻方式\n\n歡迎您的貢獻。請閱讀[貢獻指南](CONTRIBUTING.md)以取得更多資訊。\n\n<a href=\"https://github.com/drogonframework/drogon/graphs/contributors\"><img src=\"https://contributors-svg.opencollective.com/drogon/contributors.svg?width=890&button=false\" alt=\"Code contributors\" /></a>\n\n## QQ 交流群：1137909452\n\n歡迎交流討論。"
  },
  {
    "path": "build.sh",
    "content": "#!/usr/bin/env bash\n\n#build drogon\nfunction build_drogon() {\n\n    #Update the submodule and initialize\n    git submodule update --init\n    \n    #Remove the config.h generated by the old version of drogon.\n    rm -f lib/inc/drogon/config.h\n\n    #Save current directory\n    current_dir=\"${PWD}\"\n\n    #The folder in which we will build drogon\n    build_dir='./build'\n    if [ -d $build_dir ]; then\n        echo \"Deleted folder: ${build_dir}\"\n        rm -rf $build_dir\n    fi\n\n    #Create building folder\n    echo \"Created building folder: ${build_dir}\"\n    mkdir $build_dir\n\n    echo \"Entering folder: ${build_dir}\"\n    cd $build_dir\n\n    echo \"Start building drogon ...\"\n    if [ $1 -eq 1 ]; then\n        cmake .. -DBUILD_TESTING=YES $cmake_gen\n    elif [ $1 -eq 2 ]; then\n        cmake .. -DBUILD_TESTING=YES -DBUILD_SHARED_LIBS=ON -DCMAKE_CXX_VISIBILITY_PRESET=hidden -DCMAKE_VISIBILITY_INLINES_HIDDEN=1 $cmake_gen\n    else\n        cmake .. -DCMAKE_BUILD_TYPE=release $cmake_gen\n    fi\n\n    #If errors then exit\n    if [ \"$?\" != \"0\" ]; then\n        exit -1\n    fi\n    \n    $make_program $make_flags\n    \n    #If errors then exit\n    if [ \"$?\" != \"0\" ]; then\n        exit -1\n    fi\n\n    echo \"Installing ...\"\n    $make_program install\n\n    #Go back to the current directory\n    cd $current_dir\n    #Ok!\n}\n\nmake_program=make\nmake_flags=''\ncmake_gen=''\nparallel=1\n\ncase $(uname) in\n FreeBSD)\n    nproc=$(sysctl -n hw.ncpu)\n    ;;\n Darwin)\n    nproc=$(sysctl -n hw.ncpu) # sysctl -n hw.ncpu is the equivalent to nproc on macOS.\n    ;;\n *)\n    nproc=$(nproc)\n    ;;\nesac\n\n# simulate ninja's parallelism\ncase nproc in\n 1)\n    parallel=$(( nproc + 1 ))\n    ;;\n 2)\n    parallel=$(( nproc + 1 ))\n    ;;\n *)\n    parallel=$(( nproc + 2 ))\n    ;;\nesac\n\nif [ -f /bin/ninja ]; then\n    make_program=ninja\n    cmake_gen='-GNinja'\nelse\n    make_flags=\"$make_flags -j$parallel\"\nfi\n\nif [ \"X$1\" = \"X-t\" ]; then\n    build_drogon 1\nelif [ \"X$1\" = \"X-tshared\" ]; then\n    build_drogon 2\nelse\n    build_drogon 0\nfi\n"
  },
  {
    "path": "cmake/DrogonUtilities.cmake",
    "content": "# ##############################################################################\n# function drogon_create_views(target source_path output_path\n# [TRUE to use_path_as_namespace] [prefixed namespace])\n# ##############################################################################\nfunction(drogon_create_views arg)\n  if(ARGC LESS 3)\n    message(STATUS \"arguments error when calling drogon_create_views\")\n    return()\n  endif()\n  file(MAKE_DIRECTORY ${ARGV2})\n  file(GLOB_RECURSE SCP_LIST ${ARGV1}/*.csp)\n  foreach(cspFile ${SCP_LIST})\n    file(RELATIVE_PATH\n         inFile\n         ${CMAKE_CURRENT_SOURCE_DIR}\n         ${cspFile})\n    if(ARGC GREATER 3 AND ARGV3)\n      string(REPLACE \"/\"\n                     \"_\"\n                     f1\n                     ${inFile})\n      string(REPLACE \"\\\\\"\n                     \"_\"\n                     f2\n                     ${f1})\n      string(REPLACE \".csp\"\n                     \"\"\n                     outputFile\n                     ${f2})\n      set(p2ns \"\")\n      if(\"${ARGV3}\" STREQUAL \"TRUE\")\n        set(p2ns \"--path-to-namespace\")\n      endif()\n      if ( (ARGC EQUAL 5) AND ( NOT \"${ARGV4}\" STREQUAL \"\") )\n        string(REPLACE \"::\" \"_\" nSpace ${ARGV4})\n        set(outputFile \"${nSpace}_${outputFile}\")\n        set(ns -n ${ARGV4})\n      else()\n        set(ns \"\")\n      endif()\n      add_custom_command(OUTPUT ${ARGV2}/${outputFile}.h ${ARGV2}/${outputFile}.cc\n                         COMMAND drogon_ctl\n                                 ARGS\n                                 create\n                                 view\n                                 ${inFile}\n                                 ${p2ns}\n                                 -o\n                                 ${ARGV2}\n                                 ${ns}\n                         DEPENDS ${cspFile}\n                         WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}\n                         VERBATIM)\n      set(VIEWSRC ${VIEWSRC} ${ARGV2}/${outputFile}.cc)\n    else()\n      get_filename_component(classname ${cspFile} NAME_WE)\n      add_custom_command(OUTPUT ${ARGV2}/${classname}.h ${ARGV2}/${classname}.cc\n                         COMMAND drogon_ctl\n                                 ARGS\n                                 create\n                                 view\n                                 ${inFile}\n                                 -o\n                                 ${ARGV2}\n                         DEPENDS ${cspFile}\n                         WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}\n                         VERBATIM)\n      set(VIEWSRC ${VIEWSRC} ${ARGV2}/${classname}.cc)\n    endif()\n  endforeach()\n  target_sources(${ARGV0} PRIVATE ${VIEWSRC})\nendfunction(drogon_create_views)\n"
  },
  {
    "path": "cmake/Packages.cmake",
    "content": "include(GNUInstallDirs)\n\nset(CPACK_RESOURCE_FILE_LICENSE \"${PROJECT_SOURCE_DIR}/LICENSE\")\nset(CPACK_PACKAGE_CONTACT \"https://github.com/drogonframework/drogon\")\n\nset(CPACK_PACKAGE_NAME \"${PROJECT_NAME}\")\nset(CPACK_PACKAGE_VERSION \"${DROGON_VERSION}\")\nset(CPACK_PACKAGE_DESCRIPTION_SUMMARY \"A C++14/17 based HTTP web application framework running on Linux/macOS/Unix/Windows\")\n\n# DEB\n# Figure out dependencies automatically.\nset(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)\n\n# Should be set automatically, but it is not.\nexecute_process(COMMAND dpkg --print-architecture\n  OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE\n  OUTPUT_STRIP_TRAILING_WHITESPACE)\n\n# The default does not produce valid Debian package names.\nset(CPACK_DEBIAN_FILE_NAME\n  \"${CPACK_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}-0_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb\")\n\n# RPM\nset(CPACK_RPM_PACKAGE_LICENSE \"MIT\")\n\n# Figure out dependencies automatically.\nset(CPACK_RPM_PACKAGE_AUTOREQ ON)\n\n# Should be set automatically, but it is not.\nexecute_process(COMMAND uname -m\n  OUTPUT_VARIABLE CPACK_RPM_PACKAGE_ARCHITECTURE\n  OUTPUT_STRIP_TRAILING_WHITESPACE)\n\nset(CPACK_PACKAGE_FILE_NAME\n    \"${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-0.${CPACK_RPM_PACKAGE_ARCHITECTURE}\")\n\ninclude(CPack)\n"
  },
  {
    "path": "cmake/ParseAndAddDrogonTests.cmake",
    "content": "#==================================================================================================#\n# Adapted and re-written from Catch2 to work with Drogon Test                                      #\n#                                                                                                  #\n#  Usage                                                                                           #\n# 1. make sure this module is in the path or add this otherwise:                                   #\n#    set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} \"${CMAKE_SOURCE_DIR}/cmake_modules/\")              #\n# 2. make sure that you've enabled testing option for the project by the call:                     #\n#    enable_testing()                                                                              #\n# 3. add the lines to the script for testing target (sample CMakeLists.txt):                       #\n#        project(testing_target)                                                                   #\n#        enable_testing()                                                                          #\n#                                                                                                  #\n#        file(GLOB SOURCE_FILES \"*.cpp\")                                                           #\n#        add_executable(${PROJECT_NAME} ${SOURCE_FILES})                                           #\n#                                                                                                  #\n#        include(ParseAndAddDrogonTests)                                                          #\n#        ParseAndAddDrogonTests(${PROJECT_NAME})                                                   #\n#==================================================================================================#\n\ncmake_minimum_required(VERSION 3.5...3.31)\n\n# This removes the contents between\n#  - block comments (i.e. /* ... */) \n#  - full line comments (i.e. // ... ) \n# contents have been read into '${CppCode}'.\n# !keep partial line comments\nfunction(RemoveComments CppCode)\n  string(ASCII 2 CMakeBeginBlockComment)\n  string(ASCII 3 CMakeEndBlockComment)\n  string(REGEX REPLACE \"/\\\\*\" \"${CMakeBeginBlockComment}\" ${CppCode} \"${${CppCode}}\")\n  string(REGEX REPLACE \"\\\\*/\" \"${CMakeEndBlockComment}\" ${CppCode} \"${${CppCode}}\")\n  string(REGEX REPLACE \"${CMakeBeginBlockComment}[^${CMakeEndBlockComment}]*${CMakeEndBlockComment}\" \"\" ${CppCode} \"${${CppCode}}\")\n  string(REGEX REPLACE \"\\n[ \\t]*//+[^\\n]+\" \"\\n\" ${CppCode} \"${${CppCode}}\")\n\n  set(${CppCode} \"${${CppCode}}\" PARENT_SCOPE)\nendfunction()\n\n# Worker function\nfunction(ParseFile SourceFile TestTarget)\n\tset(FullSourcePath ${CMAKE_CURRENT_SOURCE_DIR}/${SourceFile})\n    if(NOT EXISTS ${FullSourcePath})\n        return()\n    endif()\n    file(STRINGS ${FullSourcePath} Contents NEWLINE_CONSUME)\n\n    # Remove block and fullline comments\n    RemoveComments(Contents)\n\n    # Find definition of test names\n    string(REGEX MATCHALL \"[ \\t]*DROGON_TEST[ \\t]*\\\\\\([a-zA-Z0-9_]+\\\\\\)\" Tests \"${Contents}\")\n\n    foreach(TestLine ${Tests})\n        # Strip newlines\n        string(REGEX REPLACE \"\\\\\\\\\\n|\\n\" \"\" TestLine \"${TestLine}\")\n\n        # Get the name of the test\n\t\tstring(REGEX REPLACE \"[ \\t]*DROGON_TEST[ \\t]*\" \"\" TestLine \"${TestLine}\")\n        string(REGEX MATCHALL \"[a-zA-Z0-9_]+\" TestName \"${TestLine}\")\n\n        # Validate that a test name and tags have been provided\n        list(LENGTH TestName TestNameLength)\n        if(NOT TestNameLength EQUAL 1)\n            message(FATAL_ERROR \"${TestName} in ${SourceFile} is not a valid test name.\"\n\t\t\t\t\" Either a bug in the Drogon Test CMake parser or a bug in the test itself\")\n        endif()\n\n        # Add the test and set its properties\n        add_test(NAME \"${TestName}\" COMMAND ${TestTarget} -r ${TestName} ${AdditionalCatchParameters})\n\n    endforeach()\nendfunction()\n\n# entry point\nfunction(ParseAndAddDrogonTests TestTarget)\n    get_target_property(SourceFiles ${TestTarget} SOURCES)\n    foreach(SourceFile ${SourceFiles})\n        ParseFile(${SourceFile} ${TestTarget})\n    endforeach()\nendfunction()"
  },
  {
    "path": "cmake/templates/DrogonConfig.cmake.in",
    "content": "# - Config file for the Drogon package\n# It defines the following variables\n#  DROGON_INCLUDE_DIRS - include directories for Drogon\n#  DROGON_LIBRARIES    - libraries to link against\n#  DROGON_EXECUTABLE   - the drogon_ctl executable\n#  Drogon_FOUND\n# This module defines the following IMPORTED target:\n# Drogon::Drogon\n\n@PACKAGE_INIT@\n\ninclude(CMakeFindDependencyMacro)\nlist(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})\n\nfind_dependency(Jsoncpp REQUIRED)\nfind_dependency(Trantor REQUIRED)\nif(NOT ${CMAKE_SYSTEM_NAME} STREQUAL \"FreeBSD\" AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL \"OpenBSD\" AND NOT WIN32)\nfind_dependency(UUID REQUIRED)\nendif(NOT ${CMAKE_SYSTEM_NAME} STREQUAL \"FreeBSD\" AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL \"OpenBSD\" AND NOT WIN32)\nfind_dependency(ZLIB REQUIRED)\nif(@pg_FOUND@)\nfind_dependency(pg)\nendif()\nif(@SQLite3_FOUND@)\nfind_dependency(SQLite3)\nendif()\nif(@MySQL_FOUND@)\nfind_dependency(MySQL)\nendif()\nif(@Brotli_FOUND@)\nfind_dependency(Brotli)\nendif()\nif(@COZ-PROFILER_FOUND@)\nfind_dependency(coz-profiler)\nendif()\nif(@Hiredis_FOUND@)\nfind_dependency(Hiredis)\nendif()\nif(@yaml-cpp_FOUND@)\nfind_dependency(yaml-cpp)\nendif()\nif(@BUILD_SHARED_LIBS@)\nfind_dependency(Threads)\nendif()\nif(@HAS_STD_FILESYSTEM_PATH@)\nfind_dependency(Filesystem)\nfind_package(Filesystem COMPONENTS Final REQUIRED)\nendif()\n\n\n# Our library dependencies (contains definitions for IMPORTED targets)\n\nget_filename_component(DROGON_CMAKE_DIR \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\nif(NOT TARGET Drogon::Drogon)\n  include(\"${DROGON_CMAKE_DIR}/DrogonTargets.cmake\")\n  include(\"${DROGON_CMAKE_DIR}/DrogonUtilities.cmake\")\n  include(\"${DROGON_CMAKE_DIR}/ParseAndAddDrogonTests.cmake\")\nendif()\n\nget_target_property(DROGON_INCLUDE_DIRS Drogon::Drogon INTERFACE_INCLUDE_DIRECTORIES)\nset(DROGON_LIBRARIES Drogon::Drogon)\nset(DROGON_EXECUTABLE drogon_ctl)\n"
  },
  {
    "path": "cmake/templates/config.h.in",
    "content": "#pragma once\n\n#cmakedefine01 USE_POSTGRESQL\n#cmakedefine01 LIBPQ_SUPPORTS_BATCH_MODE\n#cmakedefine01 USE_MYSQL\n#cmakedefine01 USE_SQLITE3\n#cmakedefine01 HAS_STD_FILESYSTEM_PATH\n#cmakedefine OpenSSL_FOUND\n#cmakedefine Boost_FOUND\n\n#cmakedefine COMPILATION_FLAGS \"@COMPILATION_FLAGS@@DROGON_CXX_STANDARD@\"\n#cmakedefine COMPILER_COMMAND \"@COMPILER_COMMAND@\"\n#cmakedefine COMPILER_ID \"@COMPILER_ID@\"\n\n#cmakedefine INCLUDING_DIRS \"@INCLUDING_DIRS@\"\n"
  },
  {
    "path": "cmake/templates/version.h.in",
    "content": "#pragma once\n\n#define MAJOR @DROGON_MAJOR_VERSION@\n#define MINOR @DROGON_MINOR_VERSION@\n#define PATCH @DROGON_PATCH_VERSION@\n#define DROGON_VERSION \"@DROGON_VERSION_STRING@\"\n#define DROGON_VERSION_SHA1 \"@GIT_SHA1@\"\n"
  },
  {
    "path": "cmake/tests/check_has_std_filesystem_path.cc",
    "content": "#include <filesystem>\r\n\r\nint main()\r\n{\r\n    std::filesystem::path aPath(\"../\");\r\n    return 0;\r\n}\r\n"
  },
  {
    "path": "cmake/tests/normal_uuid_lib_test.cc",
    "content": "#include <uuid.h>\nint main()\n{\n    uuid_t uu;\n    uuid_generate(uu);\n    return 0;\n}"
  },
  {
    "path": "cmake/tests/ossp_uuid_lib_test.cc",
    "content": "#include <uuid.h>\nint main()\n{\n    uuid_t *uuid;\n    uuid_create(&uuid);\n    uuid_make(uuid, UUID_MAKE_V1);\n    return 0;\n}"
  },
  {
    "path": "cmake/tests/test_libpq_batch_mode.cc",
    "content": "#include <libpq-fe.h>\n\nint main()\n{\n    PQenterPipelineMode(NULL);\n    PQexitPipelineMode(NULL);\n    PQpipelineSync(NULL);\n    PQpipelineStatus(NULL);\n}\n"
  },
  {
    "path": "cmake_modules/FindBrotli.cmake",
    "content": "# ***************************************************************************\n#                                  _   _ ____  _\n#  Project                     ___| | | |  _ \\| |\n#                             / __| | | | |_) | |\n#                            | (__| |_| |  _ <| |___\n#                             \\___|\\___/|_| \\_\\_____|\n#\n# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.\n#\n# This software is licensed as described in the file COPYING, which you should\n# have received as part of this distribution. The terms are also available at\n# https://curl.haxx.se/docs/copyright.html.\n#\n# You may opt to use, copy, modify, merge, publish, distribute and/or sell\n# copies of the Software, and permit persons to whom the Software is furnished\n# to do so, under the terms of the COPYING file.\n#\n# This software is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY\n# KIND, either express or implied.\n#\n# ##############################################################################\ninclude(FindPackageHandleStandardArgs)\n\nfind_path(BROTLI_INCLUDE_DIR \"brotli/decode.h\")\n\nfind_library(BROTLICOMMON_LIBRARY NAMES brotlicommon brotlicommon-static)\nfind_library(BROTLIDEC_LIBRARY NAMES brotlidec brotlidec-static)\nfind_library(BROTLIENC_LIBRARY NAMES brotlienc brotlienc-static)\n\nfind_package_handle_standard_args(Brotli\n                                  REQUIRED_VARS\n                                  BROTLIDEC_LIBRARY\n                                  BROTLIENC_LIBRARY\n                                  BROTLICOMMON_LIBRARY\n                                  BROTLI_INCLUDE_DIR\n                                  FAIL_MESSAGE\n                                  \"Could NOT find BROTLI\")\n\nset(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR})\nset(BROTLI_LIBRARIES ${BROTLIDEC_LIBRARY}\n                     ${BROTLIENC_LIBRARY} ${BROTLICOMMON_LIBRARY})\n\nif(Brotli_FOUND)\n  add_library(Brotli_lib INTERFACE IMPORTED)\n  set_target_properties(Brotli_lib\n                        PROPERTIES INTERFACE_INCLUDE_DIRECTORIES\n                                   \"${BROTLI_INCLUDE_DIRS}\"\n                                   INTERFACE_LINK_LIBRARIES\n                                   \"${BROTLI_LIBRARIES}\")\nendif(Brotli_FOUND)\n"
  },
  {
    "path": "cmake_modules/FindFilesystem.cmake",
    "content": "# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying\n# file Copyright.txt or https://cmake.org/licensing for details.\n\n#[=======================================================================[.rst:\n\nFindFilesystem\n##############\n\nThis module supports the C++17 standard library's filesystem utilities. Use the\n:imp-target:`std::filesystem` imported target to\n\nOptions\n*******\n\nThe ``COMPONENTS`` argument to this module supports the following values:\n\n.. find-component:: Experimental\n    :name: fs.Experimental\n\n    Allows the module to find the \"experimental\" Filesystem TS version of the\n    Filesystem library. This is the library that should be used with the\n    ``std::experimental::filesystem`` namespace.\n\n.. find-component:: Final\n    :name: fs.Final\n\n    Finds the final C++17 standard version of the filesystem library.\n\nIf no components are provided, behaves as if the\n:find-component:`fs.Final` component was specified.\n\nIf both :find-component:`fs.Experimental` and :find-component:`fs.Final` are\nprovided, first looks for ``Final``, and falls back to ``Experimental`` in case\nof failure. If ``Final`` is found, :imp-target:`std::filesystem` and all\n:ref:`variables <fs.variables>` will refer to the ``Final`` version.\n\n\nImported Targets\n****************\n\n.. imp-target:: std::filesystem\n\n    The ``std::filesystem`` imported target is defined when any requested\n    version of the C++ filesystem library has been found, whether it is\n    *Experimental* or *Final*.\n\n    If no version of the filesystem library is available, this target will not\n    be defined.\n\n    .. note::\n        This target has ``cxx_std_17`` as an ``INTERFACE``\n        :ref:`compile language standard feature <req-lang-standards>`. Linking\n        to this target will automatically enable C++17 if no later standard\n        version is already required on the linking target.\n\n\n.. _fs.variables:\n\nVariables\n*********\n\n.. variable:: CXX_FILESYSTEM_IS_EXPERIMENTAL\n\n    Set to ``TRUE`` when the :find-component:`fs.Experimental` version of C++\n    filesystem library was found, otherwise ``FALSE``.\n\n.. variable:: CXX_FILESYSTEM_HAVE_FS\n\n    Set to ``TRUE`` when a filesystem header was found.\n\n.. variable:: CXX_FILESYSTEM_HEADER\n\n    Set to either ``filesystem`` or ``experimental/filesystem`` depending on\n    whether :find-component:`fs.Final` or :find-component:`fs.Experimental` was\n    found.\n\n.. variable:: CXX_FILESYSTEM_NAMESPACE\n\n    Set to either ``std::filesystem`` or ``std::experimental::filesystem``\n    depending on whether :find-component:`fs.Final` or\n    :find-component:`fs.Experimental` was found.\n\n\nExamples\n********\n\nUsing `find_package(Filesystem)` with no component arguments:\n\n.. code-block:: cmake\n\n    find_package(Filesystem REQUIRED)\n\n    add_executable(my-program main.cpp)\n    target_link_libraries(my-program PRIVATE std::filesystem)\n\n\n#]=======================================================================]\n\n\nif(TARGET std::filesystem)\n    # This module has already been processed. Don't do it again.\n    return()\nendif()\n\n# Ignore filesystem check if version too low \nif(CMAKE_VERSION VERSION_LESS 3.10)\n    set(CXX_FILESYSTEM_HAVE_FS FALSE CACHE BOOL \"TRUE if we have the C++ filesystem headers\")\n    set(Filesystem_FOUND FALSE CACHE BOOL \"TRUE if we can run a program using std::filesystem\" FORCE)\n    return()\nendif()\n\ncmake_minimum_required(VERSION 3.10)\ninclude(CMakePushCheckState)\ninclude(CheckIncludeFileCXX)\n\n# If we're not cross-compiling, try to run test executables.\n# Otherwise, assume that compile + link is a sufficient check.\nif(CMAKE_CROSSCOMPILING)\n    include(CheckCXXSourceCompiles)\n    macro(_cmcm_check_cxx_source code var)\n        check_cxx_source_compiles(\"${code}\" ${var})\n    endmacro()\nelse()\n    include(CheckCXXSourceRuns)\n    macro(_cmcm_check_cxx_source code var)\n        check_cxx_source_runs(\"${code}\" ${var})\n    endmacro()\nendif()\n\ncmake_push_check_state()\n\nset(CMAKE_REQUIRED_QUIET ${Filesystem_FIND_QUIETLY})\n\n# All of our tests required C++17 or later\nset(BACKUP_CXX_STANDARD \"${CMAKE_CXX_STANDARD}\")\nset(CMAKE_CXX_STANDARD 17)\n\n# Normalize and check the component list we were given\nset(want_components ${Filesystem_FIND_COMPONENTS})\nif(Filesystem_FIND_COMPONENTS STREQUAL \"\")\n    set(want_components Final)\nendif()\n\n# Warn on any unrecognized components\nset(extra_components ${want_components})\nlist(REMOVE_ITEM extra_components Final Experimental)\nforeach(component IN LISTS extra_components)\n    message(WARNING \"Extraneous find_package component for Filesystem: ${component}\")\nendforeach()\n\n# Detect which of Experimental and Final we should look for\nset(find_experimental TRUE)\nset(find_final TRUE)\nif(NOT \"Final\" IN_LIST want_components)\n    set(find_final FALSE)\nendif()\nif(NOT \"Experimental\" IN_LIST want_components)\n    set(find_experimental FALSE)\nendif()\n\nif(find_final)\n    check_include_file_cxx(\"filesystem\" _CXX_FILESYSTEM_HAVE_HEADER)\n    mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER)\n    if(_CXX_FILESYSTEM_HAVE_HEADER)\n        # We found the non-experimental header. Don't bother looking for the\n        # experimental one.\n        set(find_experimental FALSE)\n    endif()\nelse()\n    set(_CXX_FILESYSTEM_HAVE_HEADER FALSE)\nendif()\n\nif(find_experimental)\n    check_include_file_cxx(\"experimental/filesystem\" _CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)\n    mark_as_advanced(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)\nelse()\n    set(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER FALSE)\nendif()\n\nif(_CXX_FILESYSTEM_HAVE_HEADER)\n    set(_have_fs TRUE)\n    set(_fs_header filesystem)\n    set(_fs_namespace std::filesystem)\n    set(_is_experimental FALSE)\nelseif(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)\n    set(_have_fs TRUE)\n    set(_fs_header experimental/filesystem)\n    set(_fs_namespace std::experimental::filesystem)\n    set(_is_experimental TRUE)\nelse()\n    set(_have_fs FALSE)\nendif()\n\nset(CXX_FILESYSTEM_HAVE_FS ${_have_fs} CACHE BOOL \"TRUE if we have the C++ filesystem headers\")\nset(CXX_FILESYSTEM_HEADER ${_fs_header} CACHE STRING \"The header that should be included to obtain the filesystem APIs\")\nset(CXX_FILESYSTEM_NAMESPACE ${_fs_namespace} CACHE STRING \"The C++ namespace that contains the filesystem APIs\")\nset(CXX_FILESYSTEM_IS_EXPERIMENTAL ${_is_experimental} CACHE BOOL \"TRUE if the C++ filesystem library is the experimental version\")\n\nset(_found FALSE)\n\nif(CXX_FILESYSTEM_HAVE_FS)\n    # We have some filesystem library available. Do link checks\n    string(CONFIGURE [[\n        #include <cstdio>\n        #include <@CXX_FILESYSTEM_HEADER@>\n\n        int main() {\n            auto cwd = @CXX_FILESYSTEM_NAMESPACE@::current_path();\n            printf(\"%s\", cwd.generic_string().c_str());\n            return EXIT_SUCCESS;\n        }\n    ]] code @ONLY)\n\n    # HACK: Needed to compile correctly on Yocto Linux\n    if(CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\" OR CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\"\n        OR CMAKE_CXX_COMPILER_ID STREQUAL \"AppleClang\")\n        set(CMAKE_REQUIRED_FLAGS ${prev_req_flags} -std=c++17)\n    endif ()\n    # Check a simple filesystem program without any linker flags\n    _cmcm_check_cxx_source(\"${code}\" CXX_FILESYSTEM_NO_LINK_NEEDED)\n\n    set(can_link ${CXX_FILESYSTEM_NO_LINK_NEEDED})\n\n    if(NOT CXX_FILESYSTEM_NO_LINK_NEEDED)\n        set(prev_libraries ${CMAKE_REQUIRED_LIBRARIES})\n        # Add the libstdc++ flag\n        set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lstdc++fs)\n        _cmcm_check_cxx_source(\"${code}\" CXX_FILESYSTEM_STDCPPFS_NEEDED)\n        set(can_link ${CXX_FILESYSTEM_STDCPPFS_NEEDED})\n        if(NOT CXX_FILESYSTEM_STDCPPFS_NEEDED)\n            # Try the libc++ flag\n            set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lc++fs)\n            _cmcm_check_cxx_source(\"${code}\" CXX_FILESYSTEM_CPPFS_NEEDED)\n            set(can_link ${CXX_FILESYSTEM_CPPFS_NEEDED})\n        endif()\n    endif()\n\n    if(can_link)\n        add_library(std::filesystem INTERFACE IMPORTED)\n        set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_COMPILE_FEATURES cxx_std_17)\n        set(_found TRUE)\n\n        if(CXX_FILESYSTEM_NO_LINK_NEEDED)\n            # Nothing to add...\n        elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED)\n            set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_LINK_LIBRARIES -lstdc++fs)\n        elseif(CXX_FILESYSTEM_CPPFS_NEEDED)\n            set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_LINK_LIBRARIES -lc++fs)\n        endif()\n    endif()\nendif()\n\ncmake_pop_check_state()\n\nset(Filesystem_FOUND ${_found} CACHE BOOL \"TRUE if we can run a program using std::filesystem\" FORCE)\n\nif(Filesystem_FIND_REQUIRED AND NOT Filesystem_FOUND)\n    message(FATAL_ERROR \"Cannot run simple program using std::filesystem\")\nendif()\n\nset(CMAKE_CXX_STANDARD \"${BACKUP_CXX_STANDARD}\")\n"
  },
  {
    "path": "cmake_modules/FindHiredis.cmake",
    "content": "# Try to find hiredis\n# Once done, this will define\n#\n# HIREDIS_FOUND        - system has hiredis\n# HIREDIS_INCLUDE_DIRS - hiredis include directories\n# HIREDIS_LIBRARIES    - libraries need to use hiredis\n\nif (HIREDIS_INCLUDE_DIRS AND HIREDIS_LIBRARIES)\n    set(HIREDIS_FIND_QUIETLY TRUE)\n    set(Hiredis_FOUND TRUE)\nelse ()\n    find_path(\n            HIREDIS_INCLUDE_DIR\n            NAMES hiredis/hiredis.h\n            HINTS ${HIREDIS_ROOT_DIR}\n            PATH_SUFFIXES include)\n\n    find_library(\n            HIREDIS_LIBRARY\n            NAMES hiredis\n            HINTS ${HIREDIS_ROOT_DIR}\n            PATH_SUFFIXES ${CMAKE_INSTALL_LIBDIR})\n\n    set(HIREDIS_INCLUDE_DIRS ${HIREDIS_INCLUDE_DIR})\n    set(HIREDIS_LIBRARIES ${HIREDIS_LIBRARY})\n\n    include(FindPackageHandleStandardArgs)\n    find_package_handle_standard_args(\n            Hiredis DEFAULT_MSG HIREDIS_LIBRARY HIREDIS_INCLUDE_DIR)\n\n    mark_as_advanced(HIREDIS_LIBRARY HIREDIS_INCLUDE_DIR)\nendif ()\n\nif(Hiredis_FOUND)\n    add_library(Hiredis_lib INTERFACE IMPORTED)\n    set_target_properties(Hiredis_lib\n            PROPERTIES INTERFACE_INCLUDE_DIRECTORIES\n            \"${HIREDIS_INCLUDE_DIRS}\"\n            INTERFACE_LINK_LIBRARIES\n            \"${HIREDIS_LIBRARIES}\")\nendif(Hiredis_FOUND)\n\nif(WIN32 AND MINGW)\n        target_link_libraries(Hiredis_lib INTERFACE ws2_32 secur32 crypt32 bcrypt zstd curl shlwapi)\nendif()"
  },
  {
    "path": "cmake_modules/FindJsoncpp.cmake",
    "content": "# Find jsoncpp\n#\n# Find the jsoncpp includes and library\n#\n# if you nee to add a custom library search path, do it via via\n# CMAKE_PREFIX_PATH\n#\n# This module defines JSONCPP_INCLUDE_DIRS, where to find header, etc.\n# JSONCPP_LIBRARIES, the libraries needed to use jsoncpp. JSONCPP_FOUND, If\n# false, do not try to use jsoncpp. \n# Jsoncpp_lib - The imported target library.\n\n# only look in default directories\nfind_path(JSONCPP_INCLUDE_DIRS\n          NAMES json/json.h\n          DOC \"jsoncpp include dir\"\n          PATH_SUFFIXES jsoncpp)\n\nfind_library(JSONCPP_LIBRARIES NAMES jsoncpp DOC \"jsoncpp library\")\n\n# debug library on windows same naming convention as in qt (appending debug\n# library with d) boost is using the same \"hack\" as us with \"optimized\" and\n# \"debug\" if(\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"MSVC\")\n# find_library(JSONCPP_LIBRARIES_DEBUG NAMES jsoncppd DOC \"jsoncpp debug\n# library\") if(\"${JSONCPP_LIBRARIES_DEBUG}\" STREQUAL \"JSONCPP_LIBRARIES_DEBUG-\n# NOTFOUND\") set(JSONCPP_LIBRARIES_DEBUG ${JSONCPP_LIBRARIES}) endif()\n\n# set(JSONCPP_LIBRARIES optimized ${JSONCPP_LIBRARIES} debug\n# ${JSONCPP_LIBRARIES_DEBUG})\n\n# endif()\n\n# handle the QUIETLY and REQUIRED arguments and set JSONCPP_FOUND to TRUE if all\n# listed variables are TRUE, hide their existence from configuration view\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(Jsoncpp\n                                  DEFAULT_MSG\n                                  JSONCPP_INCLUDE_DIRS\n                                  JSONCPP_LIBRARIES)\nmark_as_advanced(JSONCPP_INCLUDE_DIRS JSONCPP_LIBRARIES)\n\nif(Jsoncpp_FOUND)\n  if(NOT EXISTS ${JSONCPP_INCLUDE_DIRS}/json/version.h)\n    message(FATAL_ERROR \"Error: jsoncpp lib is too old.....stop\")\n  endif()\n  if(NOT WIN32)\n    execute_process(\n      COMMAND cat ${JSONCPP_INCLUDE_DIRS}/json/version.h\n      COMMAND grep JSONCPP_VERSION_STRING\n      COMMAND sed -e \"s/.*define/define/\"\n      COMMAND awk \"{ printf \\$3 }\"\n      COMMAND sed -e \"s/\\\"//g\"\n      OUTPUT_VARIABLE jsoncpp_ver)\n    if(NOT Jsoncpp_FIND_QUIETLY)\n      message(STATUS \"jsoncpp version:\" ${jsoncpp_ver})\n    endif()\n    if(jsoncpp_ver LESS 1.7)\n      message(\n        FATAL_ERROR\n          \"jsoncpp lib is too old, please get new version from https://github.com/open-source-parsers/jsoncpp\"\n        )\n    endif(jsoncpp_ver LESS 1.7)\n  endif()\n  if (NOT TARGET Jsoncpp_lib)\n          add_library(Jsoncpp_lib INTERFACE IMPORTED)\n  endif()\n  set_target_properties(Jsoncpp_lib\n                        PROPERTIES INTERFACE_INCLUDE_DIRECTORIES\n                                   \"${JSONCPP_INCLUDE_DIRS}\"\n                                   INTERFACE_LINK_LIBRARIES\n                                   \"${JSONCPP_LIBRARIES}\")\n\nendif(Jsoncpp_FOUND)\n"
  },
  {
    "path": "cmake_modules/FindMySQL.cmake",
    "content": "# --------------------------------------------------------\n# Copyright (C) 1995-2007 MySQL AB\n#\n# This program is free software; you can redistribute it and/or modify it under\n# the terms of version 2 of the GNU General Public License as published by the\n# Free Software Foundation.\n#\n# There are special exceptions to the terms and conditions of the GPL as it is\n# applied to this software. View the full text of the exception in file\n# LICENSE.exceptions in the top-level directory of this software distribution.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin\n# Street, Fifth Floor, Boston, MA  02110-1301, USA\n#\n# The MySQL Connector/ODBC is licensed under the terms of the GPL, like most\n# MySQL Connectors. There are special exceptions to the terms and conditions of\n# the GPL as it is applied to this software, see the FLOSS License Exception\n# available on mysql.com.\n# MySQL_lib - The imported target library.\n\n# ##############################################################################\n\n# -------------- FIND MYSQL_INCLUDE_DIRS ------------------\nfind_path(MARIADB_INCLUDE_DIRS\n          NAMES mysql.h\n          PATH_SUFFIXES mariadb\n          PATHS /usr/include/mysql\n                /usr/local/include/mysql\n                /usr/include/mariadb\n                /usr/local/include/mariadb\n                /opt/mysql/mysql/include\n                /opt/mysql/mysql/include/mysql\n                /opt/mysql/include\n                /opt/local/include/mysql5\n                /usr/local/mysql/include\n                /usr/local/mysql/include/mysql\n                /usr/local/mariadb/include\n                /usr/local/mariadb/include/mariadb\n                /opt/rh/rh-mariadb105/root/usr/include\n                /opt/rh/rh-mariadb105/root/usr/include/mysql\n                $ENV{ProgramFiles}/MySQL/*/include\n                $ENV{SystemDrive}/MySQL/*/include)\n\nfind_path(MYSQL_INCLUDE_DIRS\n          NAMES mysql.h\n          PATH_SUFFIXES mysql\n          PATHS /usr/include/mysql\n                /usr/local/include/mysql\n                /usr/include/mariadb\n                /usr/local/include/mariadb\n                /opt/mysql/mysql/include\n                /opt/mysql/mysql/include/mysql\n                /opt/mysql/include\n                /opt/local/include/mysql5\n                /usr/local/mysql/include\n                /usr/local/mysql/include/mysql\n                /usr/local/mariadb/include\n                /usr/local/mariadb/include/mariadb\n                /opt/rh/rh-mariadb105/root/usr/include\n                /opt/rh/rh-mariadb105/root/usr/include/mysql\n                $ENV{ProgramFiles}/MySQL/*/include\n                $ENV{SystemDrive}/MySQL/*/include)\n\nif(EXISTS \"${MARIADB_INCLUDE_DIRS}/mysql.h\")\n  set(MYSQL_INCLUDE_DIRS ${MARIADB_INCLUDE_DIRS})\nelseif(EXISTS \"${MYSQL_INCLUDE_DIRS}/mysql.h\")\n\nelseif(EXISTS \"${MYSQL_INCLUDE_DIRS}/mysql/mysql.h\")\n  set(MYSQL_INCLUDE_DIRS ${MYSQL_INCLUDE_DIRS}/mysql)\nendif()\n\n# ----------------- FIND MYSQL_LIBRARIES_DIR -------------------\nif(WIN32)\n  # Set lib path suffixes dist = for mysql binary distributions build = for\n  # custom built tree\n  if(CMAKE_BUILD_TYPE STREQUAL Debug)\n    set(libsuffixDist debug)\n    set(libsuffixBuild Debug)\n  else(CMAKE_BUILD_TYPE STREQUAL Debug)\n    set(libsuffixDist opt)\n    set(libsuffixBuild Release)\n    add_definitions(-DDBUG_OFF)\n  endif(CMAKE_BUILD_TYPE STREQUAL Debug)\n\n  find_library(MYSQL_LIBRARIES\n               NAMES mariadbclient\n               PATHS $ENV{MYSQL_DIR}/lib/${libsuffixDist}\n                     $ENV{MYSQL_DIR}/libmysql\n                     $ENV{MYSQL_DIR}/libmysql/${libsuffixBuild}\n                     $ENV{MYSQL_DIR}/client/${libsuffixBuild}\n                     $ENV{MYSQL_DIR}/libmysql/${libsuffixBuild}\n                     $ENV{ProgramFiles}/MySQL/*/lib/${libsuffixDist}\n                     $ENV{SystemDrive}/MySQL/*/lib/${libsuffixDist})\nelse(WIN32)\n  find_library(MYSQL_LIBRARIES\n               NAMES mysqlclient_r mariadbclient mariadb\n               PATHS /usr/lib/mysql\n                     /usr/lib/mariadb\n                     /usr/local/lib/mysql\n                     /usr/local/lib/mariadb\n                     /usr/local/mysql/lib\n                     /usr/local/mysql/lib/mysql\n                     /opt/local/mysql5/lib\n                     /opt/local/lib/mysql5/mysql\n                     /opt/mysql/mysql/lib/mysql\n                     /opt/mysql/lib/mysql\n                     /opt/rh/rh-mariadb105/root/usr/lib64)\nendif(WIN32)\n\nif(MYSQL_INCLUDE_DIRS AND MYSQL_LIBRARIES)\n  message(STATUS \"MySQL Include dir: ${MYSQL_INCLUDE_DIRS}\")\n  message(STATUS \"MySQL client libraries: ${MYSQL_LIBRARIES}\")\nelseif(MySQL_FIND_REQUIRED)\n  message(\n    FATAL_ERROR\n      \"Cannot find MySQL. Include dir: ${MYSQL_INCLUDE_DIRS}  library dir: ${MYSQL_LIBRARIES_DIR}\"\n    )\nendif(MYSQL_INCLUDE_DIRS AND MYSQL_LIBRARIES)\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(MySQL\n                                  DEFAULT_MSG\n                                  MYSQL_LIBRARIES\n                                  MYSQL_INCLUDE_DIRS)\n# Copy the results to the output variables.\nif(MySQL_FOUND)\n  add_library(MySQL_lib UNKNOWN IMPORTED)\n  set_target_properties(MySQL_lib PROPERTIES\n  INTERFACE_INCLUDE_DIRECTORIES \"${MYSQL_INCLUDE_DIRS}\"\n  IMPORTED_LOCATION \"${MYSQL_LIBRARIES}\")\n  find_package(OpenSSL QUIET)  # try to find openssl\n  if(OpenSSL_FOUND)\n    target_link_libraries(MySQL_lib INTERFACE $<LINK_ONLY:OpenSSL::SSL> $<LINK_ONLY:OpenSSL::Crypto>)\n    message(STATUS \"mysql: OpenSSL found!\")\n  else()\n    message(STATUS \"mysql: OpenSSL missing!\")\n  endif()\nelse(MySQL_FOUND)\n  set(MYSQL_LIBRARIES)\n  set(MYSQL_INCLUDE_DIRS)\nendif(MySQL_FOUND)\n\nmark_as_advanced(MYSQL_INCLUDE_DIRS MYSQL_LIBRARIES)\n"
  },
  {
    "path": "cmake_modules/FindSQLite3.cmake",
    "content": "# Copyright (C) 2007-2009 LuaDist. Created by Peter Kapec <kapecp@gmail.com>\n# Redistribution and use of this file is allowed according to the terms of the\n# MIT license. For details see the COPYRIGHT file distributed with LuaDist.\n# Note: Searching headers and libraries is very simple and is NOT as powerful as\n# scripts distributed with CMake, because LuaDist defines directories to search\n# for. Everyone is encouraged to contact the author with improvements. Maybe\n# this file becomes part of CMake distribution sometimes.\n\n# * Find sqlite3 Find the native SQLITE3 headers and libraries.\n#\n# SQLITE3_INCLUDE_DIRS    - where to find sqlite3.h, etc. \n# SQLITE3_LIBRARIES - List of libraries when using sqlite. \n# SQLite3_FOUND   - True if sqlite3 found.\n# SQLite3_lib - The imported target library.\n\n# Look for the header file.\nfind_path(SQLITE3_INCLUDE_DIRS NAMES sqlite3.h)\n\n# Look for the library.\nfind_library(SQLITE3_LIBRARIES NAMES sqlite3)\n\n# Handle the QUIETLY and REQUIRED arguments and set SQLite3_FOUND to TRUE if all\n# listed variables are TRUE.\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(SQLite3\n                                  DEFAULT_MSG\n                                  SQLITE3_LIBRARIES\n                                  SQLITE3_INCLUDE_DIRS)\n\n# Copy the results to the output variables.\nif(SQLite3_FOUND)\n  add_library(SQLite3_lib INTERFACE IMPORTED)\n  set_target_properties(SQLite3_lib\n                        PROPERTIES INTERFACE_INCLUDE_DIRECTORIES\n                                   \"${SQLITE3_INCLUDE_DIRS}\"\n                                   INTERFACE_LINK_LIBRARIES\n                                   \"${SQLITE3_LIBRARIES}\")\nelse(SQLite3_FOUND)\n  set(SQLITE3_LIBRARIES)\n  set(SQLITE3_INCLUDE_DIRS)\nendif(SQLite3_FOUND)\n\nmark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES)\n"
  },
  {
    "path": "cmake_modules/FindUUID.cmake",
    "content": "# * Try to find UUID Once done this will define\n#\n# UUID_FOUND - system has UUID \n# UUID_INCLUDE_DIRS - the UUID include directory\n# UUID_LIBRARIES - Link these to use UUID UUID_DEFINITIONS - Compiler switches\n# required for using UUID\n#\n# Copyright (c) 2006 Andreas Schneider <mail@cynapses.org>\n#\n# Redistribution and use is allowed according to the terms of the New BSD\n# license. For details see the accompanying COPYING-CMAKE-SCRIPTS file.\n#\n\nif(UUID_LIBRARIES AND UUID_INCLUDE_DIRS)\n  # in cache already\n  set(UUID_FOUND TRUE)\nelse()\n  find_path(\n    UUID_INCLUDE_DIR\n    NAMES uuid.h\n    PATH_SUFFIXES uuid\n    HINTS ${UUID_DIR}/include\n          $ENV{UUID_DIR}/include\n          $ENV{UUID_DIR}\n          ${DELTA3D_EXT_DIR}/inc\n          $ENV{DELTA_ROOT}/ext/inc\n          $ENV{DELTA_ROOT}\n    PATHS\n      ~/Library/Frameworks\n      /Library/Frameworks\n      /usr/local/include\n      /usr/include\n      /usr/include/gdal\n      /sw/include # Fink\n      /opt/local/include # DarwinPorts\n      /opt/csw/include # Blastwave\n      /opt/include\n      [HKEY_LOCAL_MACHINE\\\\SYSTEM\\\\CurrentControlSet\\\\Control\\\\Session\\ Manager\\\\Environment;OSG_ROOT]/include\n      /usr/freeware/include)\n\n  find_library(UUID_LIBRARY\n               NAMES uuid ossp-uuid\n               HINTS ${UUID_DIR}/lib\n                     $ENV{UUID_DIR}/lib\n                     $ENV{UUID_DIR}\n                     ${DELTA3D_EXT_DIR}/lib\n                     $ENV{DELTA_ROOT}/ext/lib\n                     $ENV{DELTA_ROOT}\n                     $ENV{OSG_ROOT}/lib\n               PATHS ~/Library/Frameworks\n                     /Library/Frameworks\n                     /usr/local/lib\n                     /usr/lib\n                     /sw/lib\n                     /opt/local/lib\n                     /opt/csw/lib\n                     /opt/lib\n                     /usr/freeware/lib64)\n\n  find_library(UUID_LIBRARY_DEBUG\n               NAMES uuidd\n               HINTS ${UUID_DIR}/lib\n                     $ENV{UUID_DIR}/lib\n                     $ENV{UUID_DIR}\n                     ${DELTA3D_EXT_DIR}/lib\n                     $ENV{DELTA_ROOT}/ext/lib\n                     $ENV{DELTA_ROOT}\n                     $ENV{OSG_ROOT}/lib\n               PATHS ~/Library/Frameworks\n                     /Library/Frameworks\n                     /usr/local/lib\n                     /usr/lib\n                     /sw/lib\n                     /opt/local/lib\n                     /opt/csw/lib\n                     /opt/lib\n                     /usr/freeware/lib64)\n\n  if(NOT UUID_LIBRARY AND (BSD OR APPLE))\n    set(UUID_LIBRARY \"\")\n  endif()\n\n  set(UUID_INCLUDE_DIRS ${UUID_INCLUDE_DIR})\n  set(UUID_LIBRARIES ${UUID_LIBRARY})\n\n  if(UUID_INCLUDE_DIRS)\n    if((BSD OR APPLE) OR UUID_LIBRARIES)\n      set(UUID_FOUND TRUE)\n    endif()\n  endif()\n\n  if(UUID_FOUND)\n    if(NOT UUID_FIND_QUIETLY)\n      message(STATUS \"Found UUID: ${UUID_LIBRARIES}\")\n    endif()\n  else()\n    if(UUID_FIND_REQUIRED)\n      message(FATAL_ERROR \"Could not find UUID\")\n    endif()\n  endif()\n\n  # show the UUID_INCLUDE_DIRS and UUID_LIBRARIES variables only in the advanced\n  # view\n  mark_as_advanced(UUID_INCLUDE_DIRS UUID_LIBRARIES)\n\nendif()\n\nif(UUID_FOUND)\n  add_library(UUID_lib INTERFACE IMPORTED)\n  set_target_properties(UUID_lib\n                        PROPERTIES INTERFACE_INCLUDE_DIRECTORIES\n                                   \"${UUID_INCLUDE_DIRS}\"\n                                   INTERFACE_LINK_LIBRARIES\n                                   \"${UUID_LIBRARIES}\")\nelse()\n  set(UUID_LIBRARIES)\n  set(UUID_INCLUDE_DIRS)\nendif()\n"
  },
  {
    "path": "cmake_modules/Findcoz-profiler.cmake",
    "content": "find_path(COZ_INCLUDE_DIRS NAMES coz.h)\n\nfind_library(COZ_LIBRARIES NAMES coz PATH_SUFFIXES coz-profiler)\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(coz-profiler\n                                  DEFAULT_MSG\n                                  COZ_LIBRARIES\n                                  COZ_INCLUDE_DIRS)\n\nif(COZ-PROFILER_FOUND)\n  add_library(coz::coz INTERFACE IMPORTED)\n  set_target_properties(coz::coz\n                        PROPERTIES INTERFACE_INCLUDE_DIRECTORIES\n                                   ${COZ_INCLUDE_DIRS}\n                                   INTERFACE_LINK_LIBRARIES\n                                   ${COZ_LIBRARIES})\nelse(COZ-PROFILER_FOUND)\n  set(COZ_LIBRARIES)\n  set(COZ_INCLUDE_DIRS)\nendif(COZ-PROFILER_FOUND)\n\nmark_as_advanced(COZ_INCLUDE_DIRS COZ_LIBRARIES)\n"
  },
  {
    "path": "cmake_modules/Findpg.cmake",
    "content": "# Find PostgreSQL\n#\n# Find the PostgreSQL includes and library\n#\n# This module defines PG_INCLUDE_DIRS, where to find header, etc. PG_LIBRARIES,\n# the libraries needed to use PostgreSQL. pg_FOUND, If false, do not try to use\n# PostgreSQL.\n# pg_lib - The imported target library.\n\nfind_package(PostgreSQL)\nif(PostgreSQL_FOUND)\n  set(PG_LIBRARIES ${PostgreSQL_LIBRARIES})\n  set(PG_INCLUDE_DIRS ${PostgreSQL_INCLUDE_DIRS})\n  message(STATUS \"pg inc: \" ${PostgreSQL_INCLUDE_DIRS})\n  add_library(pg_lib INTERFACE IMPORTED)\n  set_target_properties(pg_lib\n                        PROPERTIES INTERFACE_INCLUDE_DIRECTORIES\n                                   \"${PostgreSQL_INCLUDE_DIRS}\"\n                                   INTERFACE_LINK_LIBRARIES\n                                   \"${PostgreSQL_LIBRARIES}\")\n  mark_as_advanced(PG_INCLUDE_DIRS PG_LIBRARIES)\nendif(PostgreSQL_FOUND)\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(pg\n                                  DEFAULT_MSG\n                                  PG_LIBRARIES\n                                  PG_INCLUDE_DIRS)\n"
  },
  {
    "path": "conanfile.txt",
    "content": "[requires]\njsoncpp/1.9.4\nzlib/1.2.11\ngtest/1.10.0\nsqlite3/3.40.1\n#libpq/13.2\nopenssl/1.1.1t\nhiredis/1.0.0\nbrotli/1.0.9\n\n[generators]\nCMakeToolchain\n\n[options]\n\n[imports]\n"
  },
  {
    "path": "config.example.json",
    "content": "/* This is a JSON format configuration file\n */\n{\n    /*\n    //ssl:The global SSL settings. \"key\" and \"cert\" are the path to the SSL key and certificate. While\n    //    \"conf\" is an array of 1 or 2-element tuples that supplies file style options for `SSL_CONF_cmd`.\n    \"ssl\": {\n        \"cert\": \"../../trantor/trantor/tests/server.crt\",\n        \"key\": \"../../trantor/trantor/tests/server.key\",\n        \"conf\": [\n            //[\"Options\", \"-SessionTicket\"], \n            //[\"Options\", \"Compression\"]\n        ]\n    },\n    \"listeners\": [\n        {\n            //address: Ip address,0.0.0.0 by default\n            \"address\": \"0.0.0.0\",\n            //port: Port number\n            \"port\": 80,\n            //https: If true, use https for security,false by default\n            \"https\": false\n        },\n        {\n            \"address\": \"0.0.0.0\",\n            \"port\": 443,\n            \"https\": true,\n            //cert,key: Cert file path and key file path, empty by default,\n            //if empty, use the global setting\n            \"cert\": \"\",\n            \"key\": \"\",\n            //use_old_tls: enable the TLS1.0/1.1, false by default\n            \"use_old_tls\": false,\n            \"ssl_conf\": [\n                //[\"MinProtocol\", \"TLSv1.3\"]\n            ]\n        }\n    ],\n    \"db_clients\": [\n        {\n            //name: Name of the client,'default' by default\n            \"name\": \"default\",\n            //rdbms: Server type, postgresql,mysql or sqlite3, \"postgresql\" by default\n            \"rdbms\": \"postgresql\",\n            //filename: Sqlite3 db file name\n            //\"filename\":\"\",\n            //host: Server address,localhost by default\n            \"host\": \"127.0.0.1\",\n            //port: Server port, 5432 by default\n            \"port\": 5432,\n            //dbname: Database name\n            \"dbname\": \"test\",\n            //user: 'postgres' by default\n            \"user\": \"\",\n            //passwd: '' by default\n            \"passwd\": \"\",\n            //is_fast: false by default, if it is true, the client is faster but user can't call\n            //any synchronous interface of it.\n            \"is_fast\": false,\n            //client_encoding: The character set used by the client. it is empty string by default which \n            //means use the default character set.\n            //\"client_encoding\": \"\",\n            //number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of  \n            //connections per IO thread, otherwise it is the total number of all connections.  \n            \"number_of_connections\": 1,\n            //timeout: -1.0 by default, in seconds, the timeout for executing a SQL query.\n            //zero or negative value means no timeout.\n            \"timeout\": -1.0,\n            //auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see\n            //the wiki for more details.\n            \"auto_batch\": false\n            //connect_options: extra options for the connection. Only works for PostgreSQL now.\n            //For more information, see https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-OPTIONS\n            //\"connect_options\": { \"statement_timeout\": \"1s\" }\n        }\n    ],\n    \"redis_clients\": [\n        {\n            //name: Name of the client,'default' by default\n            \"name\": \"default\",\n            //host: Server IP, 127.0.0.1 by default\n            \"host\": \"127.0.0.1\",\n            //port: Server port, 6379 by default\n            \"port\": 6379,\n            //username: '' by default which means 'default' in redis ACL\n            \"username\": \"\",\n            //passwd: '' by default\n            \"passwd\": \"\",\n            //db index: 0 by default\n            \"db\": 0,\n            //is_fast: false by default, if it is true, the client is faster but user can't call\n            //any synchronous interface of it.\n            \"is_fast\": false,\n            //number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of  \n            //connections per IO thread, otherwise it is the total number of all connections.  \n            \"number_of_connections\": 1,\n            //timeout: -1.0 by default, in seconds, the timeout for executing a command.\n            //zero or negative value means no timeout.\n            \"timeout\": -1.0\n        }\n    ],*/\n    \"app\": {\n        //number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads\n        //is the number of CPU cores\n        \"number_of_threads\": 1,\n        //enable_session: False by default\n        \"enable_session\": true,\n        \"session_timeout\": 0,\n        //string value of SameSite attribute of the Set-Cookie HTTP response header\n        //valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'\n        \"session_same_site\": \"Null\",\n        //session_cookie_key: The cookie key of the session, \"JSESSIONID\" by default\n        \"session_cookie_key\": \"JSESSIONID\",\n        //session_max_age: The max age of the session cookie, -1 by default\n        \"session_max_age\": -1,\n        //document_root: Root path of HTTP document, default path is ./\n        \"document_root\": \"./\",\n        //home_page: Set the HTML file of the home page, the default value is \"index.html\"\n        //If there isn't any handler registered to the path \"/\", the home page file in the \"document_root\" is send to clients as a response\n        //to the request for \"/\".\n        \"home_page\": \"index.html\",\n        //use_implicit_page: enable implicit pages if true, true by default\n        \"use_implicit_page\": true,\n        //implicit_page: Set the file which would the server access in a directory that a user accessed.\n        //For example, by default, http://localhost/a-directory resolves to http://localhost/a-directory/index.html.\n        \"implicit_page\": \"index.html\",\n        //static_file_headers: Headers for static files\n        /*\"static_file_headers\": [\n            {\n                \"name\": \"field-name\",\n                \"value\": \"field-value\"\n            }\n        ],*/\n        //upload_path: The path to save the uploaded file. \"uploads\" by default. \n        //If the path isn't prefixed with /, ./ or ../,\n        //it is relative path of document_root path\n        \"upload_path\": \"uploads\",\n        /* file_types:\n         * HTTP download file types,The file types supported by drogon\n         * by default are \"html\", \"js\", \"css\", \"xml\", \"xsl\", \"txt\", \"svg\",\n         * \"ttf\", \"otf\", \"woff2\", \"woff\" , \"eot\", \"png\", \"jpg\", \"jpeg\",\n         * \"gif\", \"bmp\", \"ico\", \"icns\", etc. */\n        \"file_types\": [\n            \"gif\",\n            \"png\",\n            \"jpg\",\n            \"js\",\n            \"css\",\n            \"html\",\n            \"ico\",\n            \"swf\",\n            \"xap\",\n            \"apk\",\n            \"cur\",\n            \"xml\",\n            \"webp\",\n            \"svg\"\n        ],\n        // mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types\n        // note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them.\n        \"mime\": {\n            // \"text/markdown\": \"md\",\n            // \"text/gemini\": [\"gmi\", \"gemini\"]\n        },\n        //locations: An array of locations of static files for GET requests.\n        \"locations\": [\n            {\n                //uri_prefix: The URI prefix of the location prefixed with \"/\", the default value is \"\" that disables the location.\n                //\"uri_prefix\": \"/.well-known/acme-challenge/\",\n                //default_content_type: The default content type of the static files without\n                //an extension. empty string by default.\n                \"default_content_type\": \"text/plain\",\n                //alias: The location in file system, if it is prefixed with \"/\", it \n                //presents an absolute path, otherwise it presents a relative path to \n                //the document_root path. \n                //The default value is \"\" which means use the document root path as the location base path.\n                \"alias\": \"\",\n                //is_case_sensitive: indicates whether the URI prefix is case sensitive.\n                \"is_case_sensitive\": false,\n                //allow_all: true by default. If it is set to false, only static files with a valid extension can be accessed.\n                \"allow_all\": true,\n                //is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.\n                \"is_recursive\": true,\n                //filters: string array, the filters applied to the location.\n                \"filters\": []\n            }\n        ],\n        //max_connections: maximum number of connections, 100000 by default\n        \"max_connections\": 100000,\n        //max_connections_per_ip: maximum number of connections per client, 0 by default which means no limit\n        \"max_connections_per_ip\": 0,\n        //Load_dynamic_views: False by default, when set to true, drogon\n        //compiles and loads dynamically \"CSP View Files\" in directories defined\n        //by \"dynamic_views_path\"\n        \"load_dynamic_views\": false,\n        //dynamic_views_path: If the path isn't prefixed with /, ./ or ../,\n        //it is relative path of document_root path\n        \"dynamic_views_path\": [\n            \"./views\"\n        ],\n        //dynamic_views_output_path: Default by an empty string which means the output path of source \n        //files is the path where the csp files locate. If the path isn't prefixed with /, it is relative \n        //path of the current working directory.\n        \"dynamic_views_output_path\": \"\",\n        //json_parser_stack_limit: 1000 by default, the maximum number of stack depth when reading a json string by the jsoncpp library.\n        \"json_parser_stack_limit\": 1000,\n        //enable_unicode_escaping_in_json: true by default, enable unicode escaping in json.\n        \"enable_unicode_escaping_in_json\": true,\n        //float_precision_in_json: set precision of float number in json. \n        \"float_precision_in_json\": {\n            //precision: 0 by default, 0 means use the default precision of the jsoncpp lib. \n            \"precision\": 0,\n            //precision_type: must be \"significant\" or \"decimal\", defaults to \"significant\" that means \n            //setting max number of significant digits in string, \"decimal\" means setting max number of \n            //digits after \".\" in string\n            \"precision_type\": \"significant\"\n        },\n        //log: Set log output, drogon output logs to stdout by default\n        \"log\": {\n            //use_spdlog: Use spdlog library to log\n            \"use_spdlog\": false,\n            //log_path: Log file path,empty by default,in which case,logs are output to the stdout\n            //\"log_path\": \"./\",\n            //logfile_base_name: Log file base name,empty by default which means drogon names logfile as\n            //drogon.log ...\n            \"logfile_base_name\": \"\",\n            //log_size_limit: 100000000 bytes by default,\n            //When the log file size reaches \"log_size_limit\", the log file is switched.\n            \"log_size_limit\": 100000000,\n            //max_files: 0 by default,\n            //When the number of old log files exceeds \"max_files\", the oldest file will be deleted. 0 means never delete.\n            \"max_files\": 0,\n            //log_level: \"DEBUG\" by default,options:\"TRACE\",\"DEBUG\",\"INFO\",\"WARN\"\n            //The TRACE level is only valid when built in DEBUG mode.\n            \"log_level\": \"DEBUG\",\n            //display_local_time: false by default, if true, the log time is displayed in local time\n            \"display_local_time\": false\n        },\n        //run_as_daemon: False by default\n        \"run_as_daemon\": false,\n        //handle_sig_term: True by default\n        \"handle_sig_term\": true,\n        //relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;\n        \"relaunch_on_error\": false,\n        //use_sendfile: True by default, if true, the program \n        //uses sendfile() system-call to send static files to clients;\n        \"use_sendfile\": true,\n        //use_gzip: True by default, use gzip to compress the response body's content;\n        \"use_gzip\": true,\n        //use_brotli: False by default, use brotli to compress the response body's content;\n        \"use_brotli\": false,\n        //static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,\n        //0 means cache forever, the negative value means no cache\n        \"static_files_cache_time\": 5,\n        //simple_controllers_map: Used to configure mapping from path to simple controller\n        //\"simple_controllers_map\": [\n        //    {\n        //        \"path\": \"/path/name\",\n        //        \"controller\": \"controllerClassName\",\n        //        \"http_methods\": [\n        //            \"get\",\n        //            \"post\"\n        //        ],\n        //        \"filters\": [\n        //            \"FilterClassName\"\n        //        ]\n        //    }\n        //],\n        //idle_connection_timeout: Defaults to 60 seconds, the lifetime \n        //of the connection without read or write\n        \"idle_connection_timeout\": 60,\n        //server_header_field: Set the 'Server' header field in each response sent by drogon,\n        //empty string by default with which the 'Server' header field is set to \"Server: drogon/version string\\r\\n\"\n        \"server_header_field\": \"\",\n        //enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default \n        //value is true.\n        \"enable_server_header\": true,\n        //enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default \n        //value is true.\n        \"enable_date_header\": true,\n        //keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection. \n        //After the maximum number of requests are made, the connection is closed.\n        //The default value of 0 means no limit.\n        \"keepalive_requests\": 0,\n        //pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer. \n        //After the maximum number of requests are made, the connection is closed.\n        //The default value of 0 means no limit.\n        \"pipelining_requests\": 0,\n        //gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed \n        //file with the extension \".gz\" in the same path and send the compressed file to the client.\n        //The default value of gzip_static is true.\n        \"gzip_static\": true,\n        //br_static: If it is set to true, when the client requests a static file, drogon first finds the compressed \n        //file with the extension \".br\" in the same path and send the compressed file to the client.\n        //The default value of br_static is true.\n        \"br_static\": true,\n        //client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is \"1M\".\n        //One can set it to \"1024\", \"1k\", \"10M\", \"1G\", etc. Setting it to \"\" means no limit.\n        \"client_max_body_size\": \"1M\",\n        //max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is \"64K\" bytes.\n        //If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.\n        //Setting it to \"\" means no limit.\n        \"client_max_memory_body_size\": \"64K\",\n        //client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is \"128K\".\n        //One can set it to \"1024\", \"1k\", \"10M\", \"1G\", etc. Setting it to \"\" means no limit.\n        \"client_max_websocket_message_size\": \"128K\",\n        //reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time.\n        \"reuse_port\": false,\n        // enabled_compressed_request: Defaults to false. If true the server will automatically decompress compressed request bodies.\n        // Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests.\n        // Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request\n        // will be rejected.\n        \"enabled_compressed_request\": false,\n        // enable_request_stream: Defaults to false. If true the server will enable stream mode for http requests.\n        // See the wiki for more details.\n        \"enable_request_stream\": false,\n    },\n    //plugins: Define all plugins running in the application\n    \"plugins\": [\n        {\n            //name: The class name of the plugin\n            \"name\": \"drogon::plugin::PromExporter\",\n            //dependencies: Plugins that the plugin depends on. It can be commented out\n            \"dependencies\": [],\n            //config: The configuration of the plugin. This json object is the parameter to initialize the plugin.\n            //It can be commented out\n            \"config\": {\n                \"path\": \"/metrics\"\n            }\n        },\n        {\n            \"name\": \"drogon::plugin::AccessLogger\",\n            \"dependencies\": [],\n            \"config\": {\n                \"use_spdlog\": false,\n                \"log_path\": \"\",\n                \"log_format\": \"\",\n                \"log_file\": \"access.log\",\n                \"log_size_limit\": 0,\n                \"use_local_time\": true,\n                \"log_index\": 0,\n                // \"show_microseconds\": true,\n                // \"custom_time_format\": \"\",\n                // \"use_real_ip\": false\n            }\n        }\n    ],\n    //custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method. \n    \"custom_config\": {\n        \"realm\": \"drogonRealm\",\n        \"opaque\": \"drogonOpaque\",\n        \"credentials\": [\n            {\n                \"user\": \"drogon\",\n                \"password\": \"dr0g0n\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config.example.yaml",
    "content": "# This is a YAML format configuration file\n\n# ssl:The global SSL settings. \"key\" and \"cert\" are the path to the SSL key and certificate. While\n#     \"conf\" is an array of 1 or 2-element tuples that supplies file style options for `SSL_CONF_cmd`.\n# ssl:\n#   cert: ../../trantor/trantor/tests/server.crt\n#   key: ../../trantor/trantor/tests/server.key\n#   conf: [\n#     # [Options, -SessionTicket],\n#     # [Options, Compression]\n#   ]\n# listeners:\n#     # address: Ip address,0.0.0.0 by default\n#   - address: 0.0.0.0\n#     # port: Port number\n#     port: 80\n#     # https: If true, use https for security,false by default\n#     https: false\n#   - address: 0.0.0.0\n#     port: 443\n#     https: true\n#     # cert,key: Cert file path and key file path, empty by default,\n#     # if empty, use the global setting\n#     cert: ''\n#     key: ''\n#     # use_old_tls: enable the TLS1.0/1.1, false by default\n#     use_old_tls: false\n#     ssl_conf: [\n#       # [MinProtocol, TLSv1.3]\n#     ]\n# db_clients:\n#     # name: Name of the client,'default' by default\n#   - name: default\n#     # rdbms: Server type, postgresql,mysql or sqlite3, \"postgresql\" by default\n#     rdbms: postgresql\n#     # filename: Sqlite3 db file name\n#     # filename: ''\n#     # host: Server address,localhost by default\n#     host: 127.0.0.1\n#     # port: Server port, 5432 by default\n#     port: 5432\n#     # dbname: Database name\n#     dbname: test\n#     # user: 'postgres' by default\n#     user: ''\n#     # passwd: '' by default\n#     passwd: ''\n#     # is_fast: false by default, if it is true, the client is faster but user can't call\n#     # any synchronous interface of it.\n#     is_fast: false\n#     # client_encoding: The character set used by the client. it is empty string by default which \n#     # means use the default character set.\n#     # client_encoding: ''\n#     # number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of  \n#     # connections per IO thread, otherwise it is the total number of all connections.  \n#     number_of_connections: 1\n#     # timeout: -1 by default, in seconds, the timeout for executing a SQL query.\n#     # zero or negative value means no timeout.\n#     timeout: -1\n#     # auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see\n#     # the wiki for more details.\n#     auto_batch: false\n#     # connect_options: extra options for the connection. Only works for PostgreSQL now.\n#     # For more information, see https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-OPTIONS\n#     # connect_options:\n#     #   statement_timeout: '1s'\n# redis_clients:\n#     # name: Name of the client,'default' by default\n#   - name: default\n#     # host: Server IP, 127.0.0.1 by default\n#     host: 127.0.0.1\n#     # port: Server port, 6379 by default\n#     port: 6379\n#     # username: '' by default which means 'default' in redis ACL\n#     username: ''\n#     # passwd: '' by default\n#     passwd: ''\n#     # db index: 0 by default\n#     db: 0\n#     # is_fast: false by default, if it is true, the client is faster but user can't call\n#     # any synchronous interface of it.\n#     is_fast: false\n#     # number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of  \n#     # connections per IO thread, otherwise it is the total number of all connections.  \n#     number_of_connections: 1\n#     # timeout: -1.0 by default, in seconds, the timeout for executing a command.\n#     # zero or negative value means no timeout.\n#     timeout: -1\napp:\n  # number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads\n  # is the number of CPU cores\n  number_of_threads: 1\n  # enable_session: False by default\n  enable_session: true\n  session_timeout: 0\n  # string value of SameSite attribute of the Set-Cookie HTTP response header\n  # valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'\n  session_same_site: 'Null'\n  # session_cookie_key: The cookie key of the session, \"JSESSIONID\" by default\n  session_cookie_key: 'JSESSIONID'\n  # session_max_age: The max age of the session cookie, -1 by default\n  session_max_age: -1\n  # document_root: Root path of HTTP document, default path is ./\n  document_root: ./\n  # home_page: Set the HTML file of the home page, the default value is \"index.html\"\n  # If there isn't any handler registered to the path \"/\", the home page file in the \"document_root\" is send to clients as a response\n  # to the request for \"/\".\n  home_page: index.html\n  # use_implicit_page: enable implicit pages if true, true by default\n  use_implicit_page: true\n  # implicit_page: Set the file which would the server access in a directory that a user accessed.\n  # For example, by default, http://localhost/a-directory resolves to http://localhost/a-directory/index.html.\n  implicit_page: index.html\n  # static_file_headers: Headers for static files\n  # static_file_headers:\n  #   - name: field-name\n  #     value: field-value\n  # upload_path: The path to save the uploaded file. \"uploads\" by default. \n  # If the path isn't prefixed with /, ./ or ../,\n  # it is relative path of document_root path\n  upload_path: uploads\n  # file_types:\n  # HTTP download file types,The file types supported by drogon\n  # by default are \"html\", \"js\", \"css\", \"xml\", \"xsl\", \"txt\", \"svg\",\n  # \"ttf\", \"otf\", \"woff2\", \"woff\" , \"eot\", \"png\", \"jpg\", \"jpeg\",\n  # \"gif\", \"bmp\", \"ico\", \"icns\", etc.\n  file_types:\n    - gif\n    - png\n    - jpg\n    - js\n    - css\n    - html\n    - ico\n    - swf\n    - xap\n    - apk\n    - cur\n    - xml\n  # mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types\n  # note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them.\n  mime: {\n    # text/markdown: md\n    # text/gemini:\n    #   - gmi\n    #   - gemini\n    }\n  # locations: An array of locations of static files for GET requests.\n  locations:\n      # uri_prefix: The URI prefix of the location prefixed with \"/\", the default value is \"\" that disables the location.\n    - uri_prefix: '' # /.well-known/acme-challenge/\n      # default_content_type: The default content type of the static files without\n      # an extension. empty string by default.\n      default_content_type: text/plain\n      # alias: The location in file system, if it is prefixed with \"/\", it \n      # presents an absolute path, otherwise it presents a relative path to \n      # the document_root path. \n      # The default value is \"\" which means use the document root path as the location base path.\n      alias: ''\n      # is_case_sensitive: indicates whether the URI prefix is case sensitive.\n      is_case_sensitive: false\n      # allow_all: true by default. If it is set to false, only static files with a valid extension can be accessed.\n      allow_all: true\n      # is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.\n      is_recursive: true\n      # filters: string array, the filters applied to the location.\n      filters: []\n  # max_connections: maximum number of connections, 100000 by default\n  max_connections: 100000\n  # max_connections_per_ip: maximum number of connections per client, 0 by default which means no limit\n  max_connections_per_ip: 0\n  # Load_dynamic_views: False by default, when set to true, drogon\n  # compiles and loads dynamically \"CSP View Files\" in directories defined\n  # by \"dynamic_views_path\"\n  load_dynamic_views: false\n  # dynamic_views_path: If the path isn't prefixed with /, ./ or ../,\n  # it is relative path of document_root path\n  dynamic_views_path:\n    - ./views\n  # dynamic_views_output_path: Default by an empty string which means the output path of source \n  # files is the path where the csp files locate. If the path isn't prefixed with /, it is relative \n  # path of the current working directory.\n  dynamic_views_output_path: ''\n  # json_parser_stack_limit: 1000 by default, the maximum number of stack depth when reading a json string by the jsoncpp library.\n  json_parser_stack_limit: 1000\n  # enable_unicode_escaping_in_json: true by default, enable unicode escaping in json.\n  enable_unicode_escaping_in_json: true\n  # float_precision_in_json: set precision of float number in json. \n  float_precision_in_json:\n    # precision: 0 by default, 0 means use the default precision of the jsoncpp lib. \n    precision: 0\n    # precision_type: must be \"significant\" or \"decimal\", defaults to \"significant\" that means \n    # setting max number of significant digits in string, \"decimal\" means setting max number of \n    # digits after \".\" in string\n    precision_type: significant\n  # log: Set log output, drogon output logs to stdout by default\n  log:\n    # use_spdlog: Use spdlog library to log\n    use_spdlog: false\n    # log_path: Log file path,empty by default,in which case,logs are output to the stdout\n    # log_path: ./\n    # logfile_base_name: Log file base name,empty by default which means drogon names logfile as\n    # drogon.log ...\n    logfile_base_name: ''\n    # log_size_limit: 100000000 bytes by default,\n    # When the log file size reaches \"log_size_limit\", the log file is switched.\n    log_size_limit: 100000000\n    # max_files: 0 by default,\n    # When the number of old log files exceeds \"max_files\", the oldest file will be deleted. 0 means never delete.\n    max_files: 0\n    # log_level: \"DEBUG\" by default,options:\"TRACE\",\"DEBUG\",\"INFO\",\"WARN\"\n    # The TRACE level is only valid when built in DEBUG mode.\n    log_level: DEBUG\n    # display_local_time: false by default, if true, the log time is displayed in local time\n    display_local_time: false\n  # run_as_daemon: False by default\n  run_as_daemon: false\n  # handle_sig_term: True by default\n  handle_sig_term: true\n  # relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;\n  relaunch_on_error: false\n  # use_sendfile: True by default, if true, the program \n  # uses sendfile() system-call to send static files to clients;\n  use_sendfile: true\n  # use_gzip: True by default, use gzip to compress the response body's content;\n  use_gzip: true\n  # use_brotli: False by default, use brotli to compress the response body's content;\n  use_brotli: false\n  # static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,\n  # 0 means cache forever, the negative value means no cache\n  static_files_cache_time: 5\n  # simple_controllers_map: Used to configure mapping from path to simple controller\n  # simple_controllers_map:\n  #   - path: /path/name\n  #     controller: controllerClassName\n  #     http_methods:\n  #       - get\n  #       - post\n  #     filters:\n  #       - FilterClassName\n  # idle_connection_timeout: Defaults to 60 seconds, the lifetime \n  # of the connection without read or write\n  idle_connection_timeout: 60\n  # server_header_field: Set the 'Server' header field in each response sent by drogon,\n  # empty string by default with which the 'Server' header field is set to \"Server: drogon/version string\\r\\n\"\n  server_header_field: ''\n  # enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default \n  # value is true.\n  enable_server_header: true\n  # enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default \n  # value is true.\n  enable_date_header: true\n  # keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection. \n  # After the maximum number of requests are made, the connection is closed.\n  # The default value of 0 means no limit.\n  keepalive_requests: 0\n  # pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer. \n  # After the maximum number of requests are made, the connection is closed.\n  # The default value of 0 means no limit.\n  pipelining_requests: 0\n  # gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed \n  # file with the extension \".gz\" in the same path and send the compressed file to the client.\n  # The default value of gzip_static is true.\n  gzip_static: true\n  # br_static: If it is set to true, when the client requests a static file, drogon first finds the compressed \n  # file with the extension \".br\" in the same path and send the compressed file to the client.\n  # The default value of br_static is true.\n  br_static: true\n  # client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is \"1M\".\n  # One can set it to \"1024\", \"1k\", \"10M\", \"1G\", etc. Setting it to \"\" means no limit.\n  client_max_body_size: 1M\n  # max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is \"64K\" bytes.\n  # If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.\n  # Setting it to \"\" means no limit.\n  client_max_memory_body_size: 64K\n  # client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is \"128K\".\n  # One can set it to \"1024\", \"1k\", \"10M\", \"1G\", etc. Setting it to \"\" means no limit.\n  client_max_websocket_message_size: 128K\n  # reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time.\n  reuse_port: false\n  # enabled_compressed_request: Defaults to false. If true the server will automatically decompress compressed request bodies.\n  # Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests.\n  # Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request\n  # will be rejected.\n  enabled_compressed_request: false\n  # enable_request_stream: Defaults to false. If true the server will enable stream mode for http requests.\n  # See the wiki for more details.\n  enable_request_stream: false\n# plugins: Define all plugins running in the application\nplugins:\n    # name: The class name of the plugin\n  - name: drogon::plugin::PromExporter\n    # dependencies: Plugins that the plugin depends on. It can be commented out\n    dependencies: []\n    # config: The configuration of the plugin. This json object is the parameter to initialize the plugin.\n    # It can be commented out\n    config:\n      path: /metrics\n  - name: drogon::plugin::AccessLogger\n    dependencies: []\n    config:\n      use_spdlog: false\n      log_path: ''\n      log_format: ''\n      log_file: access.log\n      log_size_limit: 0\n      use_local_time: true\n      log_index: 0\n      # show_microseconds: true\n      # custom_time_format: ''\n      # use_real_ip: false\n# custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method. \ncustom_config:\n  realm: drogonRealm\n  opaque: drogonOpaque\n  credentials:\n    - user: drogon\n      password: dr0g0n\n"
  },
  {
    "path": "docker/alpine/Dockerfile",
    "content": "FROM alpine:3.14\n\nARG USER=drogon\nARG UID=1000\nARG GID=1000\nARG USER_HOME=/drogon\n\nENV TZ=UTC\n\nRUN apk update && apk --no-cache --upgrade add tzdata \\\n    && ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \\\n    && echo $TZ > /etc/timezone\n\nRUN apk --no-cache --upgrade add \\\n    sudo curl wget cmake make pkgconfig git gcc g++ \\\n    openssl libressl-dev jsoncpp-dev util-linux-dev zlib-dev c-ares-dev \\\n    postgresql-dev mariadb-dev sqlite-dev hiredis-dev\n\nRUN addgroup -S -g $GID $USER \\\n    && adduser -D -u $UID -G $USER -h $USER_HOME $USER \\\n    && mkdir -p /etc/sudoers.d \\\n    && echo \"$USER ALL=(ALL) NOPASSWD: ALL\" > /etc/sudoers.d/$USER \\\n    && chmod 0440 /etc/sudoers.d/$USER\n\nUSER $USER\nWORKDIR $USER_HOME\n\nENV LANG=en_US.UTF-8 \\\n    LANGUAGE=en_US:en \\\n    LC_ALL=en_US.UTF-8 \\\n    CC=gcc \\\n    CXX=g++ \\\n    AR=gcc-ar \\\n    RANLIB=gcc-ranlib \\\n    DROGON_INSTALLED_ROOT=$USER_HOME/install\n\nRUN wget -O $USER_HOME/version.json https://api.github.com/repos/an-tao/drogon/git/refs/heads/master \\\n    && git clone https://github.com/an-tao/drogon $DROGON_INSTALLED_ROOT\n\nRUN cd $DROGON_INSTALLED_ROOT \\\n    && sed -i 's/bash/sh/' ./build.sh \\\n    && ./build.sh\n"
  },
  {
    "path": "docker/alpine/README.md",
    "content": "## Build Docker Image\n\n```shell\n$ cd drogon/docker/alpine # from this repository\n$ docker build --no-cache --build-arg UID=`id -u` --build-arg GID=`id -g` -t drogon-alpine . # include last dot(.)\n```\n\n## Create a Drogon Project\n\n```shell\n$ cd ~/drogon_app # example\n$ docker run --rm -v=\"$PWD:/drogon/app\" -w=\"/drogon/app\" drogon-alpine drogon_ctl create project hello_world\n```\n\n## Build the Project\n\n```shell\n$ cd hello_world\n$ docker run --rm --volume=\"$PWD:/drogon/app\" -w=\"/drogon/app/build\" drogon-alpine sh -c \"cmake .. && make\"\n```\n\n## Start Server\n\n```shell\n$ docker run --name drogon_test --rm -u 0 -v=\"$PWD/build:/drogon/app\" -w=\"/drogon/app\" -p 8080:80 -d drogon-alpine ./hello_world # expose port 80 to 8080\n```\n\n## Stop Server\n\n```shell\n$ docker kill drogon_test\n```\n"
  },
  {
    "path": "docker/arch/Dockerfile",
    "content": "FROM archlinux:base-20210307.0.16708\n\nRUN pacman -Syu --noconfirm && pacman -S wget sudo cmake make git gcc jsoncpp postgresql mariadb-clients hiredis --noconfirm\n\nENV LANG=en_US.UTF-8 \\\n    LANGUAGE=en_US:en \\\n    LC_ALL=en_US.UTF-8 \\\n    CC=gcc \\\n    CXX=g++ \\\n    AR=gcc-ar \\\n    RANLIB=gcc-ranlib \\\n    IROOT=/install\n\nENV DROGON_ROOT=\"$IROOT/drogon\"\n\nADD https://api.github.com/repos/an-tao/drogon/git/refs/heads/master $IROOT/version.json\nRUN git clone https://github.com/an-tao/drogon $DROGON_ROOT\n\nWORKDIR $DROGON_ROOT\n\nRUN ./build.sh\n"
  },
  {
    "path": "docker/ubuntu/Dockerfile",
    "content": "FROM ubuntu:22.04\n\nENV TZ=UTC\nRUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone\n\nRUN apt-get update -yqq \\\n    && apt-get install -yqq --no-install-recommends software-properties-common \\\n    sudo curl wget cmake make pkg-config locales git gcc-11 g++-11 \\\n    openssl libssl-dev libjsoncpp-dev uuid-dev zlib1g-dev libc-ares-dev\\\n    postgresql-server-dev-all libmariadb-dev libsqlite3-dev libhiredis-dev\\\n    && rm -rf /var/lib/apt/lists/* \\\n    && locale-gen en_US.UTF-8\n\nENV LANG=en_US.UTF-8 \\\n    LANGUAGE=en_US:en \\\n    LC_ALL=en_US.UTF-8 \\\n    CC=gcc-11 \\\n    CXX=g++-11 \\\n    AR=gcc-ar-11 \\\n    RANLIB=gcc-ranlib-11 \\\n    IROOT=/install\n\nENV DROGON_ROOT=\"$IROOT/drogon\"\n\nADD https://api.github.com/repos/drogonframework/drogon/git/refs/heads/master $IROOT/version.json\nRUN git clone https://github.com/drogonframework/drogon $DROGON_ROOT\n\nWORKDIR $DROGON_ROOT\n\nRUN ./build.sh\n"
  },
  {
    "path": "drogon_ctl/CMakeLists.txt",
    "content": "set(ctl_sources\n    cmd.cc\n    create.cc\n    create_controller.cc\n    create_filter.cc\n    create_model.cc\n    create_plugin.cc\n    create_project.cc\n    create_view.cc\n    help.cc\n    main.cc\n    press.cc\n    version.cc)\nadd_executable(_drogon_ctl\n               main.cc\n               cmd.cc\n               create.cc\n               create_view.cc)\ntarget_link_libraries(_drogon_ctl ${PROJECT_NAME})\nif (WIN32 AND BUILD_SHARED_LIBS)\n  set(DROGON_FILE $<TARGET_FILE:drogon>)\n  if (USE_SUBMODULE)\n    set(TRANTOR_FILE $<TARGET_FILE:trantor>)\n  else()\n    set(TRANTOR_FILE $<TARGET_FILE:Trantor::Trantor>)\n  endif()\n  add_custom_command(TARGET _drogon_ctl POST_BUILD\n          COMMAND ${CMAKE_COMMAND}\n          -DCTL_FILE=${DROGON_FILE}\n          -DINSTALL_BIN_DIR=$<TARGET_FILE_DIR:_drogon_ctl>\n          -P\n          ${CMAKE_CURRENT_SOURCE_DIR}/CopyDlls.cmake)\n  add_custom_command(TARGET _drogon_ctl POST_BUILD\n          COMMAND ${CMAKE_COMMAND}\n          -DCTL_FILE=${TRANTOR_FILE}\n          -DINSTALL_BIN_DIR=$<TARGET_FILE_DIR:_drogon_ctl>\n          -P\n          ${CMAKE_CURRENT_SOURCE_DIR}/CopyDlls.cmake)\nendif()\nfile(GLOB SCP_LIST ${CMAKE_CURRENT_SOURCE_DIR}/templates/*.csp)\nforeach(cspFile ${SCP_LIST})\n  message(STATUS \"cspFile:\" ${cspFile})\n  get_filename_component(classname ${cspFile} NAME_WE)\n  message(STATUS \"view classname:\" ${classname})\n  add_custom_command(OUTPUT ${classname}.h ${classname}.cc\n                     COMMAND ${DROGON_CTL}\n                             ARGS\n                             create\n                             view\n                             ${cspFile}\n                     DEPENDS ${cspFile}\n                     VERBATIM)\n  set(TEMPL_SRC ${TEMPL_SRC} ${classname}.cc)\nendforeach()\nadd_executable(drogon_ctl ${ctl_sources} ${TEMPL_SRC})\ntarget_link_libraries(drogon_ctl PRIVATE ${PROJECT_NAME})\ntarget_include_directories(drogon_ctl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})\nadd_dependencies(drogon_ctl _drogon_ctl)\nif(WIN32)\n  target_link_libraries(drogon_ctl PRIVATE ws2_32 rpcrt4 iphlpapi)\nendif(WIN32)\nif(APPLE)\n  target_link_libraries(drogon_ctl PRIVATE resolv)\nendif()\nmessage(STATUS \"bin:\" ${INSTALL_BIN_DIR})\ninstall(TARGETS _drogon_ctl RUNTIME DESTINATION ${INSTALL_BIN_DIR})\ninstall(TARGETS drogon_ctl RUNTIME DESTINATION ${INSTALL_BIN_DIR})\nif(WIN32)\n  set(CTL_FILE $<TARGET_FILE:drogon_ctl>)\n  add_custom_command(TARGET drogon_ctl POST_BUILD\n                     COMMAND ${CMAKE_COMMAND}\n                             -DCTL_FILE=${CTL_FILE}\n                             -DINSTALL_BIN_DIR=${INSTALL_BIN_DIR}\n                             -DRENAME_EXE=ON\n                             -P\n                             ${CMAKE_CURRENT_SOURCE_DIR}/CopyDlls.cmake)\nelse(WIN32)\n  install(CODE \"execute_process( \\\n    COMMAND ${CMAKE_COMMAND} -E create_symlink \\\n    ./drogon_ctl \\\n    ./dg_ctl \\\n    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})\")\n  install(FILES \"${CMAKE_CURRENT_BINARY_DIR}/dg_ctl\"\n          DESTINATION ${INSTALL_BIN_DIR})\nendif(WIN32)\nset(ctl_targets _drogon_ctl drogon_ctl)\nset_property(TARGET ${ctl_targets} PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})\nset_property(TARGET ${ctl_targets} PROPERTY CXX_STANDARD_REQUIRED ON)\nset_property(TARGET ${ctl_targets} PROPERTY CXX_EXTENSIONS OFF)\n"
  },
  {
    "path": "drogon_ctl/CommandHandler.h",
    "content": "/**\n *\n *  CommandHandler.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include <vector>\n#include <string>\n\nclass CommandHandler : public virtual drogon::DrObjectBase\n{\n  public:\n    virtual void handleCommand(std::vector<std::string> &parameters) = 0;\n\n    virtual bool isTopCommand()\n    {\n        return false;\n    }\n\n    virtual std::string script()\n    {\n        return \"\";\n    }\n\n    virtual std::string detail()\n    {\n        return \"\";\n    }\n\n    virtual ~CommandHandler()\n    {\n    }\n};\n"
  },
  {
    "path": "drogon_ctl/CopyDlls.cmake",
    "content": "make_directory(\"${INSTALL_BIN_DIR}\")\nget_filename_component(CTL_PATH ${CTL_FILE} DIRECTORY)\nfile(GLOB DLL_FILES ${CTL_PATH}/*.dll)\nfile(COPY ${DLL_FILES} DESTINATION ${INSTALL_BIN_DIR})\nfile(COPY ${CTL_FILE} DESTINATION ${INSTALL_BIN_DIR})\nif (RENAME_EXE)\n    file(RENAME ${INSTALL_BIN_DIR}/drogon_ctl.exe ${INSTALL_BIN_DIR}/dg_ctl.exe)\nendif()"
  },
  {
    "path": "drogon_ctl/cmd.cc",
    "content": "/**\n *\n *  cmd.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"cmd.h\"\n#include \"CommandHandler.h\"\n#include <drogon/DrClassMap.h>\n#include <memory>\nusing namespace drogon;\n\nvoid exeCommand(std::vector<std::string> &parameters)\n{\n    if (parameters.empty())\n    {\n        std::cout << \"incomplete command!use help command to get usage!\"\n                  << std::endl;\n        return;\n    }\n    std::string command = parameters[0];\n\n    std::string handlerName = std::string(\"drogon_ctl::\").append(command);\n\n    parameters.erase(parameters.begin());\n\n    // new command handler to do cmd\n    auto obj = std::shared_ptr<DrObjectBase>(\n        drogon::DrClassMap::newObject(handlerName));\n    if (obj)\n    {\n        auto ctl = std::dynamic_pointer_cast<CommandHandler>(obj);\n        if (ctl)\n        {\n            ctl->handleCommand(parameters);\n        }\n        else\n        {\n            std::cout << \"command not found!use help command to get usage!\"\n                      << std::endl;\n        }\n    }\n    else\n    {\n        std::cout << \"command error!use help command to get usage!\"\n                  << std::endl;\n    }\n}\n"
  },
  {
    "path": "drogon_ctl/cmd.h",
    "content": "/**\n *\n *  cmd.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <vector>\n#include <string>\n#include <iostream>\n#define ARGS_ERROR_STR \"args error!use help command to get usage!\"\nvoid exeCommand(std::vector<std::string> &parameters);\n"
  },
  {
    "path": "drogon_ctl/create.cc",
    "content": "/**\n *\n *  @file create.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"create.h\"\n#include \"cmd.h\"\n#include <drogon/DrClassMap.h>\n#include <iostream>\n#include <memory>\nusing namespace drogon_ctl;\n\nstd::string create::detail()\n{\n    return \"Use create command to create some source files of drogon webapp\\n\\n\"\n           \"Usage:drogon_ctl create <view|controller|filter|project|model> \"\n           \"[-options] <object name>\\n\\n\"\n           \"drogon_ctl create view <csp file name> [-o <output path>] [-n \"\n           \"<namespace>] [--path-to-namespace] //create HttpView source files \"\n           \"from csp files, namespace is prefixed of path-to-namespace\\n\\n\"\n           \"drogon_ctl create controller [-s] <[namespace::]class_name> //\"\n           \"create HttpSimpleController source files\\n\\n\"\n           \"drogon_ctl create controller -h <[namespace::]class_name> //\"\n           \"create HttpController source files\\n\\n\"\n           \"drogon_ctl create controller -w <[namespace::]class_name> //\"\n           \"create WebSocketController source files\\n\\n\"\n           \"drogon_ctl create controller -r <[namespace::]class_name> \"\n           \"[--resource=...]//\"\n           \"create restful controller source files\\n\\n\"\n           \"drogon_ctl create filter <[namespace::]class_name> //\"\n           \"create a filter named class_name\\n\\n\"\n           \"drogon_ctl create plugin <[namespace::]class_name> //\"\n           \"create a plugin named class_name\\n\\n\"\n           \"drogon_ctl create project <project_name> //\"\n           \"create a project named project_name\\n\\n\"\n           \"drogon_ctl create model <model_path> [-o <output path>] [ \"\n           \"--clear-output]\"\n           \"[--table=<table_name>] [-f]//\"\n           \"create model classes in model_path\\n\";\n}\n\nvoid create::handleCommand(std::vector<std::string> &parameters)\n{\n    // std::cout<<\"create!\"<<std::endl;\n    auto createObjName = parameters[0];\n    createObjName = std::string(\"create_\") + createObjName;\n    parameters[0] = createObjName;\n    exeCommand(parameters);\n}\n"
  },
  {
    "path": "drogon_ctl/create.h",
    "content": "/**\n *\n *  create.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include \"CommandHandler.h\"\nusing namespace drogon;\n\nnamespace drogon_ctl\n{\nclass create : public DrObject<create>, public CommandHandler\n{\n  public:\n    void handleCommand(std::vector<std::string> &parameters) override;\n\n    std::string script() override\n    {\n        return \"create some source files(Use 'drogon_ctl help create' for more \"\n               \"information)\";\n    }\n\n    bool isTopCommand() override\n    {\n        return true;\n    }\n\n    std::string detail() override;\n};\n}  // namespace drogon_ctl\n"
  },
  {
    "path": "drogon_ctl/create_controller.cc",
    "content": "/**\n *\n *  create_controller.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"create_controller.h\"\n#include \"cmd.h\"\n#include <drogon/DrTemplateBase.h>\n#include <drogon/utils/Utilities.h>\n#include <iostream>\n#include <fstream>\n#include <regex>\n\nusing namespace drogon_ctl;\n\nvoid create_controller::handleCommand(std::vector<std::string> &parameters)\n{\n    // std::cout<<\"create!\"<<std::endl;\n    ControllerType type = Simple;\n    for (auto iter = parameters.begin(); iter != parameters.end(); ++iter)\n    {\n        if ((*iter)[0] == '-')\n        {\n            if (*iter == \"-s\" || (*iter == \"--simple\"))\n            {\n                parameters.erase(iter);\n                break;\n            }\n            else if (*iter == \"-a\" || *iter == \"-h\" || *iter == \"--http\")\n            {\n                type = Http;\n                parameters.erase(iter);\n                break;\n            }\n            else if (*iter == \"-w\" || *iter == \"--websocket\")\n            {\n                type = WebSocket;\n                parameters.erase(iter);\n                break;\n            }\n            else if (*iter == \"-r\" || *iter == \"--restful\")\n            {\n                type = Restful;\n                parameters.erase(iter);\n                break;\n            }\n            else\n            {\n                std::cout << ARGS_ERROR_STR << std::endl;\n                return;\n            }\n        }\n    }\n    if (type != Restful)\n        createController(parameters, type);\n    else\n    {\n        std::string resource;\n        for (auto iter = parameters.begin(); iter != parameters.end(); ++iter)\n        {\n            if ((*iter).find(\"--resource=\") == 0)\n            {\n                resource = (*iter).substr(strlen(\"--resource=\"));\n                parameters.erase(iter);\n                break;\n            }\n            if ((*iter)[0] == '-')\n            {\n                std::cerr << \"Error parameter for '\" << (*iter) << \"'\"\n                          << std::endl;\n                exit(1);\n            }\n        }\n        if (parameters.size() > 1)\n        {\n            std::cerr << \"Too many parameters\" << std::endl;\n            exit(1);\n        }\n        auto className = parameters[0];\n        createARestfulController(className, resource);\n    }\n}\n\nvoid create_controller::newSimpleControllerHeaderFile(\n    std::ofstream &file,\n    const std::string &className)\n{\n    file << \"#pragma once\\n\";\n    file << \"\\n\";\n    file << \"#include <drogon/HttpSimpleController.h>\\n\";\n    file << \"\\n\";\n    file << \"using namespace drogon;\\n\";\n    file << \"\\n\";\n    std::string class_name = className;\n    std::string namepace_path = \"/\";\n    auto pos = class_name.find(\"::\");\n    size_t namespaceCount = 0;\n    while (pos != std::string::npos)\n    {\n        ++namespaceCount;\n        auto namespaceName = class_name.substr(0, pos);\n        class_name = class_name.substr(pos + 2);\n        file << \"namespace \" << namespaceName << \"\\n\";\n        namepace_path.append(namespaceName).append(\"/\");\n        file << \"{\\n\";\n        pos = class_name.find(\"::\");\n    }\n    file << \"class \" << class_name << \" : public drogon::HttpSimpleController<\"\n         << class_name << \">\\n\";\n    file << \"{\\n\";\n    file << \"  public:\\n\";\n    file << \"    void asyncHandleHttpRequest(const HttpRequestPtr& \"\n            \"req, std::function<void (const HttpResponsePtr &)> &&callback) \"\n            \"override;\\n\";\n\n    file << \"    PATH_LIST_BEGIN\\n\";\n    file << \"    // list path definitions here;\\n\";\n    file << \"    \"\n            \"// PATH_ADD(\\\"/\"\n            \"path\\\", \\\"filter1\\\", \\\"filter2\\\", HttpMethod1, HttpMethod2...);\\n\";\n    file << \"    PATH_LIST_END\\n\";\n    file << \"};\\n\";\n    while (namespaceCount > 0)\n    {\n        --namespaceCount;\n        file << \"}\\n\";\n    }\n}\n\nvoid create_controller::newSimpleControllerSourceFile(\n    std::ofstream &file,\n    const std::string &className,\n    const std::string &filename)\n{\n    file << \"#include \\\"\" << filename << \".h\\\"\\n\";\n    file << \"\\n\";\n    auto pos = className.rfind(\"::\");\n    auto class_name = className;\n    if (pos != std::string::npos)\n    {\n        auto namespacename = className.substr(0, pos);\n        file << \"using namespace \" << namespacename << \";\\n\";\n        file << \"\\n\";\n        class_name = className.substr(pos + 2);\n    }\n    file << \"void \" << class_name\n         << \"::asyncHandleHttpRequest(const HttpRequestPtr& req, \"\n            \"std::function<void (const HttpResponsePtr &)> &&callback)\\n\";\n    file << \"{\\n\";\n    file << \"    // write your application logic here\\n\";\n    file << \"}\\n\";\n}\n\nvoid create_controller::newWebsockControllerHeaderFile(\n    std::ofstream &file,\n    const std::string &className)\n{\n    file << \"#pragma once\\n\";\n    file << \"\\n\";\n    file << \"#include <drogon/WebSocketController.h>\\n\";\n    file << \"\\n\";\n    file << \"using namespace drogon;\\n\";\n    file << \"\\n\";\n    std::string class_name = className;\n    std::string namepace_path = \"/\";\n    auto pos = class_name.find(\"::\");\n    size_t namespaceCount = 0;\n    while (pos != std::string::npos)\n    {\n        ++namespaceCount;\n        auto namespaceName = class_name.substr(0, pos);\n        class_name = class_name.substr(pos + 2);\n        file << \"namespace \" << namespaceName << \"\\n\";\n        namepace_path.append(namespaceName).append(\"/\");\n        file << \"{\\n\";\n        pos = class_name.find(\"::\");\n    }\n    file << \"class \" << class_name << \" : public drogon::WebSocketController<\"\n         << class_name << \">\\n\";\n    file << \"{\\n\";\n    file << \"  public:\\n\";\n    file << \"     void handleNewMessage(const WebSocketConnectionPtr&,\\n\";\n    file << \"                                  std::string &&,\\n\";\n    file << \"                                  const WebSocketMessageType &) \"\n            \"override;\\n\";\n    file << \"    void handleNewConnection(const HttpRequestPtr &,\\n\";\n    file << \"                                     const \"\n            \"WebSocketConnectionPtr&) override;\\n\";\n    file << \"    void handleConnectionClosed(const \"\n            \"WebSocketConnectionPtr&) override;\\n\";\n    file << \"    WS_PATH_LIST_BEGIN\\n\";\n    file << \"    // list path definitions here;\\n\";\n    file << \"    // WS_PATH_ADD(\\\"/path\\\", \\\"filter1\\\", \\\"filter2\\\", ...);\\n\";\n    file << \"    WS_PATH_LIST_END\\n\";\n    file << \"};\\n\";\n    while (namespaceCount > 0)\n    {\n        --namespaceCount;\n        file << \"}\\n\";\n    }\n}\n\nvoid create_controller::newWebsockControllerSourceFile(\n    std::ofstream &file,\n    const std::string &className,\n    const std::string &filename)\n{\n    file << \"#include \\\"\" << filename << \".h\\\"\\n\";\n    file << \"\\n\";\n    auto pos = className.rfind(\"::\");\n    auto class_name = className;\n    if (pos != std::string::npos)\n    {\n        auto namespacename = className.substr(0, pos);\n        file << \"using namespace \" << namespacename << \";\\n\";\n        file << \"\\n\";\n        class_name = className.substr(pos + 2);\n    }\n    file << \"void \" << class_name\n         << \"::handleNewMessage(const WebSocketConnectionPtr& wsConnPtr, \"\n            \"std::string &&message, const WebSocketMessageType &type)\\n\";\n    file << \"{\\n\";\n    file << \"    // write your application logic here\\n\";\n    file << \"}\\n\";\n    file << \"\\n\";\n    file << \"void \" << class_name\n         << \"::handleNewConnection(const HttpRequestPtr &req, const \"\n            \"WebSocketConnectionPtr& wsConnPtr)\\n\";\n    file << \"{\\n\";\n    file << \"    // write your application logic here\\n\";\n    file << \"}\\n\";\n    file << \"\\n\";\n    file << \"void \" << class_name\n         << \"::handleConnectionClosed(const WebSocketConnectionPtr& \"\n            \"wsConnPtr)\\n\";\n    file << \"{\\n\";\n    file << \"    // write your application logic here\\n\";\n    file << \"}\\n\";\n}\n\nvoid create_controller::newHttpControllerHeaderFile(\n    std::ofstream &file,\n    const std::string &className)\n{\n    file << \"#pragma once\\n\";\n    file << \"\\n\";\n    file << \"#include <drogon/HttpController.h>\\n\";\n    file << \"\\n\";\n    file << \"using namespace drogon;\\n\";\n    file << \"\\n\";\n    std::string class_name = className;\n    std::string namepace_path = \"/\";\n    auto pos = class_name.find(\"::\");\n    size_t namespaceCount = 0;\n    while (pos != std::string::npos)\n    {\n        ++namespaceCount;\n        auto namespaceName = class_name.substr(0, pos);\n        class_name = class_name.substr(pos + 2);\n        file << \"namespace \" << namespaceName << \"\\n\";\n        namepace_path.append(namespaceName).append(\"/\");\n        file << \"{\\n\";\n        pos = class_name.find(\"::\");\n    }\n    file << \"class \" << class_name << \" : public drogon::HttpController<\"\n         << class_name << \">\\n\";\n    file << \"{\\n\";\n    file << \"  public:\\n\";\n    file << \"    METHOD_LIST_BEGIN\\n\";\n    file << \"    // use METHOD_ADD to add your custom processing function \"\n            \"here;\\n\";\n    file << \"    // METHOD_ADD(\" << class_name\n         << \"::get, \\\"/{2}/{1}\\\", Get);\"\n            \" // path is \"\n         << namepace_path << class_name << \"/{arg2}/{arg1}\\n\";\n    file << \"    // METHOD_ADD(\" << class_name\n         << \"::your_method_name, \\\"/{1}/{2}/list\\\", Get);\"\n            \" // path is \"\n         << namepace_path << class_name << \"/{arg1}/{arg2}/list\\n\";\n    file << \"    // ADD_METHOD_TO(\" << class_name\n         << \"::your_method_name, \\\"/absolute/path/{1}/{2}/list\\\", Get);\"\n            \" // path is /absolute/path/{arg1}/{arg2}/list\\n\";\n    file << \"\\n\";\n    file << \"    METHOD_LIST_END\\n\";\n    file << \"    // your declaration of processing function maybe like this:\\n\";\n    file << \"    // void get(const HttpRequestPtr& req, \"\n            \"std::function<void (const HttpResponsePtr &)> &&callback, int \"\n            \"p1, std::string p2);\\n\";\n    file << \"    // void your_method_name(const HttpRequestPtr& req, \"\n            \"std::function<void (const HttpResponsePtr &)> &&callback, double \"\n            \"p1, int p2) const;\\n\";\n    file << \"};\\n\";\n    while (namespaceCount > 0)\n    {\n        --namespaceCount;\n        file << \"}\\n\";\n    }\n}\n\nvoid create_controller::newHttpControllerSourceFile(\n    std::ofstream &file,\n    const std::string &className,\n    const std::string &filename)\n{\n    file << \"#include \\\"\" << filename << \".h\\\"\\n\";\n    file << \"\\n\";\n    auto pos = className.rfind(\"::\");\n    auto class_name = className;\n    if (pos != std::string::npos)\n    {\n        auto namespacename = className.substr(0, pos);\n        file << \"using namespace \" << namespacename << \";\\n\";\n        file << \"\\n\";\n        class_name = className.substr(pos + 2);\n    }\n\n    file << \"// Add definition of your processing function here\\n\";\n}\n\nvoid create_controller::createController(std::vector<std::string> &httpClasses,\n                                         ControllerType type)\n{\n    for (auto iter = httpClasses.begin(); iter != httpClasses.end(); ++iter)\n    {\n        if ((*iter)[0] == '-')\n        {\n            std::cout << ARGS_ERROR_STR << std::endl;\n            return;\n        }\n    }\n    for (auto const &className : httpClasses)\n    {\n        createController(className, type);\n    }\n}\n\nvoid create_controller::createController(const std::string &className,\n                                         ControllerType type)\n{\n    std::regex regex(\"::\");\n    std::string ctlName =\n        std::regex_replace(className, regex, std::string(\"_\"));\n\n    std::string headFileName = ctlName + \".h\";\n    std::string sourceFilename = ctlName + \".cc\";\n    {\n        std::ifstream iHeadFile(headFileName.c_str(), std::ifstream::in);\n        std::ifstream iSourceFile(sourceFilename.c_str(), std::ifstream::in);\n\n        if (iHeadFile || iSourceFile)\n        {\n            std::cout << \"The file you want to create already exists, \"\n                         \"overwrite it(y/n)?\"\n                      << std::endl;\n            auto in = getchar();\n            (void)getchar();  // get the return key\n            if (in != 'Y' && in != 'y')\n            {\n                std::cout << \"Abort!\" << std::endl;\n                exit(0);\n            }\n        }\n    }\n    std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);\n    std::ofstream oSourceFile(sourceFilename.c_str(), std::ofstream::out);\n    if (!oHeadFile || !oSourceFile)\n    {\n        perror(\"\");\n        exit(1);\n    }\n    if (type == Http)\n    {\n        std::cout << \"Create a http controller: \" << className << std::endl;\n        newHttpControllerHeaderFile(oHeadFile, className);\n        newHttpControllerSourceFile(oSourceFile, className, ctlName);\n    }\n    else if (type == Simple)\n    {\n        std::cout << \"Create a http simple controller: \" << className\n                  << std::endl;\n        newSimpleControllerHeaderFile(oHeadFile, className);\n        newSimpleControllerSourceFile(oSourceFile, className, ctlName);\n    }\n    else if (type == WebSocket)\n    {\n        std::cout << \"Create a websocket controller: \" << className\n                  << std::endl;\n        newWebsockControllerHeaderFile(oHeadFile, className);\n        newWebsockControllerSourceFile(oSourceFile, className, ctlName);\n    }\n}\n\nvoid create_controller::createARestfulController(const std::string &className,\n                                                 const std::string &resource)\n{\n    std::regex regex(\"::\");\n    std::string ctlName =\n        std::regex_replace(className, regex, std::string(\"_\"));\n\n    std::string headFileName = ctlName + \".h\";\n    std::string sourceFilename = ctlName + \".cc\";\n    {\n        std::ifstream iHeadFile(headFileName.c_str(), std::ifstream::in);\n        std::ifstream iSourceFile(sourceFilename.c_str(), std::ifstream::in);\n\n        if (iHeadFile || iSourceFile)\n        {\n            std::cout << \"The file you want to create already exists, \"\n                         \"overwrite it(y/n)?\"\n                      << std::endl;\n            auto in = getchar();\n            (void)getchar();  // get the return key\n            if (in != 'Y' && in != 'y')\n            {\n                std::cout << \"Abort!\" << std::endl;\n                exit(0);\n            }\n        }\n    }\n    std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);\n    std::ofstream oSourceFile(sourceFilename.c_str(), std::ofstream::out);\n    if (!oHeadFile || !oSourceFile)\n    {\n        perror(\"\");\n        exit(1);\n    }\n    auto v = utils::splitString(className, \"::\");\n    drogon::DrTemplateData data;\n    data.insert(\"className\", v[v.size() - 1]);\n    v.pop_back();\n    data.insert(\"namespaceVector\", v);\n    data.insert(\"resource\", resource);\n    data.insert(\"fileName\", ctlName);\n    if (resource.empty())\n    {\n        data.insert(\"ctlCommand\",\n                    std::string(\"drogon_ctl create controller -r \") +\n                        className);\n    }\n    else\n    {\n        data.insert(\"ctlCommand\",\n                    std::string(\"drogon_ctl create controller -r \") +\n                        className + \" --resource=\" + resource);\n    }\n    try\n    {\n        auto templ = DrTemplateBase::newTemplate(\"restful_controller_h.csp\");\n        oHeadFile << templ->genText(data);\n        templ = DrTemplateBase::newTemplate(\"restful_controller_cc.csp\");\n        oSourceFile << templ->genText(data);\n    }\n    catch (const std::exception &err)\n    {\n        std::cerr << err.what() << std::endl;\n        exit(1);\n    }\n    std::cout << \"Create a http restful API controller: \" << className\n              << std::endl;\n    std::cout << \"File name: \" << ctlName << \".h and \" << ctlName << \".cc\"\n              << std::endl;\n}\n"
  },
  {
    "path": "drogon_ctl/create_controller.h",
    "content": "/**\n *\n *  create_controller.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include <drogon/DrTemplateBase.h>\n#include \"CommandHandler.h\"\nusing namespace drogon;\n\nnamespace drogon_ctl\n{\nclass create_controller : public DrObject<create_controller>,\n                          public CommandHandler\n{\n  public:\n    void handleCommand(std::vector<std::string> &parameters) override;\n\n    std::string script() override\n    {\n        return \"create controller files\";\n    }\n\n  protected:\n    enum ControllerType\n    {\n        Simple = 0,\n        Http,\n        WebSocket,\n        Restful\n    };\n\n    void createController(std::vector<std::string> &httpClasses,\n                          ControllerType type);\n    void createController(const std::string &className, ControllerType type);\n    void createARestfulController(const std::string &className,\n                                  const std::string &resource);\n\n    void newSimpleControllerHeaderFile(std::ofstream &file,\n                                       const std::string &className);\n    void newSimpleControllerSourceFile(std::ofstream &file,\n                                       const std::string &className,\n                                       const std::string &filename);\n\n    void newWebsockControllerHeaderFile(std::ofstream &file,\n                                        const std::string &className);\n    void newWebsockControllerSourceFile(std::ofstream &file,\n                                        const std::string &className,\n                                        const std::string &filename);\n\n    void newHttpControllerHeaderFile(std::ofstream &file,\n                                     const std::string &className);\n    void newHttpControllerSourceFile(std::ofstream &file,\n                                     const std::string &className,\n                                     const std::string &filename);\n};\n}  // namespace drogon_ctl\n"
  },
  {
    "path": "drogon_ctl/create_filter.cc",
    "content": "/**\n *\n *  create_filter.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"create_filter.h\"\n#include <drogon/DrTemplateBase.h>\n#include <drogon/utils/Utilities.h>\n\n#include <string>\n#include <iostream>\n#ifndef _WIN32\n#include <unistd.h>\n#endif\n#include <fstream>\n#include <regex>\n\n#include <sys/stat.h>\n#include <sys/types.h>\n\nusing namespace drogon_ctl;\n\nstatic void createFilterHeaderFile(std::ofstream &file,\n                                   const std::string &className,\n                                   const std::string &fileName)\n{\n    auto templ = drogon::DrTemplateBase::newTemplate(\"filter_h\");\n    HttpViewData data;\n    if (className.find(\"::\") != std::string::npos)\n    {\n        auto namespaceVector = utils::splitString(className, \"::\");\n        data.insert(\"className\", namespaceVector.back());\n        namespaceVector.pop_back();\n        data.insert(\"namespaceVector\", namespaceVector);\n    }\n    else\n    {\n        data.insert(\"className\", className);\n    }\n    data.insert(\"filename\", fileName);\n    file << templ->genText(data);\n}\n\nstatic void createFilterSourceFile(std::ofstream &file,\n                                   const std::string &className,\n                                   const std::string &fileName)\n{\n    auto templ = drogon::DrTemplateBase::newTemplate(\"filter_cc\");\n    HttpViewData data;\n    if (className.find(\"::\") != std::string::npos)\n    {\n        auto pos = className.rfind(\"::\");\n        data.insert(\"namespaceString\", className.substr(0, pos));\n        data.insert(\"className\", className.substr(pos + 2));\n    }\n    else\n    {\n        data.insert(\"className\", className);\n    }\n    data.insert(\"filename\", fileName);\n    file << templ->genText(data);\n}\n\nvoid create_filter::handleCommand(std::vector<std::string> &parameters)\n{\n    if (parameters.size() < 1)\n    {\n        std::cout << \"Invalid parameters!\" << std::endl;\n    }\n    for (auto className : parameters)\n    {\n        std::regex regex(\"::\");\n        std::string fileName =\n            std::regex_replace(className, regex, std::string(\"_\"));\n\n        std::string headFileName = fileName + \".h\";\n        std::string sourceFilename = fileName + \".cc\";\n        {\n            std::ifstream iHeadFile(headFileName.c_str(), std::ifstream::in);\n            std::ifstream iSourceFile(sourceFilename.c_str(),\n                                      std::ifstream::in);\n\n            if (iHeadFile || iSourceFile)\n            {\n                std::cout << \"The file you want to create already exists, \"\n                             \"overwrite it(y/n)?\"\n                          << std::endl;\n                auto in = getchar();\n                (void)getchar();  // get the return key\n                if (in != 'Y' && in != 'y')\n                {\n                    std::cout << \"Abort!\" << std::endl;\n                    exit(0);\n                }\n            }\n        }\n        std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);\n        std::ofstream oSourceFile(sourceFilename.c_str(), std::ofstream::out);\n        if (!oHeadFile || !oSourceFile)\n        {\n            perror(\"\");\n            exit(1);\n        }\n\n        std::cout << \"create a http filter:\" << className << std::endl;\n        createFilterHeaderFile(oHeadFile, className, fileName);\n        createFilterSourceFile(oSourceFile, className, fileName);\n    }\n}\n"
  },
  {
    "path": "drogon_ctl/create_filter.h",
    "content": "/**\n *\n *  create_filter.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include \"CommandHandler.h\"\nusing namespace drogon;\n\nnamespace drogon_ctl\n{\nclass create_filter : public DrObject<create_filter>, public CommandHandler\n{\n  public:\n    void handleCommand(std::vector<std::string> &parameters) override;\n\n    std::string script() override\n    {\n        return \"create filter class files\";\n    }\n\n  protected:\n    std::string outputPath_{\".\"};\n};\n}  // namespace drogon_ctl\n"
  },
  {
    "path": "drogon_ctl/create_model.cc",
    "content": "/**\n *\n *  create_model.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"create_model.h\"\n#include \"cmd.h\"\n#include <drogon/config.h>\n#include <drogon/utils/Utilities.h>\n#include <drogon/HttpViewData.h>\n#include <drogon/DrTemplateBase.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <iostream>\n#include <fstream>\n#include <regex>\n#include <algorithm>\n#ifndef _WIN32\n#include <unistd.h>\n#include <dirent.h>\n#include <dlfcn.h>\n#else\n#include <io.h>\n#endif\n#include <thread>\n#include <chrono>\n\nusing namespace std::chrono_literals;\nusing namespace drogon_ctl;\n\nstatic std::string toLower(const std::string &str)\n{\n    auto ret = str;\n    std::transform(ret.begin(), ret.end(), ret.begin(), [](unsigned char c) {\n        return tolower(c);\n    });\n    return ret;\n}\n\nstatic std::string escapeConnString(const std::string &str)\n{\n    bool beQuoted = str.empty() || (str.find(' ') != std::string::npos);\n\n    std::string escaped;\n    escaped.reserve(str.size());\n    for (auto ch : str)\n    {\n        if (ch == '\\'')\n            escaped.push_back('\\\\');\n        else if (ch == '\\\\')\n            escaped.push_back('\\\\');\n        escaped.push_back(ch);\n    }\n\n    if (beQuoted)\n        return \"'\" + escaped + \"'\";\n    return escaped;\n}\n\nstd::string drogon_ctl::escapeIdentifier(const std::string &identifier,\n                                         const std::string &rdbms)\n{\n    if (rdbms != \"postgresql\")\n    {\n        return identifier;\n    }\n\n    return \"\\\\\\\"\" + identifier + \"\\\\\\\"\";\n}\n\nstatic std::map<std::string, std::vector<ConvertMethod>> getConvertMethods(\n    const Json::Value &convertColumns)\n{\n    std::map<std::string, std::vector<ConvertMethod>> ret;\n    auto enabled = convertColumns.get(\"enabled\", false).asBool();\n    if (!enabled)\n    {\n        return ret;\n    }  // endif\n    auto items = convertColumns[\"items\"];\n    if (items.isNull())\n    {\n        return ret;\n    }  // endif\n    if (!items.isArray())\n    {\n        std::cerr << \"items of convert must be an array\" << std::endl;\n        exit(1);\n    }  // endif\n    for (auto &convertColumn : items)\n    {\n        try\n        {\n            ConvertMethod c(convertColumn);\n            ret[c.tableName()].push_back(c);\n        }  // try\n        catch (const std::runtime_error &e)\n        {\n            std::cerr << e.what() << std::endl;\n            exit(1);\n        }  // catch\n    }      // for\n\n    return ret;\n}\n\nstatic std::map<std::string, std::vector<Relationship>> getRelationships(\n    const Json::Value &relationships)\n{\n    std::map<std::string, std::vector<Relationship>> ret;\n    auto enabled = relationships.get(\"enabled\", false).asBool();\n    if (!enabled)\n        return ret;\n    auto items = relationships[\"items\"];\n    if (items.isNull())\n        return ret;\n    if (!items.isArray())\n    {\n        std::cerr << \"items must be an array\\n\";\n        exit(1);\n    }\n    for (auto &relationship : items)\n    {\n        try\n        {\n            Relationship r(relationship);\n            ret[r.originalTableName()].push_back(r);\n            if (r.enableReverse() &&\n                (r.originalTableName() != r.targetTableName() ||\n                 r.originalTableAlias() != r.targetTableAlias()))\n            {\n                auto reverse = r.reverse();\n                ret[reverse.originalTableName()].push_back(reverse);\n            }\n        }\n        catch (const std::runtime_error &e)\n        {\n            std::cerr << e.what() << std::endl;\n            exit(1);\n        }\n    }\n    return ret;\n}\n\nbool drogon_ctl::ConvertMethod::shouldConvert(const std::string &tableName,\n                                              const std::string &colName) const\n{\n    if (tableName == \"*\")\n    {\n        return colName == colName_;\n    }\n    else\n    {\n        return (tableName == tableName_ && colName == colName_);\n    }  // endif\n}\n\n#if USE_POSTGRESQL\nvoid create_model::createModelClassFromPG(\n    const std::string &path,\n    const DbClientPtr &client,\n    const std::string &tableName,\n    const std::string &schema,\n    const Json::Value &restfulApiConfig,\n    const std::vector<Relationship> &relationships,\n    const std::vector<ConvertMethod> &convertMethods)\n{\n    auto className = nameTransform(tableName, true);\n    HttpViewData data;\n    data[\"className\"] = className;\n    data[\"tableName\"] = tableName;\n    data[\"hasPrimaryKey\"] = (int)0;\n    data[\"primaryKeyName\"] = \"\";\n    data[\"dbName\"] = dbname_;\n    data[\"rdbms\"] = std::string(\"postgresql\");\n    data[\"relationships\"] = relationships;\n    data[\"convertMethods\"] = convertMethods;\n    if (schema != \"public\")\n    {\n        data[\"schema\"] = schema;\n    }\n    std::vector<ColumnInfo> cols;\n    *client << \"SELECT * \"\n               \"FROM information_schema.columns \"\n               \"WHERE table_schema = $1 \"\n               \"AND table_name   = $2\"\n            << schema << tableName << Mode::Blocking >>\n        [&](const Result &r) {\n            if (r.size() == 0)\n            {\n                std::cout << \"    ---Can't create model from the table \"\n                          << tableName\n                          << \", please check privileges on the table.\"\n                          << std::endl;\n                return;\n            }\n            for (Result::SizeType i = 0; i < r.size(); ++i)\n            {\n                auto row = r[i];\n                ColumnInfo info;\n                info.index_ = i;\n                info.dbType_ = \"pg\";\n                info.colName_ = row[\"column_name\"].as<std::string>();\n                info.colTypeName_ = nameTransform(info.colName_, true);\n                info.colValName_ = nameTransform(info.colName_, false);\n                auto isNullAble = row[\"is_nullable\"].as<std::string>();\n                info.notNull_ = isNullAble == \"YES\" ? false : true;\n                auto type = row[\"data_type\"].as<std::string>();\n                info.colDatabaseType_ = type;\n                if (type == \"smallint\")\n                {\n                    info.colType_ = \"short\";\n                    info.colLength_ = 2;\n                }\n                else if (type == \"integer\")\n                {\n                    info.colType_ = \"int32_t\";\n                    info.colLength_ = 4;\n                }\n                else if (type == \"bigint\")\n                {\n                    info.colType_ = \"int64_t\";\n                    info.colLength_ = 8;\n                }\n                else if (type == \"real\")\n                {\n                    info.colType_ = \"float\";\n                    info.colLength_ = sizeof(float);\n                }\n                else if (type == \"double precision\")\n                {\n                    info.colType_ = \"double\";\n                    info.colLength_ = sizeof(double);\n                }\n                else if (type == \"character varying\")\n                {\n                    info.colType_ = \"std::string\";\n                    if (!row[\"character_maximum_length\"].isNull())\n                        info.colLength_ =\n                            row[\"character_maximum_length\"].as<ssize_t>();\n                }\n                else if (type == \"boolean\")\n                {\n                    info.colType_ = \"bool\";\n                    info.colLength_ = 1;\n                }\n                else if (type == \"date\")\n                {\n                    info.colType_ = \"::trantor::Date\";\n                }\n                else if (type.find(\"timestamp\") != std::string::npos)\n                {\n                    info.colType_ = \"::trantor::Date\";\n                }\n                else if (type == \"bytea\")\n                {\n                    info.colType_ = \"std::vector<char>\";\n                }\n                else if (type.find(\"numeric\") != std::string::npos)\n                {\n                    info.colType_ = \"std::string\";\n                }\n                else\n                {\n                    info.colType_ = \"std::string\";\n                }\n                auto defaultVal = row[\"column_default\"].as<std::string>();\n                if (!defaultVal.empty())\n                {\n                    info.hasDefaultVal_ = true;\n                    if (defaultVal.find(\"nextval(\") == 0)\n                    {\n                        info.isAutoVal_ = true;\n                    }\n                }\n                auto isIdentity = row[\"is_identity\"].as<std::string>();\n                if (isIdentity == \"YES\")\n                {\n                    info.isAutoVal_ = true;\n                }\n                cols.push_back(std::move(info));\n            }\n        } >>\n        [](const DrogonDbException &e) {\n            std::cerr << e.base().what() << std::endl;\n            exit(1);\n        };\n    size_t pkNumber = 0;\n    *client << \"SELECT \"\n               \"pg_constraint.conname AS pk_name,\"\n               \"pg_constraint.conkey AS pk_vector \"\n               \"FROM pg_constraint \"\n               \"INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid \"\n               \"WHERE \"\n               \"pg_class.relname = $1 \"\n               \"AND pg_constraint.contype = 'p'\"\n            << tableName << Mode::Blocking >>\n        [&](bool isNull,\n            const std::string &pkName,\n            const std::vector<std::shared_ptr<short>> &pk) {\n            if (!isNull)\n            {\n                pkNumber = pk.size();\n            }\n        } >>\n        [](const DrogonDbException &e) {\n            std::cerr << e.base().what() << std::endl;\n            exit(1);\n        };\n    data[\"hasPrimaryKey\"] = (int)pkNumber;\n    if (pkNumber == 1)\n    {\n        *client << \"SELECT \"\n                   \"pg_attribute.attname AS colname,\"\n                   \"pg_type.typname AS typename,\"\n                   \"pg_constraint.contype AS contype \"\n                   \"FROM pg_constraint \"\n                   \"INNER JOIN pg_class ON pg_constraint.conrelid = \"\n                   \"pg_class.oid \"\n                   \"INNER JOIN pg_attribute ON pg_attribute.attrelid = \"\n                   \"pg_class.oid \"\n                   \"AND pg_attribute.attnum = pg_constraint.conkey [ 1 ] \"\n                   \"INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid \"\n                   \"WHERE pg_class.relname = $1 and pg_constraint.contype='p'\"\n                << tableName << Mode::Blocking >>\n            [&](bool isNull,\n                const std::string &colName,\n                const std::string &type) {\n                if (isNull)\n                    return;\n\n                data[\"primaryKeyName\"] = colName;\n                for (auto &col : cols)\n                {\n                    if (col.colName_ == colName)\n                    {\n                        col.isPrimaryKey_ = true;\n                        data[\"primaryKeyType\"] = col.colType_;\n                    }\n                }\n            } >>\n            [](const DrogonDbException &e) {\n                std::cerr << e.base().what() << std::endl;\n                exit(1);\n            };\n    }\n    else if (pkNumber > 1)\n    {\n        std::vector<std::string> pkNames, pkTypes, pkValNames;\n        for (size_t i = 1; i <= pkNumber; ++i)\n        {\n            *client << \"SELECT \"\n                       \"pg_attribute.attname AS colname,\"\n                       \"pg_type.typname AS typename,\"\n                       \"pg_constraint.contype AS contype \"\n                       \"FROM pg_constraint \"\n                       \"INNER JOIN pg_class ON pg_constraint.conrelid = \"\n                       \"pg_class.oid \"\n                       \"INNER JOIN pg_attribute ON pg_attribute.attrelid = \"\n                       \"pg_class.oid \"\n                       \"AND pg_attribute.attnum = pg_constraint.conkey [ $1 ] \"\n                       \"INNER JOIN pg_type ON pg_type.oid = \"\n                       \"pg_attribute.atttypid \"\n                       \"WHERE pg_class.relname = $2 and \"\n                       \"pg_constraint.contype='p'\"\n                    << (int)i << tableName << Mode::Blocking >>\n                [&](bool isNull, std::string colName, const std::string &type) {\n                    if (isNull)\n                        return;\n                    pkNames.push_back(colName);\n                    pkValNames.push_back(nameTransform(colName, false));\n                    for (auto &col : cols)\n                    {\n                        if (col.colName_ == colName)\n                        {\n                            col.isPrimaryKey_ = true;\n                            pkTypes.push_back(col.colType_);\n                        }\n                    }\n                } >>\n                [](const DrogonDbException &e) {\n                    std::cerr << e.base().what() << std::endl;\n                    exit(1);\n                };\n        }\n        data[\"primaryKeyName\"] = pkNames;\n        data[\"primaryKeyType\"] = pkTypes;\n        data[\"primaryKeyValNames\"] = pkValNames;\n    }\n\n    data[\"columns\"] = cols;\n    std::ofstream headerFile(path + \"/\" + className + \".h\", std::ofstream::out);\n    std::ofstream sourceFile(path + \"/\" + className + \".cc\",\n                             std::ofstream::out);\n    auto templ = DrTemplateBase::newTemplate(\"model_h.csp\");\n    headerFile << templ->genText(data);\n    templ = DrTemplateBase::newTemplate(\"model_cc.csp\");\n    sourceFile << templ->genText(data);\n    createRestfulAPIController(data, restfulApiConfig);\n}\n\nvoid create_model::createModelFromPG(\n    const std::string &path,\n    const DbClientPtr &client,\n    const std::string &schema,\n    const Json::Value &restfulApiConfig,\n    std::map<std::string, std::vector<Relationship>> &relationships,\n    std::map<std::string, std::vector<ConvertMethod>> &convertMethods)\n{\n    *client << \"SELECT a.oid,\"\n               \"a.relname AS name,\"\n               \"b.description AS comment \"\n               \"FROM pg_class a \"\n               \"LEFT OUTER JOIN pg_description b ON b.objsubid = 0 AND a.oid = \"\n               \"b.objoid \"\n               \"WHERE a.relnamespace = (SELECT oid FROM pg_namespace WHERE \"\n               \"nspname = $1) \"\n               \"AND a.relkind = 'r' ORDER BY a.relname\"\n            << schema << Mode::Blocking >>\n        [&](bool isNull,\n            size_t oid,\n            std::string &&tableName,\n            const std::string &comment) {\n            if (!isNull)\n            {\n                std::cout << \"table name:\" << tableName << std::endl;\n\n                createModelClassFromPG(path,\n                                       client,\n                                       tableName,\n                                       schema,\n                                       restfulApiConfig,\n                                       relationships[tableName],\n                                       convertMethods[tableName]);\n            }\n        } >>\n        [](const DrogonDbException &e) {\n            std::cerr << e.base().what() << std::endl;\n            exit(1);\n        };\n}\n#endif\n\n#if USE_MYSQL\nvoid create_model::createModelClassFromMysql(\n    const std::string &path,\n    const DbClientPtr &client,\n    const std::string &tableName,\n    const Json::Value &restfulApiConfig,\n    const std::vector<Relationship> &relationships,\n    const std::vector<ConvertMethod> &convertMethods)\n{\n    auto className = nameTransform(tableName, true);\n    HttpViewData data;\n    data[\"className\"] = className;\n    data[\"tableName\"] = toLower(tableName);\n    data[\"hasPrimaryKey\"] = (int)0;\n    data[\"primaryKeyName\"] = \"\";\n    data[\"dbName\"] = dbname_;\n    data[\"rdbms\"] = std::string(\"mysql\");\n    data[\"relationships\"] = relationships;\n    data[\"convertMethods\"] = convertMethods;\n    std::vector<ColumnInfo> cols;\n    int i = 0;\n    *client << \"desc `\" + tableName + \"`\" << Mode::Blocking >>\n        [&i, &cols](bool isNull,\n                    const std::string &field,\n                    const std::string &type,\n                    const std::string &isNullAble,\n                    const std::string &key,\n                    const std::string &defaultVal,\n                    const std::string &extra) {\n            if (!isNull)\n            {\n                ColumnInfo info;\n                info.index_ = i;\n                info.dbType_ = \"mysql\";\n                info.colName_ = field;\n                info.colTypeName_ = nameTransform(info.colName_, true);\n                info.colValName_ = nameTransform(info.colName_, false);\n                info.notNull_ = isNullAble == \"YES\" ? false : true;\n                info.colDatabaseType_ = type;\n                info.isPrimaryKey_ = key == \"PRI\" ? true : false;\n                if (type.find(\"tinyint\") == 0)\n                {\n                    info.colType_ = \"int8_t\";\n                    info.colLength_ = 1;\n                }\n                else if (type.find(\"smallint\") == 0)\n                {\n                    info.colType_ = \"int16_t\";\n                    info.colLength_ = 2;\n                }\n                else if (type.find(\"mediumint\") == 0)\n                {\n                    info.colType_ = \"int32_t\";\n                    info.colLength_ = 3;\n                }\n                else if (type.find(\"int\") == 0)\n                {\n                    info.colType_ = \"int32_t\";\n                    info.colLength_ = 4;\n                }\n                else if (type.find(\"bigint\") == 0)\n                {\n                    info.colType_ = \"int64_t\";\n                    info.colLength_ = 8;\n                }\n                else if (type.find(\"float\") == 0)\n                {\n                    info.colType_ = \"float\";\n                    info.colLength_ = sizeof(float);\n                }\n                else if (type.find(\"double\") == 0)\n                {\n                    info.colType_ = \"double\";\n                    info.colLength_ = sizeof(double);\n                }\n                else if (type.find(\"date\") == 0 || type.find(\"datetime\") == 0 ||\n                         type.find(\"timestamp\") == 0)\n                {\n                    info.colType_ = \"::trantor::Date\";\n                }\n                else if (type.find(\"blob\") != std::string::npos)\n                {\n                    info.colType_ = \"std::vector<char>\";\n                }\n                else if (type.find(\"varchar\") != std::string::npos)\n                {\n                    info.colType_ = \"std::string\";\n                    auto pos1 = type.find(\"(\");\n                    auto pos2 = type.find(\")\");\n                    if (pos1 != std::string::npos &&\n                        pos2 != std::string::npos && pos2 - pos1 > 1)\n                    {\n                        info.colLength_ =\n                            std::stoll(type.substr(pos1 + 1, pos2 - pos1 - 1));\n                    }\n                }\n                else\n                {\n                    info.colType_ = \"std::string\";\n                }\n                if (type.find(\"unsigned\") != std::string::npos &&\n                    info.colType_ != \"std::string\")\n                {\n                    info.colType_ = \"u\" + info.colType_;\n                }\n                if (!defaultVal.empty())\n                {\n                    info.hasDefaultVal_ = true;\n                }\n                if (extra.find(\"auto_\") == 0)\n                {\n                    info.isAutoVal_ = true;\n                }\n                cols.push_back(std::move(info));\n                ++i;\n            }\n        } >>\n        [](const DrogonDbException &e) {\n            std::cerr << e.base().what() << std::endl;\n            exit(1);\n        };\n    std::vector<std::string> pkNames, pkTypes, pkValNames;\n    for (auto const &col : cols)\n    {\n        if (col.isPrimaryKey_)\n        {\n            pkNames.push_back(col.colName_);\n            pkValNames.push_back(nameTransform(col.colName_, false));\n            pkTypes.push_back(col.colType_);\n        }\n    }\n    data[\"hasPrimaryKey\"] = (int)pkNames.size();\n    if (pkNames.size() == 1)\n    {\n        data[\"primaryKeyName\"] = pkNames[0];\n        data[\"primaryKeyType\"] = pkTypes[0];\n    }\n    else if (pkNames.size() > 1)\n    {\n        data[\"primaryKeyName\"] = pkNames;\n        data[\"primaryKeyType\"] = pkTypes;\n        data[\"primaryKeyValNames\"] = pkValNames;\n    }\n    data[\"columns\"] = cols;\n    std::ofstream headerFile(path + \"/\" + className + \".h\", std::ofstream::out);\n    std::ofstream sourceFile(path + \"/\" + className + \".cc\",\n                             std::ofstream::out);\n    auto templ = DrTemplateBase::newTemplate(\"model_h.csp\");\n    headerFile << templ->genText(data);\n    templ = DrTemplateBase::newTemplate(\"model_cc.csp\");\n    sourceFile << templ->genText(data);\n    createRestfulAPIController(data, restfulApiConfig);\n}\n\nvoid create_model::createModelFromMysql(\n    const std::string &path,\n    const DbClientPtr &client,\n    const Json::Value &restfulApiConfig,\n    std::map<std::string, std::vector<Relationship>> &relationships,\n    std::map<std::string, std::vector<ConvertMethod>> &convertMethods)\n{\n    *client << \"show tables\" << Mode::Blocking >> [&](bool isNull,\n                                                      std::string &&tableName) {\n        if (!isNull)\n        {\n            std::cout << \"table name:\" << tableName << std::endl;\n            createModelClassFromMysql(path,\n                                      client,\n                                      tableName,\n                                      restfulApiConfig,\n                                      relationships[tableName],\n                                      convertMethods[tableName]);\n        }\n    } >> [](const DrogonDbException &e) {\n        std::cerr << e.base().what() << std::endl;\n        exit(1);\n    };\n}\n#endif\n#if USE_SQLITE3\nvoid create_model::createModelClassFromSqlite3(\n    const std::string &path,\n    const DbClientPtr &client,\n    const std::string &tableName,\n    const Json::Value &restfulApiConfig,\n    const std::vector<Relationship> &relationships,\n    const std::vector<ConvertMethod> &convertMethods)\n{\n    HttpViewData data;\n    auto className = nameTransform(tableName, true);\n    data[\"className\"] = className;\n    data[\"tableName\"] = toLower(tableName);\n    data[\"hasPrimaryKey\"] = (int)0;\n    data[\"primaryKeyName\"] = \"\";\n    data[\"dbName\"] = std::string(\"sqlite3\");\n    data[\"rdbms\"] = std::string(\"sqlite3\");\n    data[\"relationships\"] = relationships;\n    data[\"convertMethods\"] = convertMethods;\n    std::vector<ColumnInfo> cols;\n    std::string sql = \"PRAGMA table_info(\" + tableName + \");\";\n    *client << sql << Mode::Blocking >> [&](const Result &result) {\n        size_t index = 0;\n        for (auto &row : result)\n        {\n            bool notnull = row[\"notnull\"].as<bool>();\n            bool primary = row[\"pk\"].as<int>();\n            auto type = row[\"type\"].as<std::string>();\n            std::transform(type.begin(),\n                           type.end(),\n                           type.begin(),\n                           [](unsigned char c) { return tolower(c); });\n            ColumnInfo info;\n            info.index_ = index++;\n            info.dbType_ = \"sqlite3\";\n            info.colName_ = row[\"name\"].as<std::string>();\n            info.colTypeName_ = nameTransform(info.colName_, true);\n            info.colValName_ = nameTransform(info.colName_, false);\n            info.notNull_ = notnull;\n            info.colDatabaseType_ = type;\n            info.isPrimaryKey_ = primary;\n            if (primary)\n            {\n                if (type == \"integer\")\n                {\n                    info.isAutoVal_ = true;\n                }\n                else\n                {\n                    *client << \"SELECT sql FROM sqlite_master WHERE name=? and \"\n                               \"(type='table' or type='view');\"\n                            << tableName << Mode::Blocking >>\n                        [&](bool isNull, std::string sql) {\n                            if (!isNull)\n                            {\n                                std::transform(sql.begin(),\n                                               sql.end(),\n                                               sql.begin(),\n                                               [](unsigned char c) {\n                                                   return tolower(c);\n                                               });\n                                if (sql.find(\"autoincrement\") !=\n                                    std::string::npos)\n                                {\n                                    info.isAutoVal_ = true;\n                                }\n                            }\n                        } >>\n                        [](const DrogonDbException &e) {\n\n                        };\n                }\n            }\n            auto defaultVal = row[\"dflt_value\"].as<std::string>();\n            if (!defaultVal.empty())\n            {\n                info.hasDefaultVal_ = true;\n            }\n\n            if (type.find(\"int\") != std::string::npos)\n            {\n                info.colType_ = \"int64_t\";\n                info.colLength_ = 8;\n            }\n            else if (type.find(\"char\") != std::string::npos || type == \"text\" ||\n                     type == \"clob\")\n            {\n                info.colType_ = \"std::string\";\n            }\n            else if (type.find(\"double\") != std::string::npos ||\n                     type == \"real\" || type == \"float\")\n            {\n                info.colType_ = \"double\";\n                info.colLength_ = sizeof(double);\n            }\n            else if (type == \"bool\")\n            {\n                info.colType_ = \"bool\";\n                info.colLength_ = 1;\n            }\n            else if (type == \"blob\")\n            {\n                info.colType_ = \"std::vector<char>\";\n            }\n            else if (type == \"datetime\" || type == \"date\")\n            {\n                info.colType_ = \"::trantor::Date\";\n            }\n            else\n            {\n                info.colType_ = \"std::string\";\n            }\n            cols.push_back(std::move(info));\n        }\n    } >> [](const DrogonDbException &e) {\n        std::cerr << e.base().what() << std::endl;\n        exit(1);\n    };\n    std::vector<std::string> pkNames, pkTypes, pkValNames;\n    for (auto const &col : cols)\n    {\n        if (col.isPrimaryKey_)\n        {\n            pkNames.push_back(col.colName_);\n            pkTypes.push_back(col.colType_);\n            pkValNames.push_back(nameTransform(col.colName_, false));\n        }\n    }\n    data[\"hasPrimaryKey\"] = (int)pkNames.size();\n    if (pkNames.size() == 1)\n    {\n        data[\"primaryKeyName\"] = pkNames[0];\n        data[\"primaryKeyType\"] = pkTypes[0];\n    }\n    else if (pkNames.size() > 1)\n    {\n        for (auto &col : cols)\n        {\n            col.isAutoVal_ = false;\n        }\n\n        data[\"primaryKeyName\"] = pkNames;\n        data[\"primaryKeyType\"] = pkTypes;\n        data[\"primaryKeyValNames\"] = pkValNames;\n    }\n    data[\"columns\"] = cols;\n    std::ofstream headerFile(path + \"/\" + className + \".h\", std::ofstream::out);\n    std::ofstream sourceFile(path + \"/\" + className + \".cc\",\n                             std::ofstream::out);\n    auto templ = DrTemplateBase::newTemplate(\"model_h.csp\");\n    headerFile << templ->genText(data);\n    templ = DrTemplateBase::newTemplate(\"model_cc.csp\");\n    sourceFile << templ->genText(data);\n    createRestfulAPIController(data, restfulApiConfig);\n}\n\nvoid create_model::createModelFromSqlite3(\n    const std::string &path,\n    const DbClientPtr &client,\n    const Json::Value &restfulApiConfig,\n    std::map<std::string, std::vector<Relationship>> &relationships,\n    std::map<std::string, std::vector<ConvertMethod>> &convertMethods)\n{\n    *client << \"SELECT name FROM sqlite_master WHERE name!='sqlite_sequence' \"\n               \"and (type='table' or type='view') ORDER BY name;\"\n            << Mode::Blocking >>\n        [&](bool isNull, std::string &&tableName) mutable {\n            if (!isNull)\n            {\n                std::cout << \"table name:\" << tableName << std::endl;\n                createModelClassFromSqlite3(path,\n                                            client,\n                                            tableName,\n                                            restfulApiConfig,\n                                            relationships[tableName],\n                                            convertMethods[tableName]);\n            }\n        } >>\n        [](const DrogonDbException &e) {\n            std::cerr << e.base().what() << std::endl;\n            exit(1);\n        };\n}\n#endif\n\nvoid create_model::createModel(const std::string &path,\n                               const Json::Value &config,\n                               const std::string &singleModelName)\n{\n    auto dbType = config.get(\"rdbms\", \"no dbms\").asString();\n    std::transform(dbType.begin(),\n                   dbType.end(),\n                   dbType.begin(),\n                   [](unsigned char c) { return tolower(c); });\n    auto restfulApiConfig = config[\"restful_api_controllers\"];\n    auto relationships = getRelationships(config[\"relationships\"]);\n    auto convertMethods = getConvertMethods(config[\"convert\"]);\n\n    drogon::utils::createPath(path);\n\n    if (cleanupDirectory_)\n    {\n        std::cout << \"Source files (*.h, *.cc) in '\" << path\n                  << \"' folder will be deleted, continue(y/n)?\\n\";\n        auto in = getchar();\n        (void)getchar();  // get the return key\n        if (in != 'Y' && in != 'y')\n        {\n            std::cout << \"Abort!\" << std::endl;\n            exit(0);\n        }\n\n        for (const auto &entry : std::filesystem::directory_iterator(path))\n        {\n            if (!entry.is_regular_file())\n                continue;\n\n            const std::filesystem::path &file = entry.path();\n            std::string ext = file.extension().string();\n\n            if (ext == \".h\" || ext == \".cc\")\n            {\n                std::cout << \"Removing: \" << file << \"\\n\";\n                std::error_code ret;\n                std::filesystem::remove(file, ret);\n                if (ret)\n                {\n                    std::cerr << \"Failed to remove '\" << file\n                              << \"' : \" << ret.message() << \"\\n\";\n                }\n            }\n        }\n    }\n    if (dbType == \"postgresql\")\n    {\n#if USE_POSTGRESQL\n        std::cout << \"postgresql\" << std::endl;\n        auto host = config.get(\"host\", \"127.0.0.1\").asString();\n        auto port = config.get(\"port\", 5432).asUInt();\n        auto dbname = config.get(\"dbname\", \"\").asString();\n        if (dbname == \"\")\n        {\n            std::cerr << \"Please configure dbname in \" << path << \"/model.json \"\n                      << std::endl;\n            exit(1);\n        }\n        dbname_ = dbname;\n        auto user = config.get(\"user\", \"\").asString();\n        if (user == \"\")\n        {\n            std::cerr << \"Please configure user in \" << path << \"/model.json \"\n                      << std::endl;\n            exit(1);\n        }\n        auto password = config.get(\"passwd\", \"\").asString();\n        if (password.empty())\n        {\n            password = config.get(\"password\", \"\").asString();\n        }\n\n        auto connStr =\n            utils::formattedString(\"host=%s port=%u dbname=%s user=%s\",\n                                   escapeConnString(host).c_str(),\n                                   port,\n                                   escapeConnString(dbname).c_str(),\n                                   escapeConnString(user).c_str());\n        if (!password.empty())\n        {\n            connStr += \" password=\";\n            connStr += escapeConnString(password);\n        }\n        auto characterSet = config.get(\"client_encoding\", \"\").asString();\n        if (!characterSet.empty())\n        {\n            connStr += \" client_encoding=\";\n            connStr += escapeConnString(characterSet);\n        }\n\n        auto schema = config.get(\"schema\", \"public\").asString();\n        DbClientPtr client = drogon::orm::DbClient::newPgClient(connStr, 1);\n        std::cout << \"Connect to server...\" << std::endl;\n        if (forceOverwrite_)\n        {\n            std::this_thread::sleep_for(2s);\n        }\n        else\n        {\n            std::cout << \"Source files in the \" << path\n                      << \" folder will be overwritten, continue(y/n)?\\n\";\n            auto in = getchar();\n            (void)getchar();  // get the return key\n            if (in != 'Y' && in != 'y')\n            {\n                std::cout << \"Abort!\" << std::endl;\n                exit(0);\n            }\n        }\n\n        if (singleModelName.empty())\n        {\n            auto tables = config[\"tables\"];\n            if (!tables || tables.size() == 0)\n                createModelFromPG(path,\n                                  client,\n                                  schema,\n                                  restfulApiConfig,\n                                  relationships,\n                                  convertMethods);\n            else\n            {\n                for (int i = 0; i < (int)tables.size(); ++i)\n                {\n                    auto tableName = tables[i].asString();\n                    std::transform(tableName.begin(),\n                                   tableName.end(),\n                                   tableName.begin(),\n                                   [](unsigned char c) { return tolower(c); });\n                    std::cout << \"table name:\" << tableName << std::endl;\n                    createModelClassFromPG(path,\n                                           client,\n                                           tableName,\n                                           schema,\n                                           restfulApiConfig,\n                                           relationships[tableName],\n                                           convertMethods[tableName]);\n                }\n            }\n        }\n        else\n        {\n            createModelClassFromPG(path,\n                                   client,\n                                   singleModelName,\n                                   schema,\n                                   restfulApiConfig,\n                                   relationships[singleModelName],\n                                   convertMethods[singleModelName]);\n        }\n#else\n        std::cerr\n            << \"Drogon does not support PostgreSQL, please install PostgreSQL \"\n               \"development environment before installing drogon\"\n            << std::endl;\n#endif\n    }\n    else if (dbType == \"mysql\")\n    {\n#if USE_MYSQL\n        std::cout << \"mysql\" << std::endl;\n        auto host = config.get(\"host\", \"127.0.0.1\").asString();\n        auto port = config.get(\"port\", 5432).asUInt();\n        auto dbname = config.get(\"dbname\", \"\").asString();\n        if (dbname == \"\")\n        {\n            std::cerr << \"Please configure dbname in \" << path << \"/model.json \"\n                      << std::endl;\n            exit(1);\n        }\n        dbname_ = dbname;\n        auto user = config.get(\"user\", \"\").asString();\n        if (user == \"\")\n        {\n            std::cerr << \"Please configure user in \" << path << \"/model.json \"\n                      << std::endl;\n            exit(1);\n        }\n        auto password = config.get(\"passwd\", \"\").asString();\n        if (password.empty())\n        {\n            password = config.get(\"password\", \"\").asString();\n        }\n\n        auto connStr =\n            utils::formattedString(\"host=%s port=%u dbname=%s user=%s\",\n                                   escapeConnString(host).c_str(),\n                                   port,\n                                   escapeConnString(dbname).c_str(),\n                                   escapeConnString(user).c_str());\n        if (!password.empty())\n        {\n            connStr += \" password=\";\n            connStr += escapeConnString(password);\n        }\n        auto characterSet = config.get(\"client_encoding\", \"\").asString();\n        if (!characterSet.empty())\n        {\n            connStr += \" client_encoding=\";\n            connStr += escapeConnString(characterSet);\n        }\n        DbClientPtr client = drogon::orm::DbClient::newMysqlClient(connStr, 1);\n        std::cout << \"Connect to server...\" << std::endl;\n        if (forceOverwrite_)\n        {\n            std::this_thread::sleep_for(2s);\n        }\n        else\n        {\n            std::cout << \"Source files in the \" << path\n                      << \" folder will be overwritten, continue(y/n)?\\n\";\n            auto in = getchar();\n            (void)getchar();  // get the return key\n            if (in != 'Y' && in != 'y')\n            {\n                std::cout << \"Abort!\" << std::endl;\n                exit(0);\n            }\n        }\n\n        if (singleModelName.empty())\n        {\n            auto tables = config[\"tables\"];\n            if (!tables || tables.size() == 0)\n                createModelFromMysql(path,\n                                     client,\n                                     restfulApiConfig,\n                                     relationships,\n                                     convertMethods);\n            else\n            {\n                for (int i = 0; i < (int)tables.size(); ++i)\n                {\n                    auto tableName = tables[i].asString();\n                    std::transform(tableName.begin(),\n                                   tableName.end(),\n                                   tableName.begin(),\n                                   [](unsigned char c) { return tolower(c); });\n                    std::cout << \"table name:\" << tableName << std::endl;\n                    createModelClassFromMysql(path,\n                                              client,\n                                              tableName,\n                                              restfulApiConfig,\n                                              relationships[tableName],\n                                              convertMethods[tableName]);\n                }\n            }\n        }\n        else\n        {\n            createModelClassFromMysql(path,\n                                      client,\n                                      singleModelName,\n                                      restfulApiConfig,\n                                      relationships[singleModelName],\n                                      convertMethods[singleModelName]);\n        }\n\n#else\n        std::cerr << \"Drogon does not support Mysql, please install MariaDB \"\n                     \"development environment before installing drogon\"\n                  << std::endl;\n#endif\n    }\n    else if (dbType == \"sqlite3\")\n    {\n#if USE_SQLITE3\n        auto filename = config.get(\"filename\", \"\").asString();\n        if (filename == \"\")\n        {\n            std::cerr << \"Please configure filename in \" << path\n                      << \"/model.json \" << std::endl;\n            exit(1);\n        }\n        std::string connStr = \"filename=\" + escapeConnString(filename);\n        DbClientPtr client =\n            drogon::orm::DbClient::newSqlite3Client(connStr, 1);\n        std::cout << \"Connect...\" << std::endl;\n        if (forceOverwrite_)\n        {\n            std::this_thread::sleep_for(1s);\n        }\n        else\n        {\n            std::cout << \"Source files in the \" << path\n                      << \" folder will be overwritten, continue(y/n)?\\n\";\n            auto in = getchar();\n            (void)getchar();  // get the return key\n            if (in != 'Y' && in != 'y')\n            {\n                std::cout << \"Abort!\" << std::endl;\n                exit(0);\n            }\n        }\n\n        if (singleModelName.empty())\n        {\n            auto tables = config[\"tables\"];\n            if (!tables || tables.size() == 0)\n                createModelFromSqlite3(path,\n                                       client,\n                                       restfulApiConfig,\n                                       relationships,\n                                       convertMethods);\n            else\n            {\n                for (int i = 0; i < (int)tables.size(); ++i)\n                {\n                    auto tableName = tables[i].asString();\n                    std::transform(tableName.begin(),\n                                   tableName.end(),\n                                   tableName.begin(),\n                                   [](unsigned char c) { return tolower(c); });\n                    std::cout << \"table name:\" << tableName << std::endl;\n                    createModelClassFromSqlite3(path,\n                                                client,\n                                                tableName,\n                                                restfulApiConfig,\n                                                relationships[tableName],\n                                                convertMethods[tableName]);\n                }\n            }\n        }\n        else\n        {\n            createModelClassFromSqlite3(path,\n                                        client,\n                                        singleModelName,\n                                        restfulApiConfig,\n                                        relationships[singleModelName],\n                                        convertMethods[singleModelName]);\n        }\n\n#else\n        std::cerr << \"Drogon does not support Sqlite3, please install Sqlite3 \"\n                     \"development environment before installing drogon\"\n                  << std::endl;\n#endif\n    }\n    else if (dbType == \"no dbms\")\n    {\n        std::cerr << \"Please configure Model in \" << path << \"/model.json \"\n                  << std::endl;\n        exit(1);\n    }\n    else\n    {\n        std::cerr << \"Does not support \" << dbType << std::endl;\n        exit(1);\n    }\n}\n\nvoid create_model::createModel(const std::string &path,\n                               const std::string &singleModelName)\n{\n#ifndef _WIN32\n    DIR *dp;\n    if ((dp = opendir(path.c_str())) == NULL)\n    {\n        std::cerr << \"No such file or directory : \" << path << std::endl;\n        return;\n    }\n    closedir(dp);\n#endif\n    auto configFile = path + \"/model.json\";\n#ifdef _WIN32\n    if (_access(configFile.c_str(), 0) != 0)\n#else\n    if (access(configFile.c_str(), 0) != 0)\n#endif\n    {\n        std::cerr << \"Config file \" << configFile << \" not found!\" << std::endl;\n        exit(1);\n    }\n#ifdef _WIN32\n    if (_access(configFile.c_str(), 04) != 0)\n#else\n    if (access(configFile.c_str(), R_OK) != 0)\n#endif\n    {\n        std::cerr << \"No permission to read config file \" << configFile\n                  << std::endl;\n        exit(1);\n    }\n\n    std::ifstream infile(configFile.c_str(), std::ifstream::in);\n    if (infile)\n    {\n        Json::Value configJsonRoot;\n        try\n        {\n            infile >> configJsonRoot;\n            createModel(outputPath_.empty() ? path : outputPath_,\n                        configJsonRoot,\n                        singleModelName);\n        }\n        catch (const std::exception &exception)\n        {\n            std::cerr << \"Configuration file format error! in \" << configFile\n                      << \":\" << std::endl;\n            std::cerr << exception.what() << std::endl;\n            exit(1);\n        }\n    }\n}\n\nvoid create_model::handleCommand(std::vector<std::string> &parameters)\n{\n    std::cout << \"Create model\" << std::endl;\n    if (parameters.size() == 0)\n    {\n        std::cerr << \"Missing Model path name!\" << std::endl;\n    }\n    std::string singleModelName;\n    for (auto iter = parameters.begin(); iter != parameters.end(); ++iter)\n    {\n        if ((*iter).find(\"--table=\") == 0)\n        {\n            singleModelName = (*iter).substr(8);\n            parameters.erase(iter);\n            break;\n        }\n    }\n    for (auto iter = parameters.begin(); iter != parameters.end(); ++iter)\n    {\n        if ((*iter) == \"-f\")\n        {\n            forceOverwrite_ = true;\n            parameters.erase(iter);\n            break;\n        }\n    }\n    for (auto iter = parameters.begin(); iter != parameters.end();)\n    {\n        auto &file = *iter;\n        if (file == \"-o\" || file == \"--output\")\n        {\n            iter = parameters.erase(iter);\n            if (iter != parameters.end())\n            {\n                outputPath_ = *iter;\n                iter = parameters.erase(iter);\n            }\n            continue;\n        }\n        ++iter;\n    }\n\n    for (auto iter = parameters.begin(); iter != parameters.end(); ++iter)\n    {\n        if ((*iter) == \"--clear-output\")\n        {\n            cleanupDirectory_ = true;\n            forceOverwrite_ = true;\n            parameters.erase(iter);\n            break;\n        }\n    }\n\n    for (auto const &path : parameters)\n    {\n        createModel(path, singleModelName);\n    }\n}\n\nvoid create_model::createRestfulAPIController(\n    const DrTemplateData &tableInfo,\n    const Json::Value &restfulApiConfig)\n{\n    if (restfulApiConfig.isNull())\n        return;\n    if (!restfulApiConfig.get(\"enabled\", false).asBool())\n    {\n        return;\n    }\n    auto genBaseOnly =\n        restfulApiConfig.get(\"generate_base_only\", false).asBool();\n    auto modelClassName = tableInfo.get<std::string>(\"className\");\n    std::regex regex(\"\\\\*\");\n    auto resource = std::regex_replace(\n        restfulApiConfig.get(\"resource_uri\", \"/*\").asString(),\n        regex,\n        modelClassName);\n    std::transform(resource.begin(),\n                   resource.end(),\n                   resource.begin(),\n                   [](unsigned char c) { return tolower(c); });\n    auto ctrlClassName =\n        std::regex_replace(restfulApiConfig.get(\"class_name\", \"/*\").asString(),\n                           regex,\n                           modelClassName);\n    std::regex regex1(\"::\");\n    std::string ctlName =\n        std::regex_replace(ctrlClassName, regex1, std::string(\"_\"));\n    auto v = utils::splitString(ctrlClassName, \"::\");\n\n    drogon::DrTemplateData data;\n    data.insert(\"className\", v[v.size() - 1]);\n    v.pop_back();\n    data.insert(\"namespaceVector\", v);\n    data.insert(\"resource\", resource);\n    data.insert(\"fileName\", ctlName);\n    data.insert(\"tableName\", tableInfo.get<std::string>(\"tableName\"));\n    data.insert(\"tableInfo\", tableInfo);\n    auto filters = restfulApiConfig[\"filters\"];\n    if (filters.isNull() || filters.empty() || !filters.isArray())\n    {\n        data.insert(\"filters\", \"\");\n    }\n    else\n    {\n        std::string filtersStr;\n        for (auto &filterName : filters)\n        {\n            filtersStr += \",\\\"\";\n            filtersStr.append(filterName.asString());\n            filtersStr += '\"';\n        }\n        data.insert(\"filters\", filtersStr);\n    }\n    auto dbClientConfig = restfulApiConfig[\"db_client\"];\n    if (dbClientConfig.isNull() || dbClientConfig.empty())\n    {\n        data.insertAsString(\"dbClientName\", \"default\");\n        data.insert(\"isFastDbClient\", false);\n    }\n    else\n    {\n        auto clientName = dbClientConfig.get(\"name\", \"default\").asString();\n        auto isFast = dbClientConfig.get(\"is_fast\", false).asBool();\n        data.insertAsString(\"dbClientName\", clientName);\n        data.insert(\"isFastDbClient\", isFast);\n    }\n    auto dir = restfulApiConfig.get(\"directory\", \"controllers\").asString();\n    if (dir[dir.length() - 1] != '/')\n    {\n        dir += '/';\n    }\n    {\n        std::string headFileName = dir + ctlName + \"Base.h\";\n        std::string sourceFilename = dir + ctlName + \"Base.cc\";\n        // {\n        //     std::ifstream iHeadFile(headFileName.c_str(), std::ifstream::in);\n        //     std::ifstream iSourceFile(sourceFilename.c_str(),\n        //                               std::ifstream::in);\n\n        //     if (iHeadFile || iSourceFile)\n        //     {\n        //         std::cout << \"The \" << headFileName << \" and \" <<\n        //         sourceFilename\n        //                   << \" you want to create already exist, \"\n        //                      \"overwrite it(y/n)?\"\n        //                   << std::endl;\n        //         auto in = getchar();\n        //         (void)getchar();  // get the return key\n        //         if (in != 'Y' && in != 'y')\n        //         {\n        //             std::cout << \"Abort!\" << std::endl;\n        //             exit(0);\n        //         }\n        //     }\n        // }\n        std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);\n        std::ofstream oSourceFile(sourceFilename.c_str(), std::ofstream::out);\n        if (!oHeadFile || !oSourceFile)\n        {\n            perror(\"\");\n            exit(1);\n        }\n        try\n        {\n            auto templ =\n                DrTemplateBase::newTemplate(\"restful_controller_base_h.csp\");\n            oHeadFile << templ->genText(data);\n            templ =\n                DrTemplateBase::newTemplate(\"restful_controller_base_cc.csp\");\n            oSourceFile << templ->genText(data);\n        }\n        catch (const std::exception &err)\n        {\n            std::cerr << err.what() << std::endl;\n            exit(1);\n        }\n        std::cout << \"create a http restful API controller base class:\"\n                  << ctrlClassName << \"Base\" << std::endl;\n        std::cout << \"file name: \" << headFileName << \", \" << sourceFilename\n                  << std::endl\n                  << std::endl;\n    }\n    if (!genBaseOnly)\n    {\n        std::string headFileName = dir + ctlName + \".h\";\n        std::string sourceFilename = dir + ctlName + \".cc\";\n        if (!forceOverwrite_)\n        {\n            std::ifstream iHeadFile(headFileName.c_str(), std::ifstream::in);\n            std::ifstream iSourceFile(sourceFilename.c_str(),\n                                      std::ifstream::in);\n\n            if (iHeadFile || iSourceFile)\n            {\n                std::cout << \"The \" << headFileName << \" and \" << sourceFilename\n                          << \" you want to create already exist, \"\n                             \"overwrite them(y/n)?\"\n                          << std::endl;\n                auto in = getchar();\n                (void)getchar();  // get the return key\n                if (in != 'Y' && in != 'y')\n                {\n                    std::cout << \"Abort!\" << std::endl;\n                    exit(0);\n                }\n            }\n        }\n        std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);\n        std::ofstream oSourceFile(sourceFilename.c_str(), std::ofstream::out);\n        if (!oHeadFile || !oSourceFile)\n        {\n            perror(\"\");\n            exit(1);\n        }\n        try\n        {\n            auto templ =\n                DrTemplateBase::newTemplate(\"restful_controller_custom_h.csp\");\n            oHeadFile << templ->genText(data);\n            templ =\n                DrTemplateBase::newTemplate(\"restful_controller_custom_cc.csp\");\n            oSourceFile << templ->genText(data);\n        }\n        catch (const std::exception &err)\n        {\n            std::cerr << err.what() << std::endl;\n            exit(1);\n        }\n\n        std::cout << \"create a http restful API controller class: \"\n                  << ctrlClassName << std::endl;\n        std::cout << \"file name: \" << headFileName << \", \" << sourceFilename\n                  << std::endl\n                  << std::endl;\n    }\n}\n"
  },
  {
    "path": "drogon_ctl/create_model.h",
    "content": "/**\n *\n *  create_model.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/config.h>\n#include <drogon/DrTemplateBase.h>\n#include <json/json.h>\n#include <drogon/orm/DbClient.h>\nusing namespace drogon::orm;\n#include <drogon/DrObject.h>\n#include \"CommandHandler.h\"\n#include <string>\n#include <algorithm>\n\nusing namespace drogon;\n\nnamespace drogon_ctl\n{\nstruct ColumnInfo\n{\n    std::string colName_;\n    std::string colValName_;\n    std::string colTypeName_;\n    std::string colType_;\n    std::string colDatabaseType_;\n    std::string dbType_;\n    ssize_t colLength_{0};\n    size_t index_{0};\n    bool isAutoVal_{false};\n    bool isPrimaryKey_{false};\n    bool notNull_{false};\n    bool hasDefaultVal_{false};\n};\n\ninline std::string nameTransform(const std::string &origName, bool isType)\n{\n    auto str = origName;\n    std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) {\n        return tolower(c);\n    });\n    std::string::size_type startPos = 0;\n    std::string::size_type pos;\n    std::string ret;\n    do\n    {\n        pos = str.find(\"_\", startPos);\n        if (pos == std::string::npos)\n        {\n            pos = str.find(\".\", startPos);\n        }\n        if (pos != std::string::npos)\n            ret += str.substr(startPos, pos - startPos);\n        else\n        {\n            ret += str.substr(startPos);\n            break;\n        }\n        while (str[pos] == '_' || str[pos] == '.')\n            ++pos;\n        if (str[pos] >= 'a' && str[pos] <= 'z')\n            str[pos] += ('A' - 'a');\n        startPos = pos;\n    } while (1);\n    if (isType && ret[0] >= 'a' && ret[0] <= 'z')\n        ret[0] += ('A' - 'a');\n    return ret;\n}\n\nstd::string escapeIdentifier(const std::string &identifier,\n                             const std::string &rdbms);\n\nclass PivotTable\n{\n  public:\n    PivotTable() = default;\n\n    PivotTable(const Json::Value &json)\n        : tableName_(json.get(\"table_name\", \"\").asString())\n    {\n        if (tableName_.empty())\n        {\n            throw std::runtime_error(\"table_name can't be empty\");\n        }\n        originalKey_ = json.get(\"original_key\", \"\").asString();\n        if (originalKey_.empty())\n        {\n            throw std::runtime_error(\"original_key can't be empty\");\n        }\n        targetKey_ = json.get(\"target_key\", \"\").asString();\n        if (targetKey_.empty())\n        {\n            throw std::runtime_error(\"target_key can't be empty\");\n        }\n    }\n\n    PivotTable reverse() const\n    {\n        PivotTable pivot;\n        pivot.tableName_ = tableName_;\n        pivot.originalKey_ = targetKey_;\n        pivot.targetKey_ = originalKey_;\n        return pivot;\n    }\n\n    const std::string &tableName() const\n    {\n        return tableName_;\n    }\n\n    const std::string &originalKey() const\n    {\n        return originalKey_;\n    }\n\n    const std::string &targetKey() const\n    {\n        return targetKey_;\n    }\n\n  private:\n    std::string tableName_;\n    std::string originalKey_;\n    std::string targetKey_;\n};\n\nclass ConvertMethod\n{\n  public:\n    ConvertMethod(const Json::Value &convert)\n    {\n        tableName_ = convert.get(\"table\", \"*\").asString();\n        colName_ = convert.get(\"column\", \"*\").asString();\n\n        auto method = convert[\"method\"];\n        if (method.isNull())\n        {\n            throw std::runtime_error(\"method - object is missing.\");\n        }  // endif\n        if (!method.isObject())\n        {\n            throw std::runtime_error(\"method is not an object.\");\n        }  // endif\n        methodBeforeDbWrite_ = method.get(\"before_db_write\", \"\").asString();\n        methodAfterDbRead_ = method.get(\"after_db_read\", \"\").asString();\n\n        auto includeFiles = convert[\"includes\"];\n        if (includeFiles.isNull())\n        {\n            return;\n        }  // endif\n        if (!includeFiles.isArray())\n        {\n            throw std::runtime_error(\"includes must be an array\");\n        }  // endif\n        for (auto &i : includeFiles)\n        {\n            includeFiles_.push_back(i.asString());\n        }  // for\n    }\n\n    ConvertMethod() = default;\n\n    bool shouldConvert(const std::string &tableName,\n                       const std::string &colName) const;\n\n    const std::string &tableName() const\n    {\n        return tableName_;\n    }\n\n    const std::string &colName() const\n    {\n        return colName_;\n    }\n\n    const std::string &methodBeforeDbWrite() const\n    {\n        return methodBeforeDbWrite_;\n    }\n\n    const std::string &methodAfterDbRead() const\n    {\n        return methodAfterDbRead_;\n    }\n\n    const std::vector<std::string> &includeFiles() const\n    {\n        return includeFiles_;\n    }\n\n  private:\n    std::string tableName_{\"*\"};\n    std::string colName_{\"*\"};\n    std::string methodBeforeDbWrite_;\n    std::string methodAfterDbRead_;\n    std::vector<std::string> includeFiles_;\n};\n\nclass Relationship\n{\n  public:\n    enum class Type\n    {\n        HasOne,\n        HasMany,\n        ManyToMany\n    };\n\n    Relationship(const Json::Value &relationship)\n    {\n        auto type = relationship.get(\"type\", \"has one\").asString();\n        if (type == \"has one\")\n        {\n            type_ = Relationship::Type::HasOne;\n        }\n        else if (type == \"has many\")\n        {\n            type_ = Relationship::Type::HasMany;\n        }\n        else if (type == \"many to many\")\n        {\n            type_ = Relationship::Type::ManyToMany;\n        }\n        else\n        {\n            char message[128];\n            snprintf(message,\n                     sizeof(message),\n                     \"Invalid relationship type: %s\",\n                     type.data());\n            throw std::runtime_error(message);\n        }\n        originalTableName_ =\n            relationship.get(\"original_table_name\", \"\").asString();\n        if (originalTableName_.empty())\n        {\n            throw std::runtime_error(\"original_table_name can't be empty\");\n        }\n        originalKey_ = relationship.get(\"original_key\", \"\").asString();\n        if (originalKey_.empty())\n        {\n            throw std::runtime_error(\"original_key can't be empty\");\n        }\n        originalTableAlias_ =\n            relationship.get(\"original_table_alias\", \"\").asString();\n        targetTableName_ = relationship.get(\"target_table_name\", \"\").asString();\n        if (targetTableName_.empty())\n        {\n            throw std::runtime_error(\"target_table_name can't be empty\");\n        }\n        targetKey_ = relationship.get(\"target_key\", \"\").asString();\n        if (targetKey_.empty())\n        {\n            throw std::runtime_error(\"target_key can't be empty\");\n        }\n        targetTableAlias_ =\n            relationship.get(\"target_table_alias\", \"\").asString();\n        enableReverse_ = relationship.get(\"enable_reverse\", false).asBool();\n        if (type_ == Type::ManyToMany)\n        {\n            auto &pivot = relationship[\"pivot_table\"];\n            if (pivot.isNull())\n            {\n                throw std::runtime_error(\n                    \"ManyToMany relationship needs a pivot table\");\n            }\n            pivotTable_ = PivotTable(pivot);\n        }\n    }\n\n    Relationship() = default;\n\n    Relationship reverse() const\n    {\n        Relationship r;\n        if (type_ == Type::HasMany)\n        {\n            r.type_ = Type::HasOne;\n        }\n        else\n        {\n            r.type_ = type_;\n        }\n        r.originalTableName_ = targetTableName_;\n        r.originalTableAlias_ = targetTableAlias_;\n        r.originalKey_ = targetKey_;\n        r.targetTableName_ = originalTableName_;\n        r.targetTableAlias_ = originalTableAlias_;\n        r.targetKey_ = originalKey_;\n        r.enableReverse_ = enableReverse_;\n        r.pivotTable_ = pivotTable_.reverse();\n        return r;\n    }\n\n    Type type() const\n    {\n        return type_;\n    }\n\n    bool enableReverse() const\n    {\n        return enableReverse_;\n    }\n\n    const std::string &originalTableName() const\n    {\n        return originalTableName_;\n    }\n\n    const std::string &originalTableAlias() const\n    {\n        return originalTableAlias_;\n    }\n\n    const std::string &originalKey() const\n    {\n        return originalKey_;\n    }\n\n    const std::string &targetTableName() const\n    {\n        return targetTableName_;\n    }\n\n    const std::string &targetTableAlias() const\n    {\n        return targetTableAlias_;\n    }\n\n    const std::string &targetKey() const\n    {\n        return targetKey_;\n    }\n\n    const PivotTable &pivotTable() const\n    {\n        return pivotTable_;\n    }\n\n  private:\n    Type type_{Type::HasOne};\n    std::string originalTableName_;\n    std::string originalTableAlias_;\n    std::string targetTableName_;\n    std::string targetTableAlias_;\n    std::string originalKey_;\n    std::string targetKey_;\n    bool enableReverse_{false};\n    PivotTable pivotTable_;\n};\n\nclass create_model : public DrObject<create_model>, public CommandHandler\n{\n  public:\n    void handleCommand(std::vector<std::string> &parameters) override;\n\n    std::string script() override\n    {\n        return \"create Model classes files\";\n    }\n\n  protected:\n    void createModel(const std::string &path,\n                     const std::string &singleModelName);\n    void createModel(const std::string &path,\n                     const Json::Value &config,\n                     const std::string &singleModelName);\n#if USE_POSTGRESQL\n    void createModelClassFromPG(\n        const std::string &path,\n        const DbClientPtr &client,\n        const std::string &tableName,\n        const std::string &schema,\n        const Json::Value &restfulApiConfig,\n        const std::vector<Relationship> &relationships,\n        const std::vector<ConvertMethod> &convertMethods);\n\n    void createModelFromPG(\n        const std::string &path,\n        const DbClientPtr &client,\n        const std::string &schema,\n        const Json::Value &restfulApiConfig,\n        std::map<std::string, std::vector<Relationship>> &relationships,\n        std::map<std::string, std::vector<ConvertMethod>> &convertMethods);\n#endif\n#if USE_MYSQL\n    void createModelClassFromMysql(\n        const std::string &path,\n        const DbClientPtr &client,\n        const std::string &tableName,\n        const Json::Value &restfulApiConfig,\n        const std::vector<Relationship> &relationships,\n        const std::vector<ConvertMethod> &convertMethods);\n    void createModelFromMysql(\n        const std::string &path,\n        const DbClientPtr &client,\n        const Json::Value &restfulApiConfig,\n        std::map<std::string, std::vector<Relationship>> &relationships,\n        std::map<std::string, std::vector<ConvertMethod>> &convertMethods);\n#endif\n#if USE_SQLITE3\n    void createModelClassFromSqlite3(\n        const std::string &path,\n        const DbClientPtr &client,\n        const std::string &tableName,\n        const Json::Value &restfulApiConfig,\n        const std::vector<Relationship> &relationships,\n        const std::vector<ConvertMethod> &convertMethod);\n    void createModelFromSqlite3(\n        const std::string &path,\n        const DbClientPtr &client,\n        const Json::Value &restfulApiConfig,\n        std::map<std::string, std::vector<Relationship>> &relationships,\n        std::map<std::string, std::vector<ConvertMethod>> &convertMethod);\n#endif\n    void createRestfulAPIController(const DrTemplateData &tableInfo,\n                                    const Json::Value &restfulApiConfig);\n    std::string dbname_;\n    bool forceOverwrite_{false};\n    std::string outputPath_;\n    bool cleanupDirectory_{false};\n};\n}  // namespace drogon_ctl\n"
  },
  {
    "path": "drogon_ctl/create_plugin.cc",
    "content": "/**\n *\n *  create_plugin.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"create_plugin.h\"\n#include <drogon/DrTemplateBase.h>\n#include <drogon/utils/Utilities.h>\n\n#include <string>\n#include <iostream>\n#ifndef _WIN32\n#include <unistd.h>\n#endif\n#include <fstream>\n#include <regex>\n\n#include <sys/stat.h>\n#include <sys/types.h>\n\nusing namespace drogon_ctl;\n\nstatic void createPluginHeaderFile(std::ofstream &file,\n                                   const std::string &className,\n                                   const std::string &fileName)\n{\n    auto templ = drogon::DrTemplateBase::newTemplate(\"plugin_h\");\n    HttpViewData data;\n    if (className.find(\"::\") != std::string::npos)\n    {\n        auto namespaceVector = utils::splitString(className, \"::\");\n        data.insert(\"className\", namespaceVector.back());\n        namespaceVector.pop_back();\n        data.insert(\"namespaceVector\", namespaceVector);\n    }\n    else\n    {\n        data.insert(\"className\", className);\n    }\n    data.insert(\"filename\", fileName);\n    file << templ->genText(data);\n}\n\nstatic void createPluginSourceFile(std::ofstream &file,\n                                   const std::string &className,\n                                   const std::string &fileName)\n{\n    auto templ = drogon::DrTemplateBase::newTemplate(\"plugin_cc\");\n    HttpViewData data;\n    if (className.find(\"::\") != std::string::npos)\n    {\n        auto pos = className.rfind(\"::\");\n        data.insert(\"namespaceString\", className.substr(0, pos));\n        data.insert(\"className\", className.substr(pos + 2));\n    }\n    else\n    {\n        data.insert(\"className\", className);\n    }\n    data.insert(\"filename\", fileName);\n    file << templ->genText(data);\n}\n\nvoid create_plugin::handleCommand(std::vector<std::string> &parameters)\n{\n    if (parameters.size() < 1)\n    {\n        std::cout << \"Invalid parameters!\" << std::endl;\n    }\n    for (auto className : parameters)\n    {\n        std::regex regex(\"::\");\n        std::string fileName =\n            std::regex_replace(className, regex, std::string(\"_\"));\n\n        std::string headFileName = fileName + \".h\";\n        std::string sourceFilename = fileName + \".cc\";\n        {\n            std::ifstream iHeadFile(headFileName.c_str(), std::ifstream::in);\n            std::ifstream iSourceFile(sourceFilename.c_str(),\n                                      std::ifstream::in);\n\n            if (iHeadFile || iSourceFile)\n            {\n                std::cout << \"The file you want to create already exists, \"\n                             \"overwrite it(y/n)?\"\n                          << std::endl;\n                auto in = getchar();\n                (void)getchar();  // get the return key\n                if (in != 'Y' && in != 'y')\n                {\n                    std::cout << \"Abort!\" << std::endl;\n                    exit(0);\n                }\n            }\n        }\n        std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);\n        std::ofstream oSourceFile(sourceFilename.c_str(), std::ofstream::out);\n        if (!oHeadFile || !oSourceFile)\n        {\n            perror(\"\");\n            exit(1);\n        }\n\n        std::cout << \"create a plugin:\" << className << std::endl;\n        createPluginHeaderFile(oHeadFile, className, fileName);\n        createPluginSourceFile(oSourceFile, className, fileName);\n    }\n}\n"
  },
  {
    "path": "drogon_ctl/create_plugin.h",
    "content": "/**\n *\n *  create_plugin.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include \"CommandHandler.h\"\nusing namespace drogon;\n\nnamespace drogon_ctl\n{\nclass create_plugin : public DrObject<create_plugin>, public CommandHandler\n{\n  public:\n    void handleCommand(std::vector<std::string> &parameters) override;\n\n    std::string script() override\n    {\n        return \"create plugin class files\";\n    }\n\n  protected:\n    std::string outputPath_{\".\"};\n};\n}  // namespace drogon_ctl\n"
  },
  {
    "path": "drogon_ctl/create_project.cc",
    "content": "/**\n *\n *  create_project.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"create_project.h\"\n#include <drogon/DrTemplateBase.h>\n#include <drogon/utils/Utilities.h>\n#include <iostream>\n#include <sys/stat.h>\n#include <sys/types.h>\n#ifndef _WIN32\n#include <unistd.h>\n#else\n#include <io.h>\n#include <direct.h>\n#endif\n#include <fstream>\n\nusing namespace drogon_ctl;\n\nvoid create_project::handleCommand(std::vector<std::string> &parameters)\n{\n    if (parameters.size() < 1)\n    {\n        std::cout << \"please input project name\" << std::endl;\n        exit(1);\n    }\n    auto pName = parameters[0];\n    createProject(pName);\n}\n\nstatic void newCmakeFile(std::ofstream &cmakeFile,\n                         const std::string &projectName)\n{\n    HttpViewData data;\n    data.insert(\"ProjectName\", projectName);\n    auto templ = DrTemplateBase::newTemplate(\"cmake.csp\");\n    cmakeFile << templ->genText(data);\n}\n\nstatic void newMainFile(std::ofstream &mainFile)\n{\n    auto templ = DrTemplateBase::newTemplate(\"demoMain\");\n    mainFile << templ->genText();\n}\n\nstatic void newGitIgFile(std::ofstream &gitFile)\n{\n    auto templ = DrTemplateBase::newTemplate(\"gitignore.csp\");\n    gitFile << templ->genText();\n}\n\nstatic void newConfigJsonFile(std::ofstream &configJsonFile)\n{\n    auto templ = DrTemplateBase::newTemplate(\"config_json\");\n    configJsonFile << templ->genText();\n}\n\nstatic void newConfigYamlFile(std::ofstream &configYamlFile)\n{\n    auto templ = DrTemplateBase::newTemplate(\"config_yaml\");\n    configYamlFile << templ->genText();\n}\n\nstatic void newModelConfigFile(std::ofstream &configFile)\n{\n    auto templ = DrTemplateBase::newTemplate(\"model_json\");\n    configFile << templ->genText();\n}\n\nstatic void newTestMainFile(std::ofstream &mainFile)\n{\n    auto templ = DrTemplateBase::newTemplate(\"test_main\");\n    mainFile << templ->genText();\n}\n\nstatic void newTestCmakeFile(std::ofstream &testCmakeFile,\n                             const std::string &projectName)\n{\n    HttpViewData data;\n    data.insert(\"ProjectName\", projectName);\n    auto templ = DrTemplateBase::newTemplate(\"test_cmake\");\n    testCmakeFile << templ->genText(data);\n}\n\nvoid create_project::createProject(const std::string &projectName)\n{\n#ifdef _WIN32\n    if (_access(projectName.data(), 0) == 0)\n#else\n    if (access(projectName.data(), 0) == 0)\n#endif\n    {\n        std::cerr\n            << \"The directory already exists, please use another project name!\"\n            << std::endl;\n        exit(1);\n    }\n    std::cout << \"create a project named \" << projectName << std::endl;\n\n    drogon::utils::createPath(projectName);\n// 1.create CMakeLists.txt\n#ifdef _WIN32\n    auto r = _chdir(projectName.data());\n#else\n    auto r = chdir(projectName.data());\n#endif\n    (void)(r);\n    std::ofstream cmakeFile(\"CMakeLists.txt\", std::ofstream::out);\n    newCmakeFile(cmakeFile, projectName);\n    std::ofstream mainFile(\"main.cc\", std::ofstream::out);\n    newMainFile(mainFile);\n    drogon::utils::createPath(\"views\");\n    drogon::utils::createPath(\"controllers\");\n    drogon::utils::createPath(\"filters\");\n    drogon::utils::createPath(\"plugins\");\n    drogon::utils::createPath(\"build\");\n    drogon::utils::createPath(\"models\");\n    drogon::utils::createPath(\"test\");\n\n    std::ofstream gitFile(\".gitignore\", std::ofstream::out);\n    newGitIgFile(gitFile);\n    std::ofstream configJsonFile(\"config.json\", std::ofstream::out);\n    newConfigJsonFile(configJsonFile);\n    std::ofstream configYamlFile(\"config.yaml\", std::ofstream::out);\n    newConfigYamlFile(configYamlFile);\n    std::ofstream modelConfigFile(\"models/model.json\", std::ofstream::out);\n    newModelConfigFile(modelConfigFile);\n    std::ofstream testMainFile(\"test/test_main.cc\", std::ofstream::out);\n    newTestMainFile(testMainFile);\n    std::ofstream testCmakeFile(\"test/CMakeLists.txt\", std::ofstream::out);\n    newTestCmakeFile(testCmakeFile, projectName);\n}\n"
  },
  {
    "path": "drogon_ctl/create_project.h",
    "content": "/**\n *\n *  create_project.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include <drogon/DrObject.h>\n#include \"CommandHandler.h\"\nusing namespace drogon;\n\nnamespace drogon_ctl\n{\nclass create_project : public DrObject<create_project>, public CommandHandler\n{\n  public:\n    void handleCommand(std::vector<std::string> &parameters) override;\n\n    std::string script() override\n    {\n        return \"create a project\";\n    }\n\n  protected:\n    std::string outputPath_{\".\"};\n    void createProject(const std::string &projectName);\n};\n}  // namespace drogon_ctl\n"
  },
  {
    "path": "drogon_ctl/create_view.cc",
    "content": "/**\n *\n *  @file create_view.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"create_view.h\"\n#include \"cmd.h\"\n#include <drogon/utils/Utilities.h>\n#include <iostream>\n#include <fstream>\n#include <string>\n#include <algorithm>\n#include <regex>\n\nstatic const std::string cxx_include = \"<%inc\";\nstatic const std::string cxx_end = \"%>\";\nstatic const std::string cxx_lang = \"<%c++\";\nstatic const std::string cxx_view_data = \"@@\";\nstatic const std::string cxx_output = \"$$\";\nstatic const std::string cxx_val_start = \"[[\";\nstatic const std::string cxx_val_end = \"]]\";\nstatic const std::string sub_view_start = \"<%view\";\nstatic const std::string sub_view_end = \"%>\";\n\nusing namespace drogon_ctl;\n\nstatic std::string &replace_all(std::string &str,\n                                const std::string &old_value,\n                                const std::string &new_value)\n{\n    std::string::size_type pos(0);\n    while (true)\n    {\n        // std::cout<<str<<endl;\n        // std::cout<<\"pos=\"<<pos<<endl;\n        if ((pos = str.find(old_value, pos)) != std::string::npos)\n        {\n            str = str.replace(pos, old_value.length(), new_value);\n            pos += new_value.length() - old_value.length();\n            ++pos;\n        }\n        else\n            break;\n    }\n    return str;\n}\n\nstatic void parseCxxLine(std::ofstream &oSrcFile,\n                         const std::string &line,\n                         const std::string &streamName,\n                         const std::string &viewDataName)\n{\n    if (line.length() > 0)\n    {\n        std::string tmp = line;\n        replace_all(tmp, cxx_output, streamName);\n        replace_all(tmp, cxx_view_data, viewDataName);\n        oSrcFile << tmp << \"\\n\";\n    }\n}\n\nstatic void outputVal(std::ofstream &oSrcFile,\n                      const std::string &streamName,\n                      const std::string &viewDataName,\n                      const std::string &keyName)\n{\n    oSrcFile << \"{\\n\";\n    oSrcFile << \"    auto & val=\" << viewDataName << \"[\\\"\" << keyName\n             << \"\\\"];\\n\";\n    oSrcFile << \"    if(val.type()==typeid(const char *)){\\n\";\n    oSrcFile << \"        \" << streamName\n             << \"<<*(std::any_cast<const char *>(&val));\\n\";\n    oSrcFile << \"    }else \"\n                \"if(val.type()==typeid(std::string)||val.type()==typeid(const \"\n                \"std::string)){\\n\";\n    oSrcFile << \"        \" << streamName\n             << \"<<*(std::any_cast<const std::string>(&val));\\n\";\n    oSrcFile << \"    }\\n\";\n    oSrcFile << \"}\\n\";\n}\n\nstatic void outputSubView(std::ofstream &oSrcFile,\n                          const std::string &streamName,\n                          const std::string &viewDataName,\n                          const std::string &keyName)\n{\n    oSrcFile << \"{\\n\";\n    oSrcFile << \"    auto templ=DrTemplateBase::newTemplate(\\\"\" << keyName\n             << \"\\\");\\n\";\n    oSrcFile << \"    if(templ){\\n\";\n    oSrcFile << \"      \" << streamName << \"<< templ->genText(\" << viewDataName\n             << \");\\n\";\n    oSrcFile << \"    }\\n\";\n    oSrcFile << \"}\\n\";\n}\n\nstatic void parseLine(std::ofstream &oSrcFile,\n                      std::string &line,\n                      const std::string &streamName,\n                      const std::string &viewDataName,\n                      int &cxx_flag,\n                      int returnFlag = 1)\n{\n    std::string::size_type pos(0);\n    // std::cout<<line<<\"(\"<<line.length()<<\")\\n\";\n    if (line.length() > 0 && line[line.length() - 1] == '\\r')\n    {\n        line.resize(line.length() - 1);\n    }\n    if (line.length() == 0)\n    {\n        // std::cout<<\"blank line!\"<<std::endl;\n        // std::cout<<streamName<<\"<<\\\"\\\\n\\\";\\n\";\n        if (returnFlag && !cxx_flag)\n            oSrcFile << streamName << \"<<\\\"\\\\n\\\";\\n\";\n        return;\n    }\n    if (cxx_flag == 0)\n    {\n        // find cxx lang begin\n        if ((pos = line.find(cxx_lang)) != std::string::npos)\n        {\n            std::string oldLine = line.substr(0, pos);\n            if (oldLine.length() > 0)\n                parseLine(\n                    oSrcFile, oldLine, streamName, viewDataName, cxx_flag, 0);\n            std::string newLine = line.substr(pos + cxx_lang.length());\n            cxx_flag = 1;\n            if (newLine.length() > 0)\n                parseLine(oSrcFile,\n                          newLine,\n                          streamName,\n                          viewDataName,\n                          cxx_flag,\n                          returnFlag);\n        }\n        else\n        {\n            if ((pos = line.find(cxx_val_start)) != std::string::npos)\n            {\n                std::string oldLine = line.substr(0, pos);\n                parseLine(\n                    oSrcFile, oldLine, streamName, viewDataName, cxx_flag, 0);\n                std::string newLine = line.substr(pos + cxx_val_start.length());\n                if ((pos = newLine.find(cxx_val_end)) != std::string::npos)\n                {\n                    std::string keyName = newLine.substr(0, pos);\n                    auto iter = keyName.begin();\n                    while (iter != keyName.end() && *iter == ' ')\n                        ++iter;\n                    auto iterEnd = iter;\n                    while (iterEnd != keyName.end() && *iterEnd != ' ')\n                        ++iterEnd;\n                    keyName = std::string(iter, iterEnd);\n                    outputVal(oSrcFile, streamName, viewDataName, keyName);\n                    std::string tailLine =\n                        newLine.substr(pos + cxx_val_end.length());\n                    parseLine(oSrcFile,\n                              tailLine,\n                              streamName,\n                              viewDataName,\n                              cxx_flag,\n                              returnFlag);\n                }\n                else\n                {\n                    std::cerr << \"format err!\" << std::endl;\n                    exit(1);\n                }\n            }\n            else if ((pos = line.find(sub_view_start)) != std::string::npos)\n            {\n                std::string oldLine = line.substr(0, pos);\n                parseLine(\n                    oSrcFile, oldLine, streamName, viewDataName, cxx_flag, 0);\n                std::string newLine =\n                    line.substr(pos + sub_view_start.length());\n                if ((pos = newLine.find(sub_view_end)) != std::string::npos)\n                {\n                    std::string keyName = newLine.substr(0, pos);\n                    auto iter = keyName.begin();\n                    while (iter != keyName.end() && *iter == ' ')\n                        ++iter;\n                    auto iterEnd = iter;\n                    while (iterEnd != keyName.end() && *iterEnd != ' ')\n                        ++iterEnd;\n                    keyName = std::string(iter, iterEnd);\n                    outputSubView(oSrcFile, streamName, viewDataName, keyName);\n                    std::string tailLine =\n                        newLine.substr(pos + sub_view_end.length());\n                    parseLine(oSrcFile,\n                              tailLine,\n                              streamName,\n                              viewDataName,\n                              cxx_flag,\n                              returnFlag);\n                }\n                else\n                {\n                    std::cerr << \"format err!\" << std::endl;\n                    exit(1);\n                }\n            }\n            else\n            {\n                if (line.length() > 0)\n                {\n                    replace_all(line, \"\\\\\", \"\\\\\\\\\");\n                    replace_all(line, \"\\\"\", \"\\\\\\\"\");\n                    oSrcFile << \"\\t\" << streamName << \" << \\\"\" << line;\n                }\n                if (returnFlag)\n                    oSrcFile << \"\\\\n\\\";\\n\";\n                else\n                    oSrcFile << \"\\\";\\n\";\n            }\n        }\n    }\n    else\n    {\n        if ((pos = line.find(cxx_end)) != std::string::npos)\n        {\n            std::string newLine = line.substr(0, pos);\n            parseCxxLine(oSrcFile, newLine, streamName, viewDataName);\n            std::string oldLine = line.substr(pos + cxx_end.length());\n            cxx_flag = 0;\n            if (oldLine.length() > 0)\n                parseLine(oSrcFile,\n                          oldLine,\n                          streamName,\n                          viewDataName,\n                          cxx_flag,\n                          returnFlag);\n        }\n        else\n        {\n            parseCxxLine(oSrcFile, line, streamName, viewDataName);\n        }\n    }\n}\n\nvoid create_view::handleCommand(std::vector<std::string> &parameters)\n{\n    for (auto iter = parameters.begin(); iter != parameters.end();)\n    {\n        auto &file = *iter;\n        if (file == \"-o\" || file == \"--output\")\n        {\n            iter = parameters.erase(iter);\n            if (iter != parameters.end())\n            {\n                outputPath_ = *iter;\n                iter = parameters.erase(iter);\n            }\n            continue;\n        }\n        else if (file == \"-n\" || file == \"--namespace\")\n        {\n            iter = parameters.erase(iter);\n            if (iter != parameters.end())\n            {\n                namespaces_ = utils::splitString(*iter, \"::\");\n                iter = parameters.erase(iter);\n            }\n            continue;\n        }\n        else if (file == \"--path-to-namespace\")\n        {\n            iter = parameters.erase(iter);\n            pathToNamespaceFlag_ = true;\n            continue;\n        }\n        else if (file[0] == '-')\n        {\n            std::cout << ARGS_ERROR_STR << std::endl;\n            return;\n        }\n        ++iter;\n    }\n    createViewFiles(parameters);\n}\n\nvoid create_view::createViewFiles(std::vector<std::string> &cspFileNames)\n{\n    for (auto const &file : cspFileNames)\n    {\n        std::cout << \"create view:\" << file << std::endl;\n        if (createViewFile(file) != 0)\n            exit(1);\n    }\n}\n\nint create_view::createViewFile(const std::string &script_filename)\n{\n    std::cout << \"create HttpView Class file by \" << script_filename\n              << std::endl;\n    if (pathToNamespaceFlag_)\n    {\n        std::string::size_type pos1 = 0, pos2 = 0;\n        if (script_filename.length() >= 2 && script_filename[0] == '.' &&\n            (script_filename[1] == '/' || script_filename[1] == '\\\\'))\n        {\n            pos1 = pos2 = 2;\n        }\n        else if (script_filename.length() >= 1 &&\n                 (script_filename[0] == '/' || script_filename[0] == '\\\\'))\n        {\n            pos1 = pos2 = 1;\n        }\n        while (pos2 < script_filename.length() - 1)\n        {\n            if (script_filename[pos2] == '/' || script_filename[pos2] == '\\\\')\n            {\n                if (pos2 > pos1)\n                {\n                    namespaces_.push_back(\n                        script_filename.substr(pos1, pos2 - pos1));\n                }\n                pos1 = ++pos2;\n            }\n            else\n            {\n                ++pos2;\n            }\n        }\n    }\n    std::string npPrefix;\n    for (auto &np : namespaces_)\n    {\n        npPrefix += np;\n        npPrefix += \"_\";\n    }\n    std::ifstream infile(script_filename.c_str(), std::ifstream::in);\n    if (infile)\n    {\n        std::string::size_type pos = script_filename.rfind('.');\n        if (pos != std::string::npos)\n        {\n            std::string className = script_filename.substr(0, pos);\n            if ((pos = className.rfind('/')) != std::string::npos)\n            {\n                className = className.substr(pos + 1);\n            }\n            std::cout << \"className=\" << className << std::endl;\n            std::string headFileName =\n                outputPath_ + \"/\" + npPrefix + className + \".h\";\n            std::string sourceFilename =\n                outputPath_ + \"/\" + npPrefix + className + \".cc\";\n            std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);\n            std::ofstream oSourceFile(sourceFilename.c_str(),\n                                      std::ofstream::out);\n            if (!oHeadFile || !oSourceFile)\n            {\n                std::cerr << \"Can't open \" << headFileName << \" or \"\n                          << sourceFilename << \"\\n\";\n                return -1;\n            }\n\n            newViewHeaderFile(oHeadFile, className);\n            newViewSourceFile(oSourceFile, className, npPrefix, infile);\n        }\n        else\n            return -1;\n    }\n    else\n    {\n        std::cerr << \"can't open file \" << script_filename << std::endl;\n        return -1;\n    }\n    return 0;\n}\n\nvoid create_view::newViewHeaderFile(std::ofstream &file,\n                                    const std::string &className)\n{\n    file << \"//this file is generated by program automatically,don't modify \"\n            \"it!\\n\";\n    file << \"#include <drogon/DrTemplate.h>\\n\";\n    for (auto &np : namespaces_)\n    {\n        file << \"namespace \" << np << \"\\n\";\n        file << \"{\\n\";\n    }\n    file << \"class \" << className << \":public drogon::DrTemplate<\" << className\n         << \">\\n\";\n    file << \"{\\npublic:\\n\\t\" << className << \"(){};\\n\\tvirtual ~\" << className\n         << \"(){};\\n\\t\"\n            \"virtual std::string genText(const drogon::DrTemplateData &) \"\n            \"override;\\n};\\n\";\n    for (std::size_t i = 0; i < namespaces_.size(); ++i)\n    {\n        file << \"}\\n\";\n    }\n}\n\nvoid create_view::newViewSourceFile(std::ofstream &file,\n                                    const std::string &className,\n                                    const std::string &namespacePrefix,\n                                    std::ifstream &infile)\n{\n    file << \"//this file is generated by program(drogon_ctl) \"\n            \"automatically,don't modify it!\\n\";\n    file << \"#include \\\"\" << namespacePrefix << className << \".h\\\"\\n\";\n    file << \"#include <drogon/utils/OStringStream.h>\\n\";\n    file << \"#include <drogon/utils/Utilities.h>\\n\";\n    file << \"#include <string>\\n\";\n    file << \"#include <map>\\n\";\n    file << \"#include <vector>\\n\";\n    file << \"#include <set>\\n\";\n    file << \"#include <iostream>\\n\";\n    file << \"#include <unordered_map>\\n\";\n    file << \"#include <unordered_set>\\n\";\n    file << \"#include <algorithm>\\n\";\n    file << \"#include <list>\\n\";\n    file << \"#include <deque>\\n\";\n    file << \"#include <queue>\\n\";\n\n    // Find layout tag\n    std::string layoutName;\n    std::regex layoutReg(\"<%layout[ \\\\t]+(((?!%\\\\}).)*[^ \\\\t])[ \\\\t]*%>\");\n    for (std::string buffer; std::getline(infile, buffer);)\n    {\n        std::smatch results;\n        if (std::regex_search(buffer, results, layoutReg))\n        {\n            if (results.size() > 1)\n            {\n                layoutName = results[1].str();\n                break;\n            }\n        }\n    }\n    infile.clear();\n    infile.seekg(0, std::ifstream::beg);\n    bool import_flag{false};\n    for (std::string buffer; std::getline(infile, buffer);)\n    {\n        std::string::size_type pos(0);\n\n        if (!import_flag)\n        {\n            std::string lowerBuffer = buffer;\n            std::transform(lowerBuffer.begin(),\n                           lowerBuffer.end(),\n                           lowerBuffer.begin(),\n                           [](unsigned char c) { return tolower(c); });\n            if ((pos = lowerBuffer.find(cxx_include)) != std::string::npos)\n            {\n                // std::cout<<\"haha find it!\"<<endl;\n                std::string newLine = buffer.substr(pos + cxx_include.length());\n                import_flag = true;\n                if ((pos = newLine.find(cxx_end)) != std::string::npos)\n                {\n                    newLine = newLine.substr(0, pos);\n                    file << newLine << \"\\n\";\n                    break;\n                }\n                else\n                {\n                    file << newLine << \"\\n\";\n                }\n            }\n        }\n        else\n        {\n            // std::cout<<buffer<<endl;\n            if ((pos = buffer.find(cxx_end)) != std::string::npos)\n            {\n                std::string newLine = buffer.substr(0, pos);\n                file << newLine << \"\\n\";\n                break;\n            }\n            else\n            {\n                // std::cout<<\"to source file\"<<buffer<<endl;\n                file << buffer << \"\\n\";\n            }\n        }\n    }\n    // std::cout<<\"import_flag=\"<<import_flag<<std::endl;\n    if (!import_flag)\n    {\n        infile.clear();\n        infile.seekg(0, std::ifstream::beg);\n    }\n\n    if (!namespaces_.empty())\n    {\n        file << \"using namespace \";\n        for (std::size_t i = 0; i < namespaces_.size(); ++i)\n        {\n            if (i != namespaces_.size() - 1)\n            {\n                file << namespaces_[i] << \"::\";\n            }\n            else\n            {\n                file << namespaces_[i] << \";\";\n            }\n        }\n        file << \"\\n\";\n    }\n    file << \"using namespace drogon;\\n\";\n    std::string viewDataName = className + \"_view_data\";\n    // virtual std::string genText(const DrTemplateData &)\n    file << \"std::string \" << className << \"::genText(const DrTemplateData& \"\n         << viewDataName << \")\\n{\\n\";\n    // std::string bodyName=className+\"_bodystr\";\n    std::string streamName = className + \"_tmp_stream\";\n\n    // oSrcFile <<\"\\tstd::string \"<<bodyName<<\";\\n\";\n    file << \"\\tdrogon::OStringStream \" << streamName << \";\\n\";\n    file << \"\\tstd::string layoutName{\\\"\" << layoutName << \"\\\"};\\n\";\n    int cxx_flag = 0;\n    for (std::string buffer; std::getline(infile, buffer);)\n    {\n        if (buffer.length() > 0)\n        {\n            std::smatch results;\n            if (std::regex_search(buffer, results, layoutReg))\n            {\n                if (results.size() > 1)\n                {\n                    continue;\n                }\n            }\n\n            std::regex re(\"\\\\{%[ \\\\t]*(((?!%\\\\}).)*[^ \\\\t])[ \\\\t]*%\\\\}\");\n            buffer = std::regex_replace(buffer, re, \"<%c++$$$$<<$1;%>\");\n        }\n        parseLine(file, buffer, streamName, viewDataName, cxx_flag);\n    }\n    file << \"if(layoutName.empty())\\n{\\n\";\n    file << \"std::string ret{std::move(\" << streamName << \".str())};\\n\";\n    file << \"return ret;\\n}else\\n{\\n\";\n    file << \"auto templ = DrTemplateBase::newTemplate(layoutName);\\n\";\n    file << \"if(!templ) return \\\"\\\";\\n\";\n    file << \"HttpViewData data = \" << viewDataName << \";\\n\";\n    file << \"auto str = std::move(\" << streamName << \".str());\\n\";\n    file << \"if(!str.empty() && str[str.length()-1] == '\\\\n') \"\n            \"str.resize(str.length()-1);\\n\";\n    file << \"data[\\\"\\\"] = std::move(str);\\n\";\n    file << \"return templ->genText(data);\\n\";\n    file << \"}\\n}\\n\";\n}\n"
  },
  {
    "path": "drogon_ctl/create_view.h",
    "content": "/**\n *\n *  @file create_view.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include \"CommandHandler.h\"\nusing namespace drogon;\n\nnamespace drogon_ctl\n{\nclass create_view : public DrObject<create_view>, public CommandHandler\n{\n  public:\n    void handleCommand(std::vector<std::string> &parameters) override;\n\n    std::string script() override\n    {\n        return \"create view class files\";\n    }\n\n  protected:\n    std::string outputPath_{\".\"};\n    std::vector<std::string> namespaces_;\n    bool pathToNamespaceFlag_{false};\n    void createViewFiles(std::vector<std::string> &cspFileNames);\n    int createViewFile(const std::string &script_filename);\n    void newViewHeaderFile(std::ofstream &file, const std::string &className);\n    void newViewSourceFile(std::ofstream &file,\n                           const std::string &className,\n                           const std::string &namespacePrefix,\n                           std::ifstream &infile);\n};\n}  // namespace drogon_ctl\n"
  },
  {
    "path": "drogon_ctl/help.cc",
    "content": "/**\n *\n *  help.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"help.h\"\n#include <drogon/DrClassMap.h>\n#include <iostream>\n#include <memory>\nusing namespace drogon_ctl;\n\nvoid help::handleCommand(std::vector<std::string> &parameters)\n{\n    if (parameters.size() == 0)\n    {\n        std::cout << \"usage: drogon_ctl [-v | --version] [-h | --help] \"\n                     \"<command> [<args>]\"\n                  << std::endl;\n        std::cout << \"commands list:\" << std::endl;\n        for (auto &className : drogon::DrClassMap::getAllClassName())\n        {\n            auto classPtr = std::shared_ptr<DrObjectBase>(\n                drogon::DrClassMap::newObject(className));\n            if (classPtr)\n            {\n                auto cmdHdlPtr =\n                    std::dynamic_pointer_cast<CommandHandler>(classPtr);\n                if (cmdHdlPtr)\n                {\n                    if (!cmdHdlPtr->isTopCommand())\n                        continue;\n                    auto pos = className.rfind(\"::\");\n                    if (pos != std::string::npos)\n                    {\n                        className = className.substr(pos + 2);\n                    }\n                    while (className.length() < 24)\n                        className.append(\" \");\n                    std::cout << className << cmdHdlPtr->script() << std::endl;\n                }\n            }\n        }\n    }\n    else\n    {\n        auto cmd = std::string(\"drogon_ctl::\") + parameters[0];\n\n        auto classPtr =\n            std::shared_ptr<DrObjectBase>(drogon::DrClassMap::newObject(cmd));\n        if (classPtr)\n        {\n            auto cmdHdlPtr =\n                std::dynamic_pointer_cast<CommandHandler>(classPtr);\n            if (cmdHdlPtr)\n            {\n                if (cmdHdlPtr->isTopCommand())\n                    std::cout << cmdHdlPtr->detail() << std::endl;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "drogon_ctl/help.h",
    "content": "/**\n *\n *  help.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include \"CommandHandler.h\"\nusing namespace drogon;\n\nnamespace drogon_ctl\n{\nclass help : public DrObject<help>, public CommandHandler\n{\n  public:\n    void handleCommand(std::vector<std::string> &parameters) override;\n\n    std::string script() override\n    {\n        return \"display this message\";\n    }\n\n    bool isTopCommand() override\n    {\n        return true;\n    }\n};\n}  // namespace drogon_ctl\n"
  },
  {
    "path": "drogon_ctl/main.cc",
    "content": "/**\n *\n *  @file main.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"cmd.h\"\n#include <string>\n#include <vector>\n#include <iostream>\n\nint main(int argc, char *argv[])\n{\n    std::vector<std::string> args;\n    if (argc < 2)\n    {\n        args = {\"help\"};\n        exeCommand(args);\n        return 0;\n    }\n    for (int i = 1; i < argc; ++i)\n    {\n        args.push_back(argv[i]);\n    }\n    if (args.size() > 0)\n    {\n        auto &arg = args[0];\n        if (arg == \"-h\" || arg == \"--help\")\n        {\n            arg = \"help\";\n        }\n        else if (arg == \"-v\" || arg == \"--version\")\n        {\n            arg = \"version\";\n        }\n    }\n\n    exeCommand(args);\n\n    return 0;\n}\n"
  },
  {
    "path": "drogon_ctl/press.cc",
    "content": "/**\n *\n *  press.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by the MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"press.h\"\n#include \"cmd.h\"\n#include <drogon/DrClassMap.h>\n#include <iostream>\n#include <memory>\n#include <iomanip>\n#include <cstdlib>\n#include <json/json.h>\n#include <fstream>\n#include <string>\n#include <unordered_map>\n#ifndef _WIN32\n#include <unistd.h>\n#endif\n\nusing namespace drogon_ctl;\n\nstd::string press::detail()\n{\n    return \"Use press command to do stress testing\\n\"\n           \"Usage:drogon_ctl press <options> <url>\\n\"\n           \"  -n num    number of requests(default : 1)\\n\"\n           \"  -t num    number of threads(default : 1)\\n\"\n           \"  -c num    concurrent connections(default : 1)\\n\"\n           \"  -k        disable SSL certificate validation(default: enable)\\n\"\n           \"  -f        customize http request json file(default: disenable)\\n\"\n           \"  -q        no progress indication(default: show)\\n\\n\"\n           \"example: drogon_ctl press -n 10000 -c 100 -t 4 -q \"\n           \"http://localhost:8080/index.html -f ./http_request.json\\n\";\n}\n\nvoid outputErrorAndExit(const std::string_view &err)\n{\n    std::cout << err << std::endl;\n    exit(1);\n}\n\nvoid press::handleCommand(std::vector<std::string> &parameters)\n{\n    for (auto iter = parameters.begin(); iter != parameters.end(); iter++)\n    {\n        auto &param = *iter;\n        if (param.find(\"-n\") == 0)\n        {\n            if (param == \"-n\")\n            {\n                ++iter;\n                if (iter == parameters.end())\n                {\n                    outputErrorAndExit(\"No number of requests!\");\n                }\n                auto &num = *iter;\n                try\n                {\n                    numOfRequests_ = std::stoll(num);\n                }\n                catch (...)\n                {\n                    outputErrorAndExit(\"Invalid number of requests!\");\n                }\n                continue;\n            }\n            else\n            {\n                auto num = param.substr(2);\n                try\n                {\n                    numOfRequests_ = std::stoll(num);\n                }\n                catch (...)\n                {\n                    outputErrorAndExit(\"Invalid number of requests!\");\n                }\n                continue;\n            }\n        }\n        else if (param.find(\"-t\") == 0)\n        {\n            if (param == \"-t\")\n            {\n                ++iter;\n                if (iter == parameters.end())\n                {\n                    outputErrorAndExit(\"No number of threads!\");\n                }\n                auto &num = *iter;\n                try\n                {\n                    numOfThreads_ = std::stoll(num);\n                }\n                catch (...)\n                {\n                    outputErrorAndExit(\"Invalid number of threads!\");\n                }\n                continue;\n            }\n            else\n            {\n                auto num = param.substr(2);\n                try\n                {\n                    numOfThreads_ = std::stoll(num);\n                }\n                catch (...)\n                {\n                    outputErrorAndExit(\"Invalid number of threads!\");\n                }\n                continue;\n            }\n        }\n        else if (param.find(\"-c\") == 0)\n        {\n            if (param == \"-c\")\n            {\n                ++iter;\n                if (iter == parameters.end())\n                {\n                    outputErrorAndExit(\"No number of connections!\");\n                }\n                auto &num = *iter;\n                try\n                {\n                    numOfConnections_ = std::stoll(num);\n                }\n                catch (...)\n                {\n                    outputErrorAndExit(\"Invalid number of connections!\");\n                }\n                continue;\n            }\n            else\n            {\n                auto num = param.substr(2);\n                try\n                {\n                    numOfConnections_ = std::stoll(num);\n                }\n                catch (...)\n                {\n                    outputErrorAndExit(\"Invalid number of connections!\");\n                }\n                continue;\n            }\n        }\n        else if (param.find(\"-f\") == 0)\n        {\n            if (param == \"-f\")\n            {\n                ++iter;\n                if (iter == parameters.end())\n                {\n                    outputErrorAndExit(\"No http request json file!\");\n                }\n                httpRequestJsonFile_ = *iter;\n                continue;\n            }\n            else\n            {\n                httpRequestJsonFile_ = param.substr(2);\n                continue;\n            }\n        }\n        else if (param == \"-k\")\n        {\n            certValidation_ = false;\n            continue;\n        }\n        else if (param == \"-q\")\n        {\n            processIndication_ = false;\n        }\n        else if (param[0] != '-')\n        {\n            url_ = param;\n        }\n    }\n    // std::cout << \"n=\" << numOfRequests_ << std::endl;\n    // std::cout << \"t=\" << numOfThreads_ << std::endl;\n    // std::cout << \"c=\" << numOfConnections_ << std::endl;\n    // std::cout << \"q=\" << processIndication_ << std::endl;\n    // std::cout << \"url=\" << url_ << std::endl;\n    if (url_.empty() || url_.compare(0, 4, \"http\") != 0 ||\n        (url_.compare(4, 3, \"://\") != 0 && url_.compare(4, 4, \"s://\") != 0))\n    {\n        outputErrorAndExit(\"Invalid URL\");\n    }\n    else\n    {\n        auto pos = url_.find(\"://\");\n        auto posOfPath = url_.find('/', pos + 3);\n        if (posOfPath == std::string::npos)\n        {\n            host_ = url_;\n            path_ = \"/\";\n        }\n        else\n        {\n            host_ = url_.substr(0, posOfPath);\n            path_ = url_.substr(posOfPath);\n        }\n    }\n\n    /*\n    http_request.json\n    {\n        \"method\": \"POST\",\n        \"header\": {\n            \"token\": \"e2e9d0fe-dd14-4eaf-8ac1-0997730a805d\"\n        },\n        \"body\": {\n            \"passwd\": \"123456\",\n            \"account\": \"10001\"\n        }\n    }\n    */\n    if (!httpRequestJsonFile_.empty())\n    {\n        Json::Value httpRequestJson;\n        std::ifstream httpRequestFile(httpRequestJsonFile_,\n                                      std::ifstream::binary);\n        if (!httpRequestFile.is_open())\n        {\n            outputErrorAndExit(std::string{\"No \"} + httpRequestJsonFile_);\n        }\n        httpRequestFile >> httpRequestJson;\n\n        if (!httpRequestJson.isMember(\"method\"))\n        {\n            outputErrorAndExit(\"No contain method\");\n        }\n\n        auto methodStr = httpRequestJson[\"method\"].asString();\n        std::transform(methodStr.begin(),\n                       methodStr.end(),\n                       methodStr.begin(),\n                       ::toupper);\n\n        auto toHttpMethod = [&]() -> drogon::HttpMethod {\n            if (methodStr == \"GET\")\n            {\n                return drogon::HttpMethod::Get;\n            }\n            else if (methodStr == \"POST\")\n            {\n                return drogon::HttpMethod::Post;\n            }\n            else if (methodStr == \"HEAD\")\n            {\n                return drogon::HttpMethod::Head;\n            }\n            else if (methodStr == \"PUT\")\n            {\n                return drogon::HttpMethod::Put;\n            }\n            else if (methodStr == \"DELETE\")\n            {\n                return drogon::HttpMethod::Delete;\n            }\n            else if (methodStr == \"OPTIONS\")\n            {\n                return drogon::HttpMethod::Options;\n            }\n            else if (methodStr == \"PATCH\")\n            {\n                return drogon::HttpMethod::Patch;\n            }\n            else\n            {\n                outputErrorAndExit(\"invalid method\");\n            }\n            return drogon::HttpMethod::Get;\n        };\n\n        std::unordered_map<std::string, std::string> header;\n        if (httpRequestJson.isMember(\"header\"))\n        {\n            auto &jsonValue = httpRequestJson[\"header\"];\n            for (const auto &key : jsonValue.getMemberNames())\n            {\n                if (jsonValue[key].isString())\n                {\n                    header[key] = jsonValue[key].asString();\n                }\n                else\n                {\n                    header[key] = jsonValue[key].toStyledString();\n                }\n            }\n        }\n\n        std::string body;\n        if (httpRequestJson.isMember(\"body\"))\n        {\n            Json::FastWriter fastWriter;\n            body = fastWriter.write(httpRequestJson[\"body\"]);\n        }\n\n        createHttpRequestFunc_ = [this,\n                                  method = toHttpMethod(),\n                                  body = std::move(body),\n                                  header =\n                                      std::move(header)]() -> HttpRequestPtr {\n            auto request = HttpRequest::newHttpRequest();\n            request->setPath(path_);\n            request->setMethod(method);\n            for (const auto &[field, val] : header)\n                request->addHeader(field, val);\n            if (!body.empty())\n                request->setBody(body);\n            return request;\n        };\n    }\n\n    // std::cout << \"host=\" << host_ << std::endl;\n    // std::cout << \"path=\" << path_ << std::endl;\n    doTesting();\n}\n\nvoid press::doTesting()\n{\n    createRequestAndClients();\n    if (clients_.empty())\n    {\n        outputErrorAndExit(\"No connection!\");\n    }\n    statistics_.startDate_ = trantor::Date::now();\n    for (auto &client : clients_)\n    {\n        sendRequest(client);\n    }\n    loopPool_->wait();\n}\n\nvoid press::createRequestAndClients()\n{\n    loopPool_ = std::make_unique<trantor::EventLoopThreadPool>(numOfThreads_);\n    loopPool_->start();\n    for (size_t i = 0; i < numOfConnections_; ++i)\n    {\n        auto client = HttpClient::newHttpClient(host_,\n                                                loopPool_->getNextLoop(),\n                                                false,\n                                                certValidation_);\n        client->enableCookies();\n        clients_.push_back(client);\n    }\n}\n\nvoid press::sendRequest(const HttpClientPtr &client)\n{\n    auto numOfRequest = statistics_.numOfRequestsSent_++;\n    if (numOfRequest >= numOfRequests_)\n    {\n        return;\n    }\n\n    HttpRequestPtr request;\n    if (createHttpRequestFunc_)\n    {\n        request = createHttpRequestFunc_();\n    }\n    else\n    {\n        request = HttpRequest::newHttpRequest();\n        request->setPath(path_);\n        request->setMethod(Get);\n    }\n\n    // std::cout << \"send!\" << std::endl;\n    client->sendRequest(\n        request,\n        [this, client, request](ReqResult r, const HttpResponsePtr &resp) {\n            size_t goodNum, badNum;\n            if (r == ReqResult::Ok)\n            {\n                // std::cout << \"OK\" << std::endl;\n                goodNum = ++statistics_.numOfGoodResponse_;\n                badNum = statistics_.numOfBadResponse_;\n                statistics_.bytesRecieved_ += resp->body().length();\n                auto delay = trantor::Date::now().microSecondsSinceEpoch() -\n                             request->creationDate().microSecondsSinceEpoch();\n                statistics_.totalDelay_ += delay;\n            }\n            else\n            {\n                goodNum = statistics_.numOfGoodResponse_;\n                badNum = ++statistics_.numOfBadResponse_;\n                if (badNum > numOfRequests_ / 10)\n                {\n                    outputErrorAndExit(\"Too many errors\");\n                }\n            }\n            if (goodNum + badNum >= numOfRequests_)\n            {\n                outputResults();\n            }\n            if (r == ReqResult::Ok)\n                sendRequest(client);\n            else\n            {\n                client->getLoop()->runAfter(1, [this, client]() {\n                    sendRequest(client);\n                });\n            }\n\n            if (processIndication_)\n            {\n                auto rec = goodNum + badNum;\n                if (rec % 100000 == 0)\n                {\n                    std::cout << rec << \" responses are received\" << std::endl\n                              << std::endl;\n                }\n            }\n        });\n}\n\nvoid press::outputResults()\n{\n    size_t totalSent = 0;\n    size_t totalRecv = 0;\n    for (auto &client : clients_)\n    {\n        totalSent += client->bytesSent();\n        totalRecv += client->bytesReceived();\n    }\n    auto now = trantor::Date::now();\n    auto microSecs = now.microSecondsSinceEpoch() -\n                     statistics_.startDate_.microSecondsSinceEpoch();\n    double seconds = (double)microSecs / 1000000.0;\n    auto rps = static_cast<size_t>(statistics_.numOfGoodResponse_ / seconds);\n    std::cout << std::endl;\n    std::cout << \"TOTALS:   \" << numOfConnections_ << \" connect, \"\n              << numOfRequests_ << \" requests, \"\n              << statistics_.numOfGoodResponse_ << \" success, \"\n              << statistics_.numOfBadResponse_ << \" fail\" << std::endl;\n\n    std::cout << \"TRAFFIC:  \"\n              << statistics_.bytesRecieved_ / statistics_.numOfGoodResponse_\n              << \" avg bytes, \"\n              << (totalRecv - statistics_.bytesRecieved_) /\n                     statistics_.numOfGoodResponse_\n              << \" avg overhead, \" << statistics_.bytesRecieved_ << \" bytes, \"\n              << totalRecv - statistics_.bytesRecieved_ << \" overhead\"\n              << std::endl;\n\n    std::cout << std::setiosflags(std::ios::fixed) << std::setprecision(3)\n              << \"TIMING:   \" << seconds << \" seconds, \" << rps << \" rps, \"\n              << (double)(statistics_.totalDelay_) /\n                     statistics_.numOfGoodResponse_ / 1000\n              << \" ms avg req time\" << std::endl;\n\n    std::cout << \"SPEED:    download \" << totalRecv / seconds / 1000\n              << \" kBps, upload \" << totalSent / seconds / 1000 << \" kBps\"\n              << std::endl\n              << std::endl;\n    exit(0);\n}\n"
  },
  {
    "path": "drogon_ctl/press.h",
    "content": "/**\n *\n *  press.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by the MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"CommandHandler.h\"\n#include <drogon/DrObject.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/HttpClient.h>\n#include <trantor/utils/Date.h>\n#include <trantor/net/EventLoopThreadPool.h>\n#include <functional>\n#include <string>\n#include <atomic>\n#include <memory>\n#include <vector>\n\nusing namespace drogon;\n\nnamespace drogon_ctl\n{\nstruct Statistics\n{\n    std::atomic_size_t numOfRequestsSent_{0};\n    std::atomic_size_t bytesRecieved_{0};\n    std::atomic_size_t numOfGoodResponse_{0};\n    std::atomic_size_t numOfBadResponse_{0};\n    std::atomic_size_t totalDelay_{0};\n    trantor::Date startDate_;\n    trantor::Date endDate_;\n};\n\nclass press : public DrObject<press>, public CommandHandler\n{\n  public:\n    void handleCommand(std::vector<std::string> &parameters) override;\n\n    std::string script() override\n    {\n        return \"Do stress testing(Use 'drogon_ctl help press' for more \"\n               \"information)\";\n    }\n\n    bool isTopCommand() override\n    {\n        return true;\n    }\n\n    std::string detail() override;\n\n  private:\n    size_t numOfThreads_{1};\n    size_t numOfRequests_{1};\n    size_t numOfConnections_{1};\n    std::string httpRequestJsonFile_;\n    std::function<HttpRequestPtr()> createHttpRequestFunc_;\n    bool certValidation_{true};\n    bool processIndication_{true};\n    std::string url_;\n    std::string host_;\n    std::string path_;\n    void doTesting();\n    void createRequestAndClients();\n    void sendRequest(const HttpClientPtr &client);\n    void outputResults();\n    std::unique_ptr<trantor::EventLoopThreadPool> loopPool_;\n    std::vector<HttpClientPtr> clients_;\n    Statistics statistics_;\n};\n}  // namespace drogon_ctl\n"
  },
  {
    "path": "drogon_ctl/templates/cmake.csp",
    "content": "cmake_minimum_required(VERSION 3.5)\nproject([[ProjectName]] CXX)\n\ninclude(CheckIncludeFileCXX)\n\ncheck_include_file_cxx(any HAS_ANY)\ncheck_include_file_cxx(string_view HAS_STRING_VIEW)\ncheck_include_file_cxx(coroutine HAS_COROUTINE)\nif (NOT \"${CMAKE_CXX_STANDARD}\" STREQUAL \"\")\n    # Do nothing\nelseif (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)\n    set(CMAKE_CXX_STANDARD 20)\nelseif (HAS_ANY AND HAS_STRING_VIEW)\n    set(CMAKE_CXX_STANDARD 17)\nelse ()\n    set(CMAKE_CXX_STANDARD 14)\nendif ()\n\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nset(CMAKE_CXX_EXTENSIONS OFF)\n\nadd_executable(${PROJECT_NAME} main.cc)\n\n# ##############################################################################\n# If you include the drogon source code locally in your project, use this method\n# to add drogon \n# add_subdirectory(drogon) \n# target_link_libraries(${PROJECT_NAME} PRIVATE drogon)\n#\n# and comment out the following lines\nfind_package(Drogon CONFIG REQUIRED)\ntarget_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)\n\n# ##############################################################################\n\nif (CMAKE_CXX_STANDARD LESS 17)\n    message(FATAL_ERROR \"c++17 or higher is required\")\nelseif (CMAKE_CXX_STANDARD LESS 20)\n    message(STATUS \"use c++17\")\nelse ()\n    message(STATUS \"use c++20\")\nendif ()\n\naux_source_directory(controllers CTL_SRC)\naux_source_directory(filters FILTER_SRC)\naux_source_directory(plugins PLUGIN_SRC)\naux_source_directory(models MODEL_SRC)\n\ndrogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views\n                    ${CMAKE_CURRENT_BINARY_DIR})\n# use the following line to create views with namespaces.\n# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views\n#                     ${CMAKE_CURRENT_BINARY_DIR} TRUE)\n# use the following line to create views with namespace CHANGE_ME prefixed\n# and path namespaces.\n# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views\n#                     ${CMAKE_CURRENT_BINARY_DIR} TRUE CHANGE_ME)\n\ntarget_include_directories(${PROJECT_NAME}\n                           PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}\n                                   ${CMAKE_CURRENT_SOURCE_DIR}/models)\ntarget_sources(${PROJECT_NAME}\n               PRIVATE\n               ${SRC_DIR}\n               ${CTL_SRC}\n               ${FILTER_SRC}\n               ${PLUGIN_SRC}\n               ${MODEL_SRC})\n# ##############################################################################\n# uncomment the following line for dynamically loading views \n# set_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS ON)\n\n# ##############################################################################\n\nadd_subdirectory(test)\n"
  },
  {
    "path": "drogon_ctl/templates/config_json.csp",
    "content": "/* This is a JSON format configuration file\n */\n{\n    /*\n    //ssl:The global SSL settings. \"key\" and \"cert\" are the path to the SSL key and certificate. While\n    //    \"conf\" is an array of 1 or 2-element tuples that supplies file style options for `SSL_CONF_cmd`.\n    \"ssl\": {\n        \"cert\": \"../../trantor/trantor/tests/server.crt\",\n        \"key\": \"../../trantor/trantor/tests/server.key\",\n        \"conf\": [\n            //[\"Options\", \"-SessionTicket\"], \n            //[\"Options\", \"Compression\"]\n        ]\n    },\n    \"listeners\": [\n        {\n            //address: Ip address,0.0.0.0 by default\n            \"address\": \"0.0.0.0\",\n            //port: Port number\n            \"port\": 80,\n            //https: If true, use https for security,false by default\n            \"https\": false\n        },\n        {\n            \"address\": \"0.0.0.0\",\n            \"port\": 443,\n            \"https\": true,\n            //cert,key: Cert file path and key file path, empty by default,\n            //if empty, use the global setting\n            \"cert\": \"\",\n            \"key\": \"\",\n            //use_old_tls: enable the TLS1.0/1.1, false by default\n            \"use_old_tls\": false,\n            \"ssl_conf\": [\n                //[\"MinProtocol\", \"TLSv1.3\"]\n            ]\n        }\n    ],\n    \"db_clients\": [\n        {\n            //name: Name of the client,'default' by default\n            \"name\": \"default\",\n            //rdbms: Server type, postgresql,mysql or sqlite3, \"postgresql\" by default\n            \"rdbms\": \"postgresql\",\n            //filename: Sqlite3 db file name\n            //\"filename\":\"\",\n            //host: Server address,localhost by default\n            \"host\": \"127.0.0.1\",\n            //port: Server port, 5432 by default\n            \"port\": 5432,\n            //dbname: Database name\n            \"dbname\": \"test\",\n            //user: 'postgres' by default\n            \"user\": \"\",\n            //passwd: '' by default\n            \"passwd\": \"\",\n            //is_fast: false by default, if it is true, the client is faster but user can't call\n            //any synchronous interface of it.\n            \"is_fast\": false,\n            //client_encoding: The character set used by the client. it is empty string by default which \n            //means use the default character set.\n            //\"client_encoding\": \"\",\n            //number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of  \n            //connections per IO thread, otherwise it is the total number of all connections.  \n            \"number_of_connections\": 1,\n            //timeout: -1.0 by default, in seconds, the timeout for executing a SQL query.\n            //zero or negative value means no timeout.\n            \"timeout\": -1.0,\n            //auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see\n            //the wiki for more details.\n            \"auto_batch\": false\n            //connect_options: extra options for the connection. Only works for PostgreSQL now.\n            //For more information, see https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-OPTIONS\n            //\"connect_options\": { \"statement_timeout\": \"1s\" }\n        }\n    ],\n    \"redis_clients\": [\n        {\n            //name: Name of the client,'default' by default\n            \"name\": \"default\",\n            //host: Server IP, 127.0.0.1 by default\n            \"host\": \"127.0.0.1\",\n            //port: Server port, 6379 by default\n            \"port\": 6379,\n            //username: '' by default which means 'default' in redis ACL\n            \"username\": \"\",\n            //passwd: '' by default\n            \"passwd\": \"\",\n            //db index: 0 by default\n            \"db\": 0,\n            //is_fast: false by default, if it is true, the client is faster but user can't call\n            //any synchronous interface of it.\n            \"is_fast\": false,\n            //number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of  \n            //connections per IO thread, otherwise it is the total number of all connections.  \n            \"number_of_connections\": 1,\n            //timeout: -1.0 by default, in seconds, the timeout for executing a command.\n            //zero or negative value means no timeout.\n            \"timeout\": -1.0\n        }\n    ],*/\n    \"app\": {\n        //number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads\n        //is the number of CPU cores\n        \"number_of_threads\": 1,\n        //enable_session: False by default\n        \"enable_session\": false,\n        \"session_timeout\": 0,\n        //string value of SameSite attribute of the Set-Cookie HTTP response header\n        //valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'\n        \"session_same_site\" : \"Null\",\n        //session_cookie_key: The cookie key of the session, \"JSESSIONID\" by default\n        \"session_cookie_key\": \"JSESSIONID\",\n        //session_max_age: The max age of the session cookie, -1 by default\n        \"session_max_age\": -1,\n        //document_root: Root path of HTTP document, default path is ./\n        \"document_root\": \"./\",\n        //home_page: Set the HTML file of the home page, the default value is \"index.html\"\n        //If there isn't any handler registered to the path \"/\", the home page file in the \"document_root\" is send to clients as a response\n        //to the request for \"/\".\n        \"home_page\": \"index.html\",\n        //use_implicit_page: enable implicit pages if true, true by default\n        \"use_implicit_page\": true,\n        //implicit_page: Set the file which would the server access in a directory that a user accessed.\n        //For example, by default, http://localhost/a-directory resolves to http://localhost/a-directory/index.html.\n        \"implicit_page\": \"index.html\",\n        //static_file_headers: Headers for static files\n        /*\"static_file_headers\": [\n            {\n                \"name\": \"field-name\",\n                \"value\": \"field-value\"\n            }\n        ],*/\n        //upload_path: The path to save the uploaded file. \"uploads\" by default. \n        //If the path isn't prefixed with /, ./ or ../,\n        //it is relative path of document_root path\n        \"upload_path\": \"uploads\",\n        /* file_types:\n         * HTTP download file types,The file types supported by drogon\n         * by default are \"html\", \"js\", \"css\", \"xml\", \"xsl\", \"txt\", \"svg\",\n         * \"ttf\", \"otf\", \"woff2\", \"woff\" , \"eot\", \"png\", \"jpg\", \"jpeg\",\n         * \"gif\", \"bmp\", \"ico\", \"icns\", etc. */\n        \"file_types\": [\n            \"gif\",\n            \"png\",\n            \"jpg\",\n            \"js\",\n            \"css\",\n            \"html\",\n            \"ico\",\n            \"swf\",\n            \"xap\",\n            \"apk\",\n            \"cur\",\n            \"xml\",\n            \"webp\",\n            \"svg\"\n        ],\n        // mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types\n        // note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them.\n        \"mime\": {\n            // \"text/markdown\": \"md\",\n            // \"text/gemini\": [\"gmi\", \"gemini\"]\n        },\n        //locations: An array of locations of static files for GET requests.\n        \"locations\": [\n            {\n                //uri_prefix: The URI prefix of the location prefixed with \"/\", the default value is \"\" that disables the location.\n                //\"uri_prefix\": \"/.well-known/acme-challenge/\",\n                //default_content_type: The default content type of the static files without\n                //an extension. empty string by default.\n                \"default_content_type\": \"text/plain\",\n                //alias: The location in file system, if it is prefixed with \"/\", it \n                //presents an absolute path, otherwise it presents a relative path to \n                //the document_root path. \n                //The default value is \"\" which means use the document root path as the location base path.\n                \"alias\": \"\",\n                //is_case_sensitive: indicates whether the URI prefix is case sensitive.\n                \"is_case_sensitive\": false,\n                //allow_all: true by default. If it is set to false, only static files with a valid extension can be accessed.\n                \"allow_all\": true,\n                //is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.\n                \"is_recursive\": true,\n                //filters: string array, the filters applied to the location.\n                \"filters\": []\n            }\n        ],\n        //max_connections: maximum number of connections, 100000 by default\n        \"max_connections\": 100000,\n        //max_connections_per_ip: maximum number of connections per client, 0 by default which means no limit\n        \"max_connections_per_ip\": 0,\n        //Load_dynamic_views: False by default, when set to true, drogon\n        //compiles and loads dynamically \"CSP View Files\" in directories defined\n        //by \"dynamic_views_path\"\n        \"load_dynamic_views\": false,\n        //dynamic_views_path: If the path isn't prefixed with /, ./ or ../,\n        //it is relative path of document_root path\n        \"dynamic_views_path\": [\n            \"./views\"\n        ],\n        //dynamic_views_output_path: Default by an empty string which means the output path of source \n        //files is the path where the csp files locate. If the path isn't prefixed with /, it is relative \n        //path of the current working directory.\n        \"dynamic_views_output_path\": \"\",\n        //json_parser_stack_limit: 1000 by default, the maximum number of stack depth when reading a json string by the jsoncpp library.\n        \"json_parser_stack_limit\": 1000,\n        //enable_unicode_escaping_in_json: true by default, enable unicode escaping in json.\n        \"enable_unicode_escaping_in_json\": true,\n        //float_precision_in_json: set precision of float number in json. \n        \"float_precision_in_json\": {\n            //precision: 0 by default, 0 means use the default precision of the jsoncpp lib. \n            \"precision\": 0,\n            //precision_type: must be \"significant\" or \"decimal\", defaults to \"significant\" that means \n            //setting max number of significant digits in string, \"decimal\" means setting max number of \n            //digits after \".\" in string\n            \"precision_type\": \"significant\"\n        },\n        //log: Set log output, drogon output logs to stdout by default\n        \"log\": {\n            //use_spdlog: Use spdlog library to log\n            \"use_spdlog\": false,\n            //log_path: Log file path,empty by default,in which case,logs are output to the stdout\n            //\"log_path\": \"./\",\n            //logfile_base_name: Log file base name,empty by default which means drogon names logfile as\n            //drogon.log ...\n            \"logfile_base_name\": \"\",\n            //log_size_limit: 100000000 bytes by default,\n            //When the log file size reaches \"log_size_limit\", the log file is switched.\n            \"log_size_limit\": 100000000,\n            //max_files: 0 by default,\n            //When the number of old log files exceeds \"max_files\", the oldest file will be deleted. 0 means never delete.\n            \"max_files\": 0,\n            //log_level: \"DEBUG\" by default,options:\"TRACE\",\"DEBUG\",\"INFO\",\"WARN\"\n            //The TRACE level is only valid when built in DEBUG mode.\n            \"log_level\": \"DEBUG\",\n            //display_local_time: false by default, if true, the log time is displayed in local time\n            \"display_local_time\": false\n        },\n        //run_as_daemon: False by default\n        \"run_as_daemon\": false,\n        //handle_sig_term: True by default\n        \"handle_sig_term\": true,\n        //relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;\n        \"relaunch_on_error\": false,\n        //use_sendfile: True by default, if true, the program \n        //uses sendfile() system-call to send static files to clients;\n        \"use_sendfile\": true,\n        //use_gzip: True by default, use gzip to compress the response body's content;\n        \"use_gzip\": true,\n        //use_brotli: False by default, use brotli to compress the response body's content;\n        \"use_brotli\": false,\n        //static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,\n        //0 means cache forever, the negative value means no cache\n        \"static_files_cache_time\": 5,\n        //simple_controllers_map: Used to configure mapping from path to simple controller\n        //\"simple_controllers_map\": [\n        //    {\n        //        \"path\": \"/path/name\",\n        //        \"controller\": \"controllerClassName\",\n        //        \"http_methods\": [\n        //            \"get\",\n        //            \"post\"\n        //        ],\n        //        \"filters\": [\n        //            \"FilterClassName\"\n        //        ]\n        //    }\n        //],\n        //idle_connection_timeout: Defaults to 60 seconds, the lifetime \n        //of the connection without read or write\n        \"idle_connection_timeout\": 60,\n        //server_header_field: Set the 'Server' header field in each response sent by drogon,\n        //empty string by default with which the 'Server' header field is set to \"Server: drogon/version string\\r\\n\"\n        \"server_header_field\": \"\",\n        //enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default \n        //value is true.\n        \"enable_server_header\": true,\n        //enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default \n        //value is true.\n        \"enable_date_header\": true,\n        //keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection. \n        //After the maximum number of requests are made, the connection is closed.\n        //The default value of 0 means no limit.\n        \"keepalive_requests\": 0,\n        //pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer. \n        //After the maximum number of requests are made, the connection is closed.\n        //The default value of 0 means no limit.\n        \"pipelining_requests\": 0,\n        //gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed \n        //file with the extension \".gz\" in the same path and send the compressed file to the client.\n        //The default value of gzip_static is true.\n        \"gzip_static\": true,\n        //br_static: If it is set to true, when the client requests a static file, drogon first finds the compressed \n        //file with the extension \".br\" in the same path and send the compressed file to the client.\n        //The default value of br_static is true.\n        \"br_static\": true,\n        //client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is \"1M\".\n        //One can set it to \"1024\", \"1k\", \"10M\", \"1G\", etc. Setting it to \"\" means no limit.\n        \"client_max_body_size\": \"1M\",\n        //max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is \"64K\" bytes.\n        //If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.\n        //Setting it to \"\" means no limit.\n        \"client_max_memory_body_size\": \"64K\",\n        //client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is \"128K\".\n        //One can set it to \"1024\", \"1k\", \"10M\", \"1G\", etc. Setting it to \"\" means no limit.\n        \"client_max_websocket_message_size\": \"128K\",\n        //reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time.\n        \"reuse_port\": false,\n        // enabled_compressed_request: Defaults to false. If true the server will automatically decompress compressed request bodies.\n        // Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests.\n        // Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request\n        // will be rejected.\n        \"enabled_compressed_request\": false,\n        // enable_request_stream: Defaults to false. If true the server will enable stream mode for http requests.\n        // See the wiki for more details.\n        \"enable_request_stream\": false,\n    },\n    //plugins: Define all plugins running in the application\n    \"plugins\": [\n        {\n            //name: The class name of the plugin\n            \"name\": \"drogon::plugin::PromExporter\",\n            //dependencies: Plugins that the plugin depends on. It can be commented out\n            \"dependencies\": [],\n            //config: The configuration of the plugin. This json object is the parameter to initialize the plugin.\n            //It can be commented out\n            \"config\": {\n                \"path\": \"/metrics\"\n            }\n        },\n        {\n            \"name\": \"drogon::plugin::AccessLogger\",\n            \"dependencies\": [],\n            \"config\": {\n                \"use_spdlog\": false,\n                \"log_path\": \"\",\n                \"log_format\": \"\",\n                \"log_file\": \"access.log\",\n                \"log_size_limit\": 0,\n                \"use_local_time\": true,\n                \"log_index\": 0,\n                // \"show_microseconds\": true,\n                // \"custom_time_format\": \"\",\n                // \"use_real_ip\": false\n            }\n        }\n    ],\n    //custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method. \n    \"custom_config\": {}\n}\n"
  },
  {
    "path": "drogon_ctl/templates/config_yaml.csp",
    "content": "# This is a YAML format configuration file\n\n# ssl:The global SSL settings. \"key\" and \"cert\" are the path to the SSL key and certificate. While\n#     \"conf\" is an array of 1 or 2-element tuples that supplies file style options for `SSL_CONF_cmd`.\n# ssl:\n#   cert: ../../trantor/trantor/tests/server.crt\n#   key: ../../trantor/trantor/tests/server.key\n#   conf: [\n#     # [Options, -SessionTicket],\n#     # [Options, Compression]\n#   ]\n# listeners:\n#     # address: Ip address,0.0.0.0 by default\n#   - address: 0.0.0.0\n#     # port: Port number\n#     port: 80\n#     # https: If true, use https for security,false by default\n#     https: false\n#   - address: 0.0.0.0\n#     port: 443\n#     https: true\n#     # cert,key: Cert file path and key file path, empty by default,\n#     # if empty, use the global setting\n#     cert: ''\n#     key: ''\n#     # use_old_tls: enable the TLS1.0/1.1, false by default\n#     use_old_tls: false\n#     ssl_conf: [\n#       # [MinProtocol, TLSv1.3]\n#     ]\n# db_clients:\n#     # name: Name of the client,'default' by default\n#   - name: default\n#     # rdbms: Server type, postgresql,mysql or sqlite3, \"postgresql\" by default\n#     rdbms: postgresql\n#     # filename: Sqlite3 db file name\n#     # filename: ''\n#     # host: Server address,localhost by default\n#     host: 127.0.0.1\n#     # port: Server port, 5432 by default\n#     port: 5432\n#     # dbname: Database name\n#     dbname: test\n#     # user: 'postgres' by default\n#     user: ''\n#     # passwd: '' by default\n#     passwd: ''\n#     # is_fast: false by default, if it is true, the client is faster but user can't call\n#     # any synchronous interface of it.\n#     is_fast: false\n#     # client_encoding: The character set used by the client. it is empty string by default which \n#     # means use the default character set.\n#     # client_encoding: ''\n#     # number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of  \n#     # connections per IO thread, otherwise it is the total number of all connections.  \n#     number_of_connections: 1\n#     # timeout: -1 by default, in seconds, the timeout for executing a SQL query.\n#     # zero or negative value means no timeout.\n#     timeout: -1\n#     # auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see\n#     # the wiki for more details.\n#     auto_batch: false\n#     # connect_options: extra options for the connection. Only works for PostgreSQL now.\n#     # For more information, see https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-OPTIONS\n#     # connect_options:\n#     #   statement_timeout: '1s'\n# redis_clients:\n#     # name: Name of the client,'default' by default\n#   - name: default\n#     # host: Server IP, 127.0.0.1 by default\n#     host: 127.0.0.1\n#     # port: Server port, 6379 by default\n#     port: 6379\n#     # username: '' by default which means 'default' in redis ACL\n#     username: ''\n#     # passwd: '' by default\n#     passwd: ''\n#     # db index: 0 by default\n#     db: 0\n#     # is_fast: false by default, if it is true, the client is faster but user can't call\n#     # any synchronous interface of it.\n#     is_fast: false\n#     # number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of  \n#     # connections per IO thread, otherwise it is the total number of all connections.  \n#     number_of_connections: 1\n#     # timeout: -1.0 by default, in seconds, the timeout for executing a command.\n#     # zero or negative value means no timeout.\n#     timeout: -1\napp:\n  # number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads\n  # is the number of CPU cores\n  number_of_threads: 1\n  # enable_session: False by default\n  enable_session: false\n  session_timeout: 0\n  # string value of SameSite attribute of the Set-Cookie HTTP response header\n  # valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'\n  session_same_site: 'Null'\n  # session_cookie_key: The cookie key of the session, \"JSESSIONID\" by default\n  session_cookie_key: 'JSESSIONID'\n  # session_max_age: The max age of the session cookie, -1 by default\n  session_max_age: -1\n  # document_root: Root path of HTTP document, default path is ./\n  document_root: ./\n  # home_page: Set the HTML file of the home page, the default value is \"index.html\"\n  # If there isn't any handler registered to the path \"/\", the home page file in the \"document_root\" is send to clients as a response\n  # to the request for \"/\".\n  home_page: index.html\n  # use_implicit_page: enable implicit pages if true, true by default\n  use_implicit_page: true\n  # implicit_page: Set the file which would the server access in a directory that a user accessed.\n  # For example, by default, http://localhost/a-directory resolves to http://localhost/a-directory/index.html.\n  implicit_page: index.html\n  # static_file_headers: Headers for static files\n  # static_file_headers:\n  #   - name: field-name\n  #     value: field-value\n  # upload_path: The path to save the uploaded file. \"uploads\" by default. \n  # If the path isn't prefixed with /, ./ or ../,\n  # it is relative path of document_root path\n  upload_path: uploads\n  # file_types:\n  # HTTP download file types,The file types supported by drogon\n  # by default are \"html\", \"js\", \"css\", \"xml\", \"xsl\", \"txt\", \"svg\",\n  # \"ttf\", \"otf\", \"woff2\", \"woff\" , \"eot\", \"png\", \"jpg\", \"jpeg\",\n  # \"gif\", \"bmp\", \"ico\", \"icns\", etc.\n  file_types:\n    - gif\n    - png\n    - jpg\n    - js\n    - css\n    - html\n    - ico\n    - swf\n    - xap\n    - apk\n    - cur\n    - xml\n  # mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types\n  # note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them.\n  mime: {\n    # text/markdown: md\n    # text/gemini:\n    #   - gmi\n    #   - gemini\n    }\n  # locations: An array of locations of static files for GET requests.\n  locations:\n      # uri_prefix: The URI prefix of the location prefixed with \"/\", the default value is \"\" that disables the location.\n    - uri_prefix: '' # /.well-known/acme-challenge/\n      # default_content_type: The default content type of the static files without\n      # an extension. empty string by default.\n      default_content_type: text/plain\n      # alias: The location in file system, if it is prefixed with \"/\", it \n      # presents an absolute path, otherwise it presents a relative path to \n      # the document_root path. \n      # The default value is \"\" which means use the document root path as the location base path.\n      alias: ''\n      # is_case_sensitive: indicates whether the URI prefix is case sensitive.\n      is_case_sensitive: false\n      # allow_all: true by default. If it is set to false, only static files with a valid extension can be accessed.\n      allow_all: true\n      # is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.\n      is_recursive: true\n      # filters: string array, the filters applied to the location.\n      filters: []\n  # max_connections: maximum number of connections, 100000 by default\n  max_connections: 100000\n  # max_connections_per_ip: maximum number of connections per client, 0 by default which means no limit\n  max_connections_per_ip: 0\n  # Load_dynamic_views: False by default, when set to true, drogon\n  # compiles and loads dynamically \"CSP View Files\" in directories defined\n  # by \"dynamic_views_path\"\n  load_dynamic_views: false\n  # dynamic_views_path: If the path isn't prefixed with /, ./ or ../,\n  # it is relative path of document_root path\n  dynamic_views_path:\n    - ./views\n  # dynamic_views_output_path: Default by an empty string which means the output path of source \n  # files is the path where the csp files locate. If the path isn't prefixed with /, it is relative \n  # path of the current working directory.\n  dynamic_views_output_path: ''\n  # json_parser_stack_limit: 1000 by default, the maximum number of stack depth when reading a json string by the jsoncpp library.\n  json_parser_stack_limit: 1000\n  # enable_unicode_escaping_in_json: true by default, enable unicode escaping in json.\n  enable_unicode_escaping_in_json: true\n  # float_precision_in_json: set precision of float number in json. \n  float_precision_in_json:\n    # precision: 0 by default, 0 means use the default precision of the jsoncpp lib. \n    precision: 0\n    # precision_type: must be \"significant\" or \"decimal\", defaults to \"significant\" that means \n    # setting max number of significant digits in string, \"decimal\" means setting max number of \n    # digits after \".\" in string\n    precision_type: significant\n  # log: Set log output, drogon output logs to stdout by default\n  log:\n    # use_spdlog: Use spdlog library to log\n    use_spdlog: false\n    # log_path: Log file path,empty by default,in which case,logs are output to the stdout\n    # log_path: ./\n    # logfile_base_name: Log file base name,empty by default which means drogon names logfile as\n    # drogon.log ...\n    logfile_base_name: ''\n    # log_size_limit: 100000000 bytes by default,\n    # When the log file size reaches \"log_size_limit\", the log file is switched.\n    log_size_limit: 100000000\n    # max_files: 0 by default,\n    # When the number of old log files exceeds \"max_files\", the oldest file will be deleted. 0 means never delete.\n    max_files: 0\n    # log_level: \"DEBUG\" by default,options:\"TRACE\",\"DEBUG\",\"INFO\",\"WARN\"\n    # The TRACE level is only valid when built in DEBUG mode.\n    log_level: DEBUG\n    # display_local_time: false by default, if true, the log time is displayed in local time\n    display_local_time: false\n  # run_as_daemon: False by default\n  run_as_daemon: false\n  # handle_sig_term: True by default\n  handle_sig_term: true\n  # relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;\n  relaunch_on_error: false\n  # use_sendfile: True by default, if true, the program \n  # uses sendfile() system-call to send static files to clients;\n  use_sendfile: true\n  # use_gzip: True by default, use gzip to compress the response body's content;\n  use_gzip: true\n  # use_brotli: False by default, use brotli to compress the response body's content;\n  use_brotli: false\n  # static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,\n  # 0 means cache forever, the negative value means no cache\n  static_files_cache_time: 5\n  # simple_controllers_map: Used to configure mapping from path to simple controller\n  # simple_controllers_map:\n  #   - path: /path/name\n  #     controller: controllerClassName\n  #     http_methods:\n  #       - get\n  #       - post\n  #     filters:\n  #       - FilterClassName\n  # idle_connection_timeout: Defaults to 60 seconds, the lifetime \n  # of the connection without read or write\n  idle_connection_timeout: 60\n  # server_header_field: Set the 'Server' header field in each response sent by drogon,\n  # empty string by default with which the 'Server' header field is set to \"Server: drogon/version string\\r\\n\"\n  server_header_field: ''\n  # enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default \n  # value is true.\n  enable_server_header: true\n  # enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default \n  # value is true.\n  enable_date_header: true\n  # keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection. \n  # After the maximum number of requests are made, the connection is closed.\n  # The default value of 0 means no limit.\n  keepalive_requests: 0\n  # pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer. \n  # After the maximum number of requests are made, the connection is closed.\n  # The default value of 0 means no limit.\n  pipelining_requests: 0\n  # gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed \n  # file with the extension \".gz\" in the same path and send the compressed file to the client.\n  # The default value of gzip_static is true.\n  gzip_static: true\n  # br_static: If it is set to true, when the client requests a static file, drogon first finds the compressed \n  # file with the extension \".br\" in the same path and send the compressed file to the client.\n  # The default value of br_static is true.\n  br_static: true\n  # client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is \"1M\".\n  # One can set it to \"1024\", \"1k\", \"10M\", \"1G\", etc. Setting it to \"\" means no limit.\n  client_max_body_size: 1M\n  # max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is \"64K\" bytes.\n  # If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.\n  # Setting it to \"\" means no limit.\n  client_max_memory_body_size: 64K\n  # client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is \"128K\".\n  # One can set it to \"1024\", \"1k\", \"10M\", \"1G\", etc. Setting it to \"\" means no limit.\n  client_max_websocket_message_size: 128K\n  # reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time.\n  reuse_port: false\n  # enabled_compressed_request: Defaults to false. If true the server will automatically decompress compressed request bodies.\n  # Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests.\n  # Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request\n  # will be rejected.\n  enabled_compressed_request: false\n  # enable_request_stream: Defaults to false. If true the server will enable stream mode for http requests.\n  # See the wiki for more details.\n  enable_request_stream: false\n# plugins: Define all plugins running in the application\nplugins:\n    # name: The class name of the plugin\n  - name: drogon::plugin::PromExporter\n    # dependencies: Plugins that the plugin depends on. It can be commented out\n    dependencies: []\n    # config: The configuration of the plugin. This json object is the parameter to initialize the plugin.\n    # It can be commented out\n    config:\n      path: /metrics\n  - name: drogon::plugin::AccessLogger\n    dependencies: []\n    config:\n      use_spdlog: false\n      log_path: ''\n      log_format: ''\n      log_file: access.log\n      log_size_limit: 0\n      use_local_time: true\n      log_index: 0\n      # show_microseconds: true\n      # custom_time_format: ''\n      # use_real_ip: false\n# custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method. \ncustom_config: {}\n"
  },
  {
    "path": "drogon_ctl/templates/demoMain.csp",
    "content": "#include <drogon/drogon.h>\nint main() {\n    //Set HTTP listener address and port\n    drogon::app().addListener(\"0.0.0.0\", 5555);\n    //Load config file\n    //drogon::app().loadConfigFile(\"../config.json\");\n    //drogon::app().loadConfigFile(\"../config.yaml\");\n    //Run HTTP framework,the method will block in the internal event loop\n    drogon::app().run();\n    return 0;\n}\n"
  },
  {
    "path": "drogon_ctl/templates/filter_cc.csp",
    "content": "/**\n *\n *  [[filename]].cc\n *\n */\n\n#include \"[[filename]].h\"\n\nusing namespace drogon;\n<%c++auto namespaceStr=@@.get<std::string>(\"namespaceString\");\nif(!namespaceStr.empty())\n$$<<\"using namespace \"<<namespaceStr<<\";\\n\";\n%>\n\nvoid [[className]]::doFilter(const HttpRequestPtr &req,\n                         FilterCallback &&fcb,\n                         FilterChainCallback &&fccb)\n{\n    //Edit your logic here\n    if (1)\n    {\n        //Passed\n        fccb();\n        return;\n    }\n    //Check failed\n    auto res = drogon::HttpResponse::newHttpResponse();\n    res->setStatusCode(k500InternalServerError);\n    fcb(res);\n}\n"
  },
  {
    "path": "drogon_ctl/templates/filter_h.csp",
    "content": "/**\n *\n *  [[filename]].h\n *\n */\n\n#pragma once\n\n#include <drogon/HttpFilter.h>\nusing namespace drogon;\n<%c++\nauto namespaceVector=@@.get<std::vector<std::string>>(\"namespaceVector\");\nif(namespaceVector.empty())\n$$<<\"\\n\";\nfor(auto &namespaceName:namespaceVector){%>\nnamespace {%namespaceName%}\n\n{\n<%c++}\n$$<<\"\\n\";%>\nclass [[className]] : public HttpFilter<[[className]]>\n{\n  public:\n    [[className]]() {}\n    void doFilter(const HttpRequestPtr &req,\n                  FilterCallback &&fcb,\n                  FilterChainCallback &&fccb) override;\n};\n\n<%c++for(size_t i=0;i<namespaceVector.size();i++){%>\n}\n<%c++}%>"
  },
  {
    "path": "drogon_ctl/templates/gitignore.csp",
    "content": "# Created by https://www.toptal.com/developers/gitignore/api/intellij+all,visualstudio,visualstudiocode,cmake,c,c++\n# Edit at https://www.toptal.com/developers/gitignore?templates=intellij+all,visualstudio,visualstudiocode,cmake,c,c++\n\n### C ###\n# Prerequisites\n*.d\n\n# Object files\n*.o\n*.ko\n*.obj\n*.elf\n\n# Linker output\n*.ilk\n*.map\n*.exp\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Libraries\n*.lib\n*.a\n*.la\n*.lo\n\n# Shared objects (inc. Windows DLLs)\n*.dll\n*.so\n*.so.*\n*.dylib\n\n# Executables\n*.exe\n*.out\n*.app\n*.i*86\n*.x86_64\n*.hex\n\n# Debug files\n*.dSYM/\n*.su\n*.idb\n*.pdb\n\n# Kernel Module Compile Results\n*.mod*\n*.cmd\n.tmp_versions/\nmodules.order\nModule.symvers\nMkfile.old\ndkms.conf\n\n### C++ ###\n# Prerequisites\n\n# Compiled Object files\n*.slo\n\n# Precompiled Headers\n\n# Linker files\n\n# Debugger Files\n\n# Compiled Dynamic libraries\n\n# Fortran module files\n*.mod\n*.smod\n\n# Compiled Static libraries\n*.lai\n\n# Executables\n\n### CMake ###\nCMakeLists.txt.user\nCMakeCache.txt\nCMakeFiles\nCMakeScripts\nTesting\nMakefile\ncmake_install.cmake\ninstall_manifest.txt\ncompile_commands.json\nCTestTestfile.cmake\n_deps\nCMakeUserPresets.json\n\n### CMake Patch ###\n# External projects\n*-prefix/\n\n### Intellij+all ###\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider\n# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\n\n# User-specific stuff\n.idea/**/workspace.xml\n.idea/**/tasks.xml\n.idea/**/usage.statistics.xml\n.idea/**/dictionaries\n.idea/**/shelf\n\n# Generated files\n.idea/**/contentModel.xml\n\n# Sensitive or high-churn files\n.idea/**/dataSources/\n.idea/**/dataSources.ids\n.idea/**/dataSources.local.xml\n.idea/**/sqlDataSources.xml\n.idea/**/dynamic.xml\n.idea/**/uiDesigner.xml\n.idea/**/dbnavigator.xml\n\n# Gradle\n.idea/**/gradle.xml\n.idea/**/libraries\n\n# Gradle and Maven with auto-import\n# When using Gradle or Maven with auto-import, you should exclude module files,\n# since they will be recreated, and may cause churn.  Uncomment if using\n# auto-import.\n# .idea/artifacts\n# .idea/compiler.xml\n# .idea/jarRepositories.xml\n# .idea/modules.xml\n# .idea/*.iml\n# .idea/modules\n# *.iml\n# *.ipr\n\n# CMake\ncmake-build-*/\n\n# Mongo Explorer plugin\n.idea/**/mongoSettings.xml\n\n# File-based project format\n*.iws\n\n# IntelliJ\nout/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# Cursive Clojure plugin\n.idea/replstate.xml\n\n# Crashlytics plugin (for Android Studio and IntelliJ)\ncom_crashlytics_export_strings.xml\ncrashlytics.properties\ncrashlytics-build.properties\nfabric.properties\n\n# Editor-based Rest Client\n.idea/httpRequests\n\n# Android studio 3.1+ serialized cache file\n.idea/caches/build_file_checksums.ser\n\n### Intellij+all Patch ###\n# Ignores the whole .idea folder and all .iml files\n# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360\n\n.idea/\n\n# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023\n\n*.iml\nmodules.xml\n.idea/misc.xml\n*.ipr\n\n# Sonarlint plugin\n.idea/sonarlint\n\n### VisualStudioCode ###\n.vscode/*\n!.vscode/tasks.json\n!.vscode/launch.json\n*.code-workspace\n\n### VisualStudioCode Patch ###\n# Ignore all local history of files\n.history\n.ionide\n\n### VisualStudio ###\n## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Mono auto generated files\nmono_crash.*\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Ww][Ii][Nn]32/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n[Ll]ogs/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUnit\n*.VisualState.xml\nTestResult.xml\nnunit-*.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# ASP.NET Scaffolding\nScaffoldingReadMe.txt\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.meta\n*.iobj\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Coverlet is a free, cross platform Code Coverage Tool\ncoverage*[.json, .xml, .info]\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# NuGet Symbol Packages\n*.snupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n*.appxbundle\n*.appxupload\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- [Bb]ackup.rdl\n*- [Bb]ackup ([0-9]).rdl\n*- [Bb]ackup ([0-9][0-9]).rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\nMigrationBackup/\n\n# Ionide (cross platform F# VS Code tools) working folder\n.ionide/\n\n# Fody - auto-generated XML schema\nFodyWeavers.xsd\n\n### VisualStudio Patch ###\n# Additional files built by Visual Studio\n*.tlog\n\n# End of https://www.toptal.com/developers/gitignore/api/intellij+all,visualstudio,visualstudiocode,cmake,c,c++"
  },
  {
    "path": "drogon_ctl/templates/model_cc.csp",
    "content": "<%inc#include \"create_model.h\"\nusing namespace drogon_ctl;\n%>\n/**\n *\n *  [[className]].cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"[[className]].h\"\n<%c++\nstd::vector<std::string> relationshipClassNames;\nauto &relationships=@@.get<std::vector<Relationship>>(\"relationships\");\nauto rdbms=@@.get<std::string>(\"rdbms\");\nfor(auto &relationship : relationships)\n{\n    if(relationship.type() == Relationship::Type::HasOne ||\n       relationship.type() == Relationship::Type::HasMany ||\n       relationship.type() == Relationship::Type::ManyToMany)\n    {\n        auto &name=relationship.targetTableName();\n        auto relationshipClassName=nameTransform(name, true);\n        auto originalClassName=nameTransform(relationship.originalTableName(),true);\n        if(relationshipClassName!=originalClassName)\n        {\n            relationshipClassNames.push_back(relationshipClassName);\n        }\n    }\n    if(relationship.type() == Relationship::Type::ManyToMany)\n    {\n        auto &pivotTableName=relationship.pivotTable().tableName();\n        auto pivotTableClassName=nameTransform(pivotTableName, true);\n        relationshipClassNames.push_back(pivotTableClassName);\n    }\n}\nstd::sort(relationshipClassNames.begin(), relationshipClassNames.end());\nrelationshipClassNames.erase(std::unique(relationshipClassNames.begin(), relationshipClassNames.end()), relationshipClassNames.end());\nfor(std::string &relationshipClassName : relationshipClassNames)\n{\n%>\n#include \"{%relationshipClassName%}.h\"\n<%c++\n}\n%>\n#include <drogon/utils/Utilities.h>\n<%c++\nauto &convertMethods=@@.get<std::vector<ConvertMethod>>(\"convertMethods\");\nfor(auto convertMethod : convertMethods ) {\n    for(auto i : convertMethod.includeFiles()) { %>\n<%c++ $$<<\"#include \"<<i<<\"\\n\";%>\n<%c++\n\t} //for\n} //for\n%>\n#include <string>\n<%c++\n    const auto &cols=@@.get<std::vector<ColumnInfo>>(\"columns\");\n    auto className=@@.get<std::string>(\"className\");\n    std::string indentStr(@@.get<std::string>(\"className\").length(), ' ');\n%>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::[[dbName]]<%c++\nauto &schema=@@.get<std::string>(\"schema\");\nif(!schema.empty())\n{\n    $$<<\"::\"<<schema<<\";\\n\";\n}\nelse\n{\n    $$<<\";\\n\";\n}\n%>\n\n<%c++for(auto col:cols){\n%>\nconst std::string [[className]]::Cols::_{%col.colName_%} = \"{%escapeIdentifier(col.colName_, rdbms)%}\";\n<%c++\n}%>\n<%c++if(@@.get<int>(\"hasPrimaryKey\")<=1){%>\nconst std::string [[className]]::primaryKeyName = \"[[primaryKeyName]]\";\n<%c++}else{%>\nconst std::vector<std::string> [[className]]::primaryKeyName = {<%c++\nauto pkName=@@.get<std::vector<std::string>>(\"primaryKeyName\");\nfor(size_t i=0;i<pkName.size();i++)\n{\n    $$<<\"\\\"\"<<pkName[i]<<\"\\\"\";\n    if(i<(pkName.size()-1))\n        $$<<\",\";\n}\n%>};\n<%c++}%>\n<%c++ if(@@.get<int>(\"hasPrimaryKey\")>0){%>\nconst bool [[className]]::hasPrimaryKey = true;\n<%c++ }else{%>\nconst bool [[className]]::hasPrimaryKey = false;\n<%c++}%>\nconst std::string [[className]]::tableName = \"<%c++\nif(!schema.empty())\n{\n    $$<<schema<<\".\";\n}\n%>{%escapeIdentifier(@@.get<std::string>(\"tableName\"), rdbms)%}\";\n\nconst std::vector<typename [[className]]::MetaData> [[className]]::metaData_={\n<%c++for(size_t i=0;i<cols.size();i++){\n    auto &col=cols[i];\n$$<<\"{\\\"\"<<col.colName_<<\"\\\",\\\"\"<<col.colType_<<\"\\\",\\\"\"<<col.colDatabaseType_<<\"\\\",\"<<col.colLength_<<\",\"<<col.isAutoVal_<<\",\"<<col.isPrimaryKey_<<\",\"<<col.notNull_<<\"}\";\nif(i<(cols.size()-1))\n$$<<\",\\n\";\nelse\n$$<<\"\\n\";\n}%>\n};\nconst std::string &[[className]]::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n[[className]]::[[className]](const Row &r, const ssize_t indexOffset) noexcept\n{\n    if(indexOffset < 0)\n    {\n<%c++\n    for(size_t i = 0; i <cols.size(); ++i)\n    {\n        auto &col = cols[i];\n        if(col.colType_.empty())\n            continue;\n%>\n        if(!r[\"{%col.colName_%}\"].isNull())\n        {\n<%c++\n            if(col.colDatabaseType_==\"date\")\n            {\n                $$<<\"            auto daysStr = r[\\\"\"<<col.colName_<<\"\\\"].as<std::string>();\\n\";\n                $$<<\"            struct tm stm;\\n\";\n                $$<<\"            memset(&stm,0,sizeof(stm));\\n\";\n                $$<<\"            strptime(daysStr.c_str(),\\\"%Y-%m-%d\\\",&stm);\\n\";\n                $$<<\"            time_t t = mktime(&stm);\\n\";\n//                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(::trantor::Date(946656000000000).after(daysNum*86400));\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(t*1000000);\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodAfterDbRead() != \"\" ) {\n                    $$<<\"            \"<< convertMethod->methodAfterDbRead() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_.find(\"timestamp\")!=std::string::npos||col.colDatabaseType_.find(\"datetime\")!=std::string::npos)\n            {\n                $$<<\"            auto timeStr = r[\\\"\"<<col.colName_<<\"\\\"].as<std::string>();\\n\";\n                $$<<\"            struct tm stm;\\n\";\n                $$<<\"            memset(&stm,0,sizeof(stm));\\n\";\n                $$<<\"            auto p = strptime(timeStr.c_str(),\\\"%Y-%m-%d %H:%M:%S\\\",&stm);\\n\";\n                $$<<\"            time_t t = mktime(&stm);\\n\";\n                $$<<\"            size_t decimalNum = 0;\\n\";\n                $$<<\"            if(p)\\n\";\n                $$<<\"            {\\n\";\n                $$<<\"                if(*p=='.')\\n\";\n                $$<<\"                {\\n\";\n                $$<<\"                    std::string decimals(p+1,&timeStr[timeStr.length()]);\\n\";\n                $$<<\"                    while(decimals.length()<6)\\n\";\n                $$<<\"                    {\\n\";\n                $$<<\"                        decimals += \\\"0\\\";\\n\";\n                $$<<\"                    }\\n\";\n                $$<<\"                    decimalNum = (size_t)atol(decimals.c_str());\\n\";\n                $$<<\"                }\\n\";\n //               $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(::trantor::Date(946656000000000).after(daysNum*86400));\\n\";\n                $$<<\"                \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(t*1000000+decimalNum);\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodAfterDbRead() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodAfterDbRead() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"            }\\n\";\n                $$<<\"        }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_==\"bytea\")\n            {\n                $$<<\"            auto str = r[\\\"\"<<col.colName_<<\"\\\"].as<std::string_view>();\\n\";\n                $$<<\"            if(str.length()>=2&&\\n\";\n                $$<<\"                str[0]=='\\\\\\\\'&&str[1]=='x')\\n\";\n                $$<<\"            {\\n\";\n                $$<<\"                \"<<col.colValName_<<\"_=std::make_shared<std::vector<char>>(drogon::utils::hexToBinaryVector(str.data()+2,str.length()-2));\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodAfterDbRead() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodAfterDbRead() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"            }\\n\";\n                $$<<\"        }\\n\";\n                continue;\n            }\n%>\n            {%col.colValName_%}_=std::make_shared<{%col.colType_%}>(r[\"{%col.colName_%}\"].as<{%col.colType_%}>());\n<%c++\n            auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n            if (convertMethod != convertMethods.end() && convertMethod->methodAfterDbRead() != \"\") {\n                $$<<\"            \"<< convertMethod->methodAfterDbRead() << \"(\" << col.colValName_ << \"_);\\n\";\n            } //endif %>\n        }\n<%c++}\n%>\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if(offset + {%cols.size()%} > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n<%c++\n    for(size_t i = 0; i <cols.size(); ++i)\n    {\n        auto &col = cols[i];\n        if(col.colType_.empty())\n            continue;\n%>\n        index = offset + {%i%};\n        if(!r[index].isNull())\n        {\n<%c++\n            if(col.colDatabaseType_==\"date\")\n            {\n                $$<<\"            auto daysStr = r[index].as<std::string>();\\n\";\n                $$<<\"            struct tm stm;\\n\";\n                $$<<\"            memset(&stm,0,sizeof(stm));\\n\";\n                $$<<\"            strptime(daysStr.c_str(),\\\"%Y-%m-%d\\\",&stm);\\n\";\n                $$<<\"            time_t t = mktime(&stm);\\n\";\n //               $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(::trantor::Date(946656000000000).after(daysNum*86400));\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(t*1000000);\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodAfterDbRead() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodAfterDbRead() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_.find(\"timestamp\")!=std::string::npos||col.colDatabaseType_.find(\"datetime\")!=std::string::npos)\n            {\n                $$<<\"            auto timeStr = r[index].as<std::string>();\\n\";\n                $$<<\"            struct tm stm;\\n\";\n                $$<<\"            memset(&stm,0,sizeof(stm));\\n\";\n                $$<<\"            auto p = strptime(timeStr.c_str(),\\\"%Y-%m-%d %H:%M:%S\\\",&stm);\\n\";\n                $$<<\"            time_t t = mktime(&stm);\\n\";\n                $$<<\"            size_t decimalNum = 0;\\n\";\n                $$<<\"            if(p)\\n\";\n                $$<<\"            {\\n\";\n                $$<<\"                if(*p=='.')\\n\";\n                $$<<\"                {\\n\";\n                $$<<\"                    std::string decimals(p+1,&timeStr[timeStr.length()]);\\n\";\n                $$<<\"                    while(decimals.length()<6)\\n\";\n                $$<<\"                    {\\n\";\n                $$<<\"                        decimals += \\\"0\\\";\\n\";\n                $$<<\"                    }\\n\";\n                $$<<\"                    decimalNum = (size_t)atol(decimals.c_str());\\n\";\n                $$<<\"                }\\n\";\n //               $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(::trantor::Date(946656000000000).after(daysNum*86400));\\n\";\n                $$<<\"                \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(t*1000000+decimalNum);\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodAfterDbRead() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodAfterDbRead() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"            }\\n\";\n                $$<<\"        }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_==\"bytea\")\n            {\n                $$<<\"            auto str = r[index].as<std::string_view>();\\n\";\n                $$<<\"            if(str.length()>=2&&\\n\";\n                $$<<\"                str[0]=='\\\\\\\\'&&str[1]=='x')\\n\";\n                $$<<\"            {\\n\";\n                $$<<\"                \"<<col.colValName_<<\"_=std::make_shared<std::vector<char>>(drogon::utils::hexToBinaryVector(str.data()+2,str.length()-2));\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodAfterDbRead() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodAfterDbRead() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"            }\\n\";\n                $$<<\"        }\\n\";\n                continue;\n            }\n%>\n            {%col.colValName_%}_=std::make_shared<{%col.colType_%}>(r[index].as<{%col.colType_%}>());\n<%c++\n            auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n            if (convertMethod != convertMethods.end() && convertMethod->methodAfterDbRead() != \"\") {\n                $$<<\"            \"<< convertMethod->methodAfterDbRead() << \"(\" << col.colValName_ << \"_);\\n\";\n            } //endif %>\n        }\n<%c++}%>\n    }\n\n}\n\n[[className]]::[[className]](const Json::Value &pJson, const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if(pMasqueradingVector.size() != {%cols.size()%})\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n<%c++\n    for(size_t i=0; i<cols.size(); ++i)\n    {\n        auto &col = cols[i];\n        if(col.colType_.empty())\n            continue;\n%>\n    if(!pMasqueradingVector[{%i%}].empty() && pJson.isMember(pMasqueradingVector[{%i%}]))\n    {\n        dirtyFlag_[{%i%}] = true;\n<%c++\n            if(col.colType_ == \"std::string\")\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<std::string>(pJson[pMasqueradingVector[\"<<i<<\"]].asString());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_==\"date\")\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            auto daysStr = pJson[pMasqueradingVector[\"<<i<<\"]].asString();\\n\";\n                $$<<\"            struct tm stm;\\n\";\n                $$<<\"            memset(&stm,0,sizeof(stm));\\n\";\n                $$<<\"            strptime(daysStr.c_str(),\\\"%Y-%m-%d\\\",&stm);\\n\";\n                $$<<\"            time_t t = mktime(&stm);\\n\";\n //               $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(::trantor::Date(946656000000000).after(daysNum*86400));\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(t*1000000);\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_.find(\"timestamp\")!=std::string::npos||col.colDatabaseType_.find(\"datetime\")!=std::string::npos)\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            auto timeStr = pJson[pMasqueradingVector[\"<<i<<\"]].asString();\\n\";\n                $$<<\"            struct tm stm;\\n\";\n                $$<<\"            memset(&stm,0,sizeof(stm));\\n\";\n                $$<<\"            auto p = strptime(timeStr.c_str(),\\\"%Y-%m-%d %H:%M:%S\\\",&stm);\\n\";\n                $$<<\"            time_t t = mktime(&stm);\\n\";\n                $$<<\"            size_t decimalNum = 0;\\n\";\n                $$<<\"            if(p)\\n\";\n                $$<<\"            {\\n\";\n                $$<<\"                if(*p=='.')\\n\";\n                $$<<\"                {\\n\";\n                $$<<\"                    std::string decimals(p+1,&timeStr[timeStr.length()]);\\n\";\n                $$<<\"                    while(decimals.length()<6)\\n\";\n                $$<<\"                    {\\n\";\n                $$<<\"                        decimals += \\\"0\\\";\\n\";\n                $$<<\"                    }\\n\";\n                $$<<\"                    decimalNum = (size_t)atol(decimals.c_str());\\n\";\n                $$<<\"                }\\n\";\n //               $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(::trantor::Date(946656000000000).after(daysNum*86400));\\n\";\n                $$<<\"                \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(t*1000000+decimalNum);\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"            }\\n\";\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_==\"bytea\" || col.colDatabaseType_.find(\"blob\") != std::string::npos)\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            auto str = pJson[pMasqueradingVector[\"<<i<<\"]].asString();\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<std::vector<char>>(drogon::utils::base64DecodeToVector(str));\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_.find(\"uint\") == 0 || col.colType_ == \"unsigned short\")\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<\"<<col.colType_<<\">((\"<<col.colType_<<\")pJson[pMasqueradingVector[\"<<i<<\"]].asUInt64());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_.find(\"int\") == 0 || col.colType_ == \"short\")\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<\"<<col.colType_<<\">((\"<<col.colType_<<\")pJson[pMasqueradingVector[\"<<i<<\"]].asInt64());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_ == \"float\")\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<float>(pJson[pMasqueradingVector[\"<<i<<\"]].asFloat());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"     }\\n\";\n                continue;\n            }\n            else if(col.colType_ == \"double\")\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<double>(pJson[pMasqueradingVector[\"<<i<<\"]].asDouble());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_ == \"bool\")\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<bool>(pJson[pMasqueradingVector[\"<<i<<\"]].asBool());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n%>\n        }\n<%c++\n    }\n%>\n}\n\n[[className]]::[[className]](const Json::Value &pJson) noexcept(false)\n{\n<%c++\n    for(size_t i=0;i<cols.size();i++)\n    {\n        auto &col = cols[i];\n        if(col.colType_.empty())\n            continue;\n%>\n    if(pJson.isMember(\"{%col.colName_%}\"))\n    {\n        dirtyFlag_[{%i%}]=true;\n<%c++\n            if(col.colType_ == \"std::string\")\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<std::string>(pJson[\\\"\"<<col.colName_<<\"\\\"].asString());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_==\"date\")\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            auto daysStr = pJson[\\\"\"<<col.colName_<<\"\\\"].asString();\\n\";\n                $$<<\"            struct tm stm;\\n\";\n                $$<<\"            memset(&stm,0,sizeof(stm));\\n\";\n                $$<<\"            strptime(daysStr.c_str(),\\\"%Y-%m-%d\\\",&stm);\\n\";\n                $$<<\"            time_t t = mktime(&stm);\\n\";\n //               $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(::trantor::Date(946656000000000).after(daysNum*86400));\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(t*1000000);\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_.find(\"timestamp\")!=std::string::npos||col.colDatabaseType_.find(\"datetime\")!=std::string::npos)\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            auto timeStr = pJson[\\\"\"<<col.colName_<<\"\\\"].asString();\\n\";\n                $$<<\"            struct tm stm;\\n\";\n                $$<<\"            memset(&stm,0,sizeof(stm));\\n\";\n                $$<<\"            auto p = strptime(timeStr.c_str(),\\\"%Y-%m-%d %H:%M:%S\\\",&stm);\\n\";\n                $$<<\"            time_t t = mktime(&stm);\\n\";\n                $$<<\"            size_t decimalNum = 0;\\n\";\n                $$<<\"            if(p)\\n\";\n                $$<<\"            {\\n\";\n                $$<<\"                if(*p=='.')\\n\";\n                $$<<\"                {\\n\";\n                $$<<\"                    std::string decimals(p+1,&timeStr[timeStr.length()]);\\n\";\n                $$<<\"                    while(decimals.length()<6)\\n\";\n                $$<<\"                    {\\n\";\n                $$<<\"                        decimals += \\\"0\\\";\\n\";\n                $$<<\"                    }\\n\";\n                $$<<\"                    decimalNum = (size_t)atol(decimals.c_str());\\n\";\n                $$<<\"                }\\n\";\n //               $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(::trantor::Date(946656000000000).after(daysNum*86400));\\n\";\n                $$<<\"                \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(t*1000000+decimalNum);\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"            }\\n\";\n                 $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_==\"bytea\" || col.colDatabaseType_.find(\"blob\") != std::string::npos)\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            auto str = pJson[\\\"\"<<col.colName_<<\"\\\"].asString();\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<std::vector<char>>(drogon::utils::base64DecodeToVector(str));\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_.find(\"uint\") == 0 || col.colType_ == \"unsigned short\")\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<\"<<col.colType_<<\">((\"<<col.colType_<<\")pJson[\\\"\"<<col.colName_<<\"\\\"].asUInt64());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_.find(\"int\") == 0 || col.colType_ == \"short\")\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<\"<<col.colType_<<\">((\"<<col.colType_<<\")pJson[\\\"\"<<col.colName_<<\"\\\"].asInt64());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_ == \"float\")\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<float>(pJson[\\\"\"<<col.colName_<<\"\\\"].asFloat());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"     }\\n\";\n                continue;\n            }\n            else if(col.colType_ == \"double\")\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<double>(pJson[\\\"\"<<col.colName_<<\"\\\"].asDouble());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_ == \"bool\")\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<bool>(pJson[\\\"\"<<col.colName_<<\"\\\"].asBool());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n%>\n    }\n<%c++\n    }\n    %>\n}\n\nvoid [[className]]::updateByMasqueradedJson(const Json::Value &pJson,\n                                            const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if(pMasqueradingVector.size() != {%cols.size()%})\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n<%c++\n    for(size_t i=0;i<cols.size();i++)\n    {\n        auto &col = cols[i];\n        if(col.colType_.empty())\n            continue;\n%>\n    if(!pMasqueradingVector[{%i%}].empty() && pJson.isMember(pMasqueradingVector[{%i%}]))\n    {\n<%c++\n            if(!col.isAutoVal_ && !col.isPrimaryKey_)\n            {\n                $$<<\"        dirtyFlag_[\"<<i<<\"] = true;\\n\";\n            }\n            if(col.colType_ == \"std::string\")\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<std::string>(pJson[pMasqueradingVector[\"<<i<<\"]].asString());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_==\"date\")\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            auto daysStr = pJson[pMasqueradingVector[\"<<i<<\"]].asString();\\n\";\n                $$<<\"            struct tm stm;\\n\";\n                $$<<\"            memset(&stm,0,sizeof(stm));\\n\";\n                $$<<\"            strptime(daysStr.c_str(),\\\"%Y-%m-%d\\\",&stm);\\n\";\n                $$<<\"            time_t t = mktime(&stm);\\n\";\n //               $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(::trantor::Date(946656000000000).after(daysNum*86400));\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(t*1000000);\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_.find(\"timestamp\")!=std::string::npos||col.colDatabaseType_.find(\"datetime\")!=std::string::npos)\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            auto timeStr = pJson[pMasqueradingVector[\"<<i<<\"]].asString();\\n\";\n                $$<<\"            struct tm stm;\\n\";\n                $$<<\"            memset(&stm,0,sizeof(stm));\\n\";\n                $$<<\"            auto p = strptime(timeStr.c_str(),\\\"%Y-%m-%d %H:%M:%S\\\",&stm);\\n\";\n                $$<<\"            time_t t = mktime(&stm);\\n\";\n                $$<<\"            size_t decimalNum = 0;\\n\";\n                $$<<\"            if(p)\\n\";\n                $$<<\"            {\\n\";\n                $$<<\"                if(*p=='.')\\n\";\n                $$<<\"                {\\n\";\n                $$<<\"                    std::string decimals(p+1,&timeStr[timeStr.length()]);\\n\";\n                $$<<\"                    while(decimals.length()<6)\\n\";\n                $$<<\"                    {\\n\";\n                $$<<\"                        decimals += \\\"0\\\";\\n\";\n                $$<<\"                    }\\n\";\n                $$<<\"                    decimalNum = (size_t)atol(decimals.c_str());\\n\";\n                $$<<\"                }\\n\";\n //               $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(::trantor::Date(946656000000000).after(daysNum*86400));\\n\";\n                $$<<\"                \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(t*1000000+decimalNum);\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"            }\\n\";\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_==\"bytea\" || col.colDatabaseType_.find(\"blob\") != std::string::npos)\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            auto str = pJson[pMasqueradingVector[\"<<i<<\"]].asString();\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<std::vector<char>>(drogon::utils::base64DecodeToVector(str));\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_.find(\"uint\") == 0 || col.colType_ == \"unsigned short\")\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<\"<<col.colType_<<\">((\"<<col.colType_<<\")pJson[pMasqueradingVector[\"<<i<<\"]].asUInt64());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_.find(\"int\") == 0 || col.colType_ == \"short\")\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<\"<<col.colType_<<\">((\"<<col.colType_<<\")pJson[pMasqueradingVector[\"<<i<<\"]].asInt64());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_ == \"float\")\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<float>(pJson[pMasqueradingVector[\"<<i<<\"]].asFloat());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_ == \"double\")\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<double>(pJson[pMasqueradingVector[\"<<i<<\"]].asDouble());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_ == \"bool\")\n            {\n                $$<<\"        if(!pJson[pMasqueradingVector[\"<<i<<\"]].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<bool>(pJson[pMasqueradingVector[\"<<i<<\"]].asBool());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n%>\n        }\n<%c++\n    }\n    %>\n}\n\nvoid [[className]]::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n<%c++\n    for(size_t i=0;i<cols.size();i++)\n    {\n        auto &col = cols[i];\n        if(col.colType_.empty())\n            continue;\n        %>\n    if(pJson.isMember(\"{%col.colName_%}\"))\n    {\n<%c++\n            if(!col.isAutoVal_ && !col.isPrimaryKey_)\n            {\n                $$<<\"        dirtyFlag_[\"<<i<<\"] = true;\\n\";\n            }\n            if(col.colType_ == \"std::string\")\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<std::string>(pJson[\\\"\"<<col.colName_<<\"\\\"].asString());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_==\"date\")\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            auto daysStr = pJson[\\\"\"<<col.colName_<<\"\\\"].asString();\\n\";\n                $$<<\"            struct tm stm;\\n\";\n                $$<<\"            memset(&stm,0,sizeof(stm));\\n\";\n                $$<<\"            strptime(daysStr.c_str(),\\\"%Y-%m-%d\\\",&stm);\\n\";\n                $$<<\"            time_t t = mktime(&stm);\\n\";\n //               $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(::trantor::Date(946656000000000).after(daysNum*86400));\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(t*1000000);\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_.find(\"timestamp\")!=std::string::npos||col.colDatabaseType_.find(\"datetime\")!=std::string::npos)\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            auto timeStr = pJson[\\\"\"<<col.colName_<<\"\\\"].asString();\\n\";\n                $$<<\"            struct tm stm;\\n\";\n                $$<<\"            memset(&stm,0,sizeof(stm));\\n\";\n                $$<<\"            auto p = strptime(timeStr.c_str(),\\\"%Y-%m-%d %H:%M:%S\\\",&stm);\\n\";\n                $$<<\"            time_t t = mktime(&stm);\\n\";\n                $$<<\"            size_t decimalNum = 0;\\n\";\n                $$<<\"            if(p)\\n\";\n                $$<<\"            {\\n\";\n                $$<<\"                if(*p=='.')\\n\";\n                $$<<\"                {\\n\";\n                $$<<\"                    std::string decimals(p+1,&timeStr[timeStr.length()]);\\n\";\n                $$<<\"                    while(decimals.length()<6)\\n\";\n                $$<<\"                    {\\n\";\n                $$<<\"                        decimals += \\\"0\\\";\\n\";\n                $$<<\"                    }\\n\";\n                $$<<\"                    decimalNum = (size_t)atol(decimals.c_str());\\n\";\n                $$<<\"                }\\n\";\n //               $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(::trantor::Date(946656000000000).after(daysNum*86400));\\n\";\n                $$<<\"                \"<<col.colValName_<<\"_=std::make_shared<::trantor::Date>(t*1000000+decimalNum);\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"            }\\n\";\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colDatabaseType_==\"bytea\" || col.colDatabaseType_.find(\"blob\") != std::string::npos)\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            auto str = pJson[\\\"\"<<col.colName_<<\"\\\"].asString();\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<std::vector<char>>(drogon::utils::base64DecodeToVector(str));\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_.find(\"uint\") == 0 || col.colType_ == \"unsigned short\")\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<\"<<col.colType_<<\">((\"<<col.colType_<<\")pJson[\\\"\"<<col.colName_<<\"\\\"].asUInt64());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_.find(\"int\") == 0 || col.colType_ == \"short\")\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<\"<<col.colType_<<\">((\"<<col.colType_<<\")pJson[\\\"\"<<col.colName_<<\"\\\"].asInt64());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_ == \"float\")\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<float>(pJson[\\\"\"<<col.colName_<<\"\\\"].asFloat());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_ == \"double\")\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<double>(pJson[\\\"\"<<col.colName_<<\"\\\"].asDouble());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n            else if(col.colType_ == \"bool\")\n            {\n                $$<<\"        if(!pJson[\\\"\"<<col.colName_<<\"\\\"].isNull())\\n\";\n                $$<<\"        {\\n\";\n                $$<<\"            \"<<col.colValName_<<\"_=std::make_shared<bool>(pJson[\\\"\"<<col.colName_<<\"\\\"].asBool());\\n\";\n                auto convertMethod=std::find_if(convertMethods.begin(),convertMethods.end(),[col](const ConvertMethod& c){ return c.shouldConvert(\"*\", col.colName_); });\n                if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != \"\") {\n                    $$<<\"            \"<< convertMethod->methodBeforeDbWrite() << \"(\" << col.colValName_ << \"_);\\n\";\n                } //endif\n                $$<<\"        }\\n\";\n                $$<<\"    }\\n\";\n                continue;\n            }\n%>\n        }\n<%c++\n    }\n    %>\n}\n\n<%c++\n    for(size_t i=0;i<cols.size();i++)\n    {\n        auto &col = cols[i];\n        if(!col.colType_.empty())\n        {\n            $$<<\"const \"<<col.colType_<<\" &\"<<className<<\"::getValueOf\"<<col.colTypeName_<<\"() const noexcept\\n\";\n            $$<<\"{\\n\";\n            $$<<\"    static const \"<<col.colType_<<\" defaultValue = \"<<col.colType_<<\"();\\n\";\n            $$<<\"    if(\"<<col.colValName_<<\"_)\\n\";\n            $$<<\"        return *\"<<col.colValName_<<\"_;\\n\";\n            $$<<\"    return defaultValue;\\n\";\n            $$<<\"}\\n\";\n            if(col.colType_==\"std::vector<char>\")\n            {\n                $$<<\"std::string \"<<className<<\"::getValueOf\"<<col.colTypeName_<<\"AsString() const noexcept\\n\";\n                $$<<\"{\\n\";\n                $$<<\"    static const std::string defaultValue = std::string();\\n\";\n                $$<<\"    if(\"<<col.colValName_<<\"_)\\n\";\n                $$<<\"        return std::string(\"<<col.colValName_<<\"_->data(),\"<<col.colValName_<<\"_->size());\\n\";\n                $$<<\"    return defaultValue;\\n\";\n                $$<<\"}\\n\";\n            }\n            $$<<\"const std::shared_ptr<\"<<col.colType_<<\"> &\"<<className<<\"::get\"<<col.colTypeName_<<\"() const noexcept\\n\";\n            $$<<\"{\\n\";\n            $$<<\"    return \"<<col.colValName_<<\"_;\\n\";\n            $$<<\"}\\n\";\n\n                $$<<\"void \"<<className<<\"::set\"<<col.colTypeName_<<\"(const \"<<col.colType_<<\" &p\"<<col.colTypeName_<<\") noexcept\\n\";\n                $$<<\"{\\n\";\n                if(col.colDatabaseType_==\"date\")\n                {\n                    $$<<\"    \"<<col.colValName_<<\"_ = std::make_shared<\"<<col.colType_<<\">(p\"<<col.colTypeName_<<\".roundDay());\\n\";\n                }\n                else\n                {\n                    $$<<\"    \"<<col.colValName_<<\"_ = std::make_shared<\"<<col.colType_<<\">(p\"<<col.colTypeName_<<\");\\n\";\n                }\n                $$<<\"    dirtyFlag_[\"<<i<<\"] = true;\\n\";\n                $$<<\"}\\n\";\n\n                if(col.colType_==\"std::string\")\n                {\n                    $$<<\"void \"<<className<<\"::set\"<<col.colTypeName_<<\"(\"<<col.colType_<<\" &&p\"<<col.colTypeName_<<\") noexcept\\n\";\n                    $$<<\"{\\n\";\n                    $$<<\"    \"<<col.colValName_<<\"_ = std::make_shared<\"<<col.colType_<<\">(std::move(p\"<<col.colTypeName_<<\"));\\n\";\n                    $$<<\"    dirtyFlag_[\"<<i<<\"] = true;\\n\";\n                    $$<<\"}\\n\";\n                }\n\n                if(col.colType_==\"std::vector<char>\")\n                {\n                    $$<<\"void \"<<className<<\"::set\"<<col.colTypeName_<<\"(const std::string &p\"<<col.colTypeName_<<\") noexcept\\n\";\n                    $$<<\"{\\n\";\n                    $$<<\"    \"<<col.colValName_<<\"_ = std::make_shared<std::vector<char>>(p\"<<col.colTypeName_<<\".c_str(),p\"<<col.colTypeName_<<\".c_str()+p\"<<col.colTypeName_<<\".length());\\n\";\n                    $$<<\"    dirtyFlag_[\"<<i<<\"] = true;\\n\";\n                    $$<<\"}\\n\";\n                }\n\n                if(!col.notNull_)\n                {\n                    $$<<\"void \"<<className<<\"::set\"<<col.colTypeName_<<\"ToNull() noexcept\\n\";\n                    $$<<\"{\\n\";\n                    $$<<\"    \"<<col.colValName_<<\"_.reset();\\n\";\n                    $$<<\"    dirtyFlag_[\"<<i<<\"] = true;\\n\";\n                    $$<<\"}\\n\";\n                }\n\n            if(col.isPrimaryKey_&&@@.get<int>(\"hasPrimaryKey\")==1)\n            {\n                $$<<\"const typename \"<<className<<\"::PrimaryKeyType & \"<<className<<\"::getPrimaryKey() const\\n\";\n                $$<<\"{\\n\";\n                $$<<\"    assert(\"<<col.colValName_<<\"_);\\n\";\n                $$<<\"    return *\"<<col.colValName_<<\"_;\\n\";\n                $$<<\"}\\n\";\n            }\n        }\n        $$<<\"\\n\";\n    }\n    if(@@.get<std::string>(\"rdbms\")==\"postgresql\"||@@.get<int>(\"hasPrimaryKey\")!=1)\n    {\n        $$<<\"void \"<<className<<\"::updateId(const uint64_t id)\\n\";\n        $$<<\"{\\n\";\n        $$<<\"}\\n\";\n    }\n    else if(@@.get<std::string>(\"rdbms\")==\"mysql\"||@@.get<std::string>(\"rdbms\")==\"sqlite3\")\n    {\n        auto primaryKeyTypeString=@@.get<std::string>(\"primaryKeyType\");\n        $$<<\"void \"<<className<<\"::updateId(const uint64_t id)\\n\";\n        $$<<\"{\\n\";\n        for(auto col:cols)\n        {\n            if(col.isAutoVal_)\n            {\n                if(primaryKeyTypeString!=\"uint64_t\")\n                {\n                    $$<<\"    \"<<col.colValName_<<\"_ = std::make_shared<\"<<primaryKeyTypeString<<\">(static_cast<\"<<primaryKeyTypeString<<\">(id));\\n\";\n                }\n                else\n                {\n                    $$<<\"    \"<<col.colValName_<<\"_ = std::make_shared<uint64_t>(id);\\n\";\n                }\n                break;\n            }\n        }\n        $$<<\"}\\n\";\n    }\n    if(@@.get<int>(\"hasPrimaryKey\")>1)\n    {\n        $$<<\"typename \"<<className<<\"::PrimaryKeyType \"<<className<<\"::getPrimaryKey() const\\n\";\n        $$<<\"{\\n\";\n        $$<<\"    return std::make_tuple(\";\n        int count=0;\n        auto pkNames=@@.get<std::vector<std::string>>(\"primaryKeyValNames\");\n        for(auto pkName:pkNames)\n        {\n            ++count;\n            $$<<\"*\"<<pkName<<\"_\";\n            if(count<@@.get<int>(\"hasPrimaryKey\"))\n                $$<<\",\";\n        }\n        $$<<\");\\n\";\n        $$<<\"}\\n\";\n    }\n%>\n\nconst std::vector<std::string> &[[className]]::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols={\n<%c++for(size_t i=0;i<cols.size();i++){\n    auto col=cols[i];\n    if(!col.isAutoVal_&&!col.colType_.empty())\n    {\n        $$<<\"        \\\"\"<<col.colName_<<\"\\\"\";\n        if(i<(cols.size()-1))\n            $$<<\",\\n\";\n        else\n            $$<<\"\\n\";\n    }\n}%>\n    };\n    return inCols;\n}\n\nvoid [[className]]::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n<%c++for(size_t i = 0; i < cols.size(); ++i){\n    auto &col = cols[i];\n    if(!col.isAutoVal_&&!col.colType_.empty())\n    {\n%>\n    if(dirtyFlag_[{%i%}])\n    {\n        if(get{%col.colTypeName_%}())\n        {\n            binder << getValueOf{%col.colTypeName_%}();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n<%c++\n    }\n}\n%>\n}\n\nconst std::vector<std::string> [[className]]::updateColumns() const\n{\n    std::vector<std::string> ret;\n<%c++\n    for(size_t i=0;i<cols.size();i++)\n    {\n        auto & col=cols[i];\n        if(col.colType_.empty()||col.isAutoVal_)\n            continue;\n%>\n    if(dirtyFlag_[{%i%}])\n    {\n        ret.push_back(getColumnName({%i%}));\n    }\n<%c++\n    }\n%>\n    return ret;\n}\n\nvoid [[className]]::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n<%c++\n    for(size_t i=0;i<cols.size();i++)\n    {\n        auto & col=cols[i];\n        if(col.colType_.empty()||col.isAutoVal_)\n            continue;\n%>\n    if(dirtyFlag_[{%i%}])\n    {\n        if(get{%col.colTypeName_%}())\n        {\n            binder << getValueOf{%col.colTypeName_%}();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n<%c++\n    }\n%>\n}\nJson::Value [[className]]::toJson() const\n{\n    Json::Value ret;\n<%c++for(auto col:cols){%>\n    if(get{%col.colTypeName_%}())\n    {\n<%c++if(col.colDatabaseType_==\"date\"){%>\n        ret[\"{%col.colName_%}\"]=get{%col.colTypeName_%}()->toDbStringLocal();\n<%c++}else if(col.colDatabaseType_.find(\"timestamp\")!=std::string::npos||col.colDatabaseType_.find(\"datetime\")!=std::string::npos){%>\n        ret[\"{%col.colName_%}\"]=get{%col.colTypeName_%}()->toDbStringLocal();\n<%c++}else if(col.colDatabaseType_==\"bytea\"||col.colDatabaseType_.find(\"blob\")!=std::string::npos){%>\n        ret[\"{%col.colName_%}\"]=drogon::utils::base64Encode((const unsigned char *)get{%col.colTypeName_%}()->data(),get{%col.colTypeName_%}()->size());\n<%c++}else if(col.colType_==\"int64_t\"){%>\n        ret[\"{%col.colName_%}\"]=(Json::Int64)getValueOf{%col.colTypeName_%}();\n<%c++}else if(col.colType_==\"uint64_t\"){%>\n        ret[\"{%col.colName_%}\"]=(Json::UInt64)getValueOf{%col.colTypeName_%}();\n<%c++}else{%>\n        ret[\"{%col.colName_%}\"]=getValueOf{%col.colTypeName_%}();\n<%c++}%>\n    }\n    else\n    {\n        ret[\"{%col.colName_%}\"]=Json::Value();\n    }\n<%c++\n}%>\n    return ret;\n}\n\nstd::string [[className]]::toString() const\n{\n    return toJson().toStyledString();\n}\n\nJson::Value [[className]]::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if(pMasqueradingVector.size() == {%cols.size()%})\n    {\n<%c++for(size_t i = 0; i < cols.size(); ++i){\n    auto &col = cols[i];\n    %>\n        if(!pMasqueradingVector[{%i%}].empty())\n        {\n            if(get{%col.colTypeName_%}())\n            {\n<%c++if(col.colDatabaseType_==\"date\"){%>\n                ret[pMasqueradingVector[{%i%}]]=get{%col.colTypeName_%}()->toDbStringLocal();\n<%c++}else if(col.colDatabaseType_.find(\"timestamp\")!=std::string::npos||col.colDatabaseType_.find(\"datetime\")!=std::string::npos){%>\n                ret[pMasqueradingVector[{%i%}]]=get{%col.colTypeName_%}()->toDbStringLocal();\n<%c++}else if(col.colDatabaseType_==\"bytea\"||col.colDatabaseType_.find(\"blob\")!=std::string::npos){%>\n                ret[pMasqueradingVector[{%i%}]]=drogon::utils::base64Encode((const unsigned char *)get{%col.colTypeName_%}()->data(),get{%col.colTypeName_%}()->size());\n<%c++}else if(col.colType_==\"int64_t\"){%>\n                ret[pMasqueradingVector[{%i%}]]=(Json::Int64)getValueOf{%col.colTypeName_%}();\n<%c++}else if(col.colType_==\"uint64_t\"){%>\n                ret[pMasqueradingVector[{%i%}]]=(Json::UInt64)getValueOf{%col.colTypeName_%}();\n<%c++}else{%>\n                ret[pMasqueradingVector[{%i%}]]=getValueOf{%col.colTypeName_%}();\n<%c++}%>\n            }\n            else\n            {\n                ret[pMasqueradingVector[{%i%}]]=Json::Value();\n            }\n        }\n<%c++\n}%>\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n<%c++for(auto col:cols){%>\n    if(get{%col.colTypeName_%}())\n    {\n<%c++if(col.colDatabaseType_==\"date\"){%>\n        ret[\"{%col.colName_%}\"]=get{%col.colTypeName_%}()->toDbStringLocal();\n<%c++}else if(col.colDatabaseType_.find(\"timestamp\")!=std::string::npos||col.colDatabaseType_.find(\"datetime\")!=std::string::npos){%>\n        ret[\"{%col.colName_%}\"]=get{%col.colTypeName_%}()->toDbStringLocal();\n<%c++}else if(col.colDatabaseType_==\"bytea\"||col.colDatabaseType_.find(\"blob\")!=std::string::npos){%>\n        ret[\"{%col.colName_%}\"]=drogon::utils::base64Encode((const unsigned char *)get{%col.colTypeName_%}()->data(),get{%col.colTypeName_%}()->size());\n<%c++}else if(col.colType_==\"int64_t\"){%>\n        ret[\"{%col.colName_%}\"]=(Json::Int64)getValueOf{%col.colTypeName_%}();\n<%c++}else if(col.colType_==\"uint64_t\"){%>\n        ret[\"{%col.colName_%}\"]=(Json::UInt64)getValueOf{%col.colTypeName_%}();\n<%c++}else{%>\n        ret[\"{%col.colName_%}\"]=getValueOf{%col.colTypeName_%}();\n<%c++}%>\n    }\n    else\n    {\n        ret[\"{%col.colName_%}\"]=Json::Value();\n    }\n<%c++\n}%>\n    return ret;\n}\n\nbool [[className]]::validateJsonForCreation(const Json::Value &pJson, std::string &err)\n{\n<%c++\n    for(size_t i=0;i<cols.size();i++)\n    {\n        auto &col = cols[i];\n        if(col.colType_.empty())\n            continue;\n%>\n    if(pJson.isMember(\"{%col.colName_%}\"))\n    {\n        if(!validJsonOfField({%i%}, \"{%col.colName_%}\", pJson[\"{%col.colName_%}\"], err, true))\n            return false;\n    }\n<%c++if(col.notNull_ && !col.isAutoVal_ && !col.hasDefaultVal_){%>\n    else\n    {\n        err=\"The {%col.colName_%} column cannot be null\";\n        return false;\n    }\n<%c++}\n}%>\n    return true;\n}\nbool [[className]]::validateMasqueradedJsonForCreation(const Json::Value &pJson,\n     {%indentStr%}                                     const std::vector<std::string> &pMasqueradingVector,\n     {%indentStr%}                                     std::string &err)\n{\n    if(pMasqueradingVector.size() != {%cols.size()%})\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try {\n<%c++\n    for(size_t i=0;i<cols.size();i++)\n    {\n        auto &col = cols[i];\n        if(col.colType_.empty())\n            continue;\n%>\n      if(!pMasqueradingVector[{%i%}].empty())\n      {\n          if(pJson.isMember(pMasqueradingVector[{%i%}]))\n          {\n              if(!validJsonOfField({%i%}, pMasqueradingVector[{%i%}], pJson[pMasqueradingVector[{%i%}]], err, true))\n                  return false;\n          }\n<%c++if(col.notNull_ && !col.isAutoVal_ && !col.hasDefaultVal_){%>\n        else\n        {\n            err=\"The \" + pMasqueradingVector[{%i%}] + \" column cannot be null\";\n            return false;\n        }\n<%c++}%>\n      }\n<%c++}%>\n    }\n    catch(const Json::LogicError &e)\n    {\n      err = e.what();\n      return false;\n    }\n    return true;\n}\nbool [[className]]::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n<%c++\n    for(size_t i=0;i<cols.size();i++)\n    {\n        auto &col = cols[i];\n        if(col.colType_.empty())\n            continue;\n%>\n    if(pJson.isMember(\"{%col.colName_%}\"))\n    {\n        if(!validJsonOfField({%i%}, \"{%col.colName_%}\", pJson[\"{%col.colName_%}\"], err, false))\n            return false;\n    }\n<%c++\n    if(col.isPrimaryKey_)\n    {\n%>\n    else\n    {\n        err = \"The value of primary key must be set in the json object for update\";\n        return false;\n    }\n<%c++\n    }\n}%>\n    return true;\n}\nbool [[className]]::validateMasqueradedJsonForUpdate(const Json::Value &pJson,\n     {%indentStr%}                                   const std::vector<std::string> &pMasqueradingVector,\n     {%indentStr%}                                   std::string &err)\n{\n    if(pMasqueradingVector.size() != {%cols.size()%})\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try {\n<%c++\n    for(size_t i=0;i<cols.size();i++)\n    {\n        auto &col = cols[i];\n        if(col.colType_.empty())\n            continue;\n%>\n      if(!pMasqueradingVector[{%i%}].empty() && pJson.isMember(pMasqueradingVector[{%i%}]))\n      {\n          if(!validJsonOfField({%i%}, pMasqueradingVector[{%i%}], pJson[pMasqueradingVector[{%i%}]], err, false))\n              return false;\n      }\n<%c++\n    if(col.isPrimaryKey_)\n    {\n%>\n    else\n    {\n        err = \"The value of primary key must be set in the json object for update\";\n        return false;\n    }\n<%c++\n    }\n}%>\n    }\n    catch(const Json::LogicError &e)\n    {\n      err = e.what();\n      return false;\n    }\n    return true;\n}\nbool [[className]]::validJsonOfField(size_t index,\n     {%indentStr%}                   const std::string &fieldName,\n     {%indentStr%}                   const Json::Value &pJson,\n     {%indentStr%}                   std::string &err,\n     {%indentStr%}                   bool isForCreation)\n{\n    switch(index)\n    {\n<%c++\n    for(size_t i=0;i<cols.size();i++)\n    {\n        auto &col = cols[i];\n        if(col.colType_.empty())\n            continue;\n%>\n        case {%i%}:\n<%c++if(col.notNull_){%>\n            if(pJson.isNull())\n            {\n                err=\"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n<%c++}\n            if(col.isAutoVal_)\n            {\n                if(col.isPrimaryKey_)\n                {\n%>\n            if(isForCreation)\n            {\n                err=\"The automatic primary key cannot be set\";\n                return false;\n            }\n<%c++\n                }else\n                {\n%>\n            if(isForCreation)\n            {\n                err=\"The automatic primary key cannot be set\";\n                return false;\n            }\n            else\n            {\n                err=\"The automatic primary key cannot be update\";\n                return false;\n            }\n<%c++\n                }\n            }\nif(!col.notNull_){%>\n            if(pJson.isNull())\n            {\n                return true;\n            }\n<%c++}\n            if(col.colType_ == \"uint64_t\")\n            {\n%>\n            if(!pJson.isUInt64())\n            {\n                err=\"Type error in the \"+fieldName+\" field\";\n                return false;\n            }\n<%c++\n            }\n            else if(col.colType_ == \"int64_t\")\n            {\n%>\n            if(!pJson.isInt64())\n            {\n                err=\"Type error in the \"+fieldName+\" field\";\n                return false;\n            }\n<%c++\n            }\n            else if(col.colType_.find(\"uint\") == 0)\n            {\n%>\n            if(!pJson.isUInt())\n            {\n                err=\"Type error in the \"+fieldName+\" field\";\n                return false;\n            }\n<%c++\n            }\n            else if(col.colType_.find(\"int\") == 0 || col.colType_ == \"short\")\n            {\n%>\n            if(!pJson.isInt())\n            {\n                err=\"Type error in the \"+fieldName+\" field\";\n                return false;\n            }\n<%c++\n            }\n            else if(col.colType_ == \"float\" || col.colType_ == \"double\")\n            {\n%>\n            if(!pJson.isNumeric())\n            {\n                err=\"Type error in the \"+fieldName+\" field\";\n                return false;\n            }\n<%c++\n            }\n            else if(col.colType_ == \"bool\")\n            {\n%>\n            if(!pJson.isBool())\n            {\n                err=\"Type error in the \"+fieldName+\" field\";\n                return false;\n            }\n<%c++\n            }\n            else\n            {\n%>\n            if(!pJson.isString())\n            {\n                err=\"Type error in the \"+fieldName+\" field\";\n                return false;\n            }\n<%c++\n                if(col.colType_ == \"std::string\" && col.colLength_>0)\n                {\n%>\n            if(pJson.isString() && std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>{}\n                .from_bytes(pJson.asCString()).size() > {%col.colLength_%})\n            {\n                err=\"String length exceeds limit for the \" +\n                    fieldName +\n                    \" field (the maximum value is {%col.colLength_%})\";\n                return false;\n            }\n<%c++\n                }\n            }\n%>\n            break;\n<%c++\n}%>\n        default:\n            err=\"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n<%c++\nfor(auto &relationship : relationships)\n{\n    if(relationship.targetKey().empty() || relationship.originalKey().empty())\n        {\n            continue;\n        }\n        auto &name=relationship.targetTableName();\n        auto relationshipClassName=nameTransform(name, true);\n        auto relationshipValName=nameTransform(name, false);\n        auto alias=relationship.targetTableAlias();\n        auto aliasValName=nameTransform(alias, false);\n        if(!alias.empty())\n        {\n            if(alias[0] <= 'z' && alias[0] >= 'a')\n            {\n                alias[0] += ('A' - 'a');\n            }\n        }\n        else\n        {\n            alias = relationshipClassName;\n        }\n\t\tstd::string alind(alias.length(), ' ');\n        if(relationship.type() == Relationship::Type::HasOne)\n        {\n%>\n{%relationshipClassName%} [[className]]::get{%alias%}(const DbClientPtr &clientPtr) const {\n    static const std::string sql = \"select * from {%name%} where {%relationship.targetKey()%} = <%c++\n            if(rdbms==\"postgresql\")\n            {\n                $$<<\"$1\";\n            }\n            else\n            {\n                $$<<\"?\";\n            }%>\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *{%nameTransform(relationship.originalKey(), false)%}_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    if (r.size() == 0)\n    {\n        throw UnexpectedRows(\"0 rows found\");\n    }\n    else if (r.size() > 1)\n    {\n        throw UnexpectedRows(\"Found more than one row\");\n    }\n    return {%relationshipClassName%}(r[0]);\n}\n\nvoid [[className]]::get{%alias%}(const DbClientPtr &clientPtr,\n     {%indentStr%}     {%alind%} const std::function<void({%relationshipClassName%})> &rcb,\n     {%indentStr%}     {%alind%} const ExceptionCallback &ecb) const\n{\n    static const std::string sql = \"select * from {%name%} where {%relationship.targetKey()%} = <%c++\n            if(rdbms==\"postgresql\")\n            {\n                $$<<\"$1\";\n            }\n            else\n            {\n                $$<<\"?\";\n            }%>\";\n    *clientPtr << sql\n               << *{%nameTransform(relationship.originalKey(), false)%}_\n               >> [rcb = std::move(rcb), ecb](const Result &r){\n                    if (r.size() == 0)\n                    {\n                        ecb(UnexpectedRows(\"0 rows found\"));\n                    }\n                    else if (r.size() > 1)\n                    {\n                        ecb(UnexpectedRows(\"Found more than one row\"));\n                    }\n                    else\n                    {\n                        rcb({%relationshipClassName%}(r[0]));\n                    }\n               }\n               >> ecb;\n}\n<%c++\n        }\n        else if(relationship.type() == Relationship::Type::HasMany)\n        {\n%>\nstd::vector<{%relationshipClassName%}> [[className]]::get{%alias%}(const DbClientPtr &clientPtr) const {\n    static const std::string sql = \"select * from {%name%} where {%relationship.targetKey()%} = <%c++\n            if(rdbms==\"postgresql\")\n            {\n                $$<<\"$1\";\n            }\n            else\n            {\n                $$<<\"?\";\n            }%>\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *{%nameTransform(relationship.originalKey(), false)%}_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    std::vector<{%relationshipClassName%}> ret;\n    ret.reserve(r.size());\n    for (auto const &row : r)\n    {\n        ret.emplace_back({%relationshipClassName%}(row));\n    }\n    return ret;\n}\n\nvoid [[className]]::get{%alias%}(const DbClientPtr &clientPtr,\n     {%indentStr%}     {%alind%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,\n     {%indentStr%}     {%alind%} const ExceptionCallback &ecb) const\n{\n    static const std::string sql = \"select * from {%name%} where {%relationship.targetKey()%} = <%c++\n            if(rdbms==\"postgresql\")\n            {\n                $$<<\"$1\";\n            }\n            else\n            {\n                $$<<\"?\";\n            }%>\";\n    *clientPtr << sql\n               << *{%nameTransform(relationship.originalKey(), false)%}_\n               >> [rcb = std::move(rcb)](const Result &r){\n                   std::vector<{%relationshipClassName%}> ret;\n                   ret.reserve(r.size());\n                   for (auto const &row : r)\n                   {\n                       ret.emplace_back({%relationshipClassName%}(row));\n                   }\n                   rcb(ret);\n               }\n               >> ecb;\n}\n<%c++\n        }\n        else if(relationship.type() == Relationship::Type::ManyToMany)\n        {\n            auto &pivotTableName=relationship.pivotTable().tableName();\n            auto pivotTableClassName=nameTransform(pivotTableName, true);\n            auto &pivotOriginalKey=relationship.pivotTable().originalKey();\n            auto &pivotTargetKey=relationship.pivotTable().targetKey();\n%>\nstd::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> [[className]]::get{%alias%}(const DbClientPtr &clientPtr) const {\n    static const std::string sql = \"select * from {%name%},{%pivotTableName%} where {%pivotTableName%}.{%pivotOriginalKey%} = <%c++\n            if(rdbms==\"postgresql\")\n            {\n                $$<<\"$1\";\n            }\n            else\n            {\n                $$<<\"?\";\n            }%> and {%pivotTableName%}.{%pivotTargetKey%} = {%name%}.{%relationship.targetKey()%}\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *{%nameTransform(relationship.originalKey(), false)%}_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> ret;\n    ret.reserve(r.size());\n    for (auto const &row : r)\n    {\n        ret.emplace_back(std::pair<{%relationshipClassName%},{%pivotTableClassName%}>(\n            {%relationshipClassName%}(row),{%pivotTableClassName%}(row,{%relationshipClassName%}::getColumnNumber())));\n    }\n    return ret;\n}\n\nvoid [[className]]::get{%alias%}(const DbClientPtr &clientPtr,\n     {%indentStr%}     {%alind%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,\n     {%indentStr%}     {%alind%} const ExceptionCallback &ecb) const\n{\n    static const std::string sql = \"select * from {%name%},{%pivotTableName%} where {%pivotTableName%}.{%pivotOriginalKey%} = <%c++\n            if(rdbms==\"postgresql\")\n            {\n                $$<<\"$1\";\n            }\n            else\n            {\n                $$<<\"?\";\n            }%> and {%pivotTableName%}.{%pivotTargetKey%} = {%name%}.{%relationship.targetKey()%}\";\n    *clientPtr << sql\n               << *{%nameTransform(relationship.originalKey(), false)%}_\n               >> [rcb = std::move(rcb)](const Result &r){\n                   std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> ret;\n                   ret.reserve(r.size());\n                   for (auto const &row : r)\n                   {\n                       ret.emplace_back(std::pair<{%relationshipClassName%},{%pivotTableClassName%}>(\n                           {%relationshipClassName%}(row),{%pivotTableClassName%}(row,{%relationshipClassName%}::getColumnNumber())));\n                   }\n                   rcb(ret);\n               }\n               >> ecb;\n}\n<%c++\n        }\n    }\n%>\n"
  },
  {
    "path": "drogon_ctl/templates/model_h.csp",
    "content": "<%inc#include \"create_model.h\"\nusing namespace drogon_ctl;\n%>\n/**\n *\n *  [[className]].h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}\n}\nnamespace drogon_model\n{\nnamespace [[dbName]]\n{\n<%c++\nauto &schema=@@.get<std::string>(\"schema\");\n    if(!schema.empty())\n    {\n        $$<<\"namespace \"<<schema<<\"\\n\";\n        $$<<\"{\\n\";\n    }\n    std::vector<std::string> relationshipClassNames;\n    auto &relationships=@@.get<std::vector<Relationship>>(\"relationships\");\n    for(auto &relationship : relationships)\n    {\n        auto &name = relationship.targetTableName();\n        auto relationshipClassName = nameTransform(name, true);\n        relationshipClassNames.push_back(relationshipClassName);\n        if(relationship.type() == Relationship::Type::ManyToMany)\n        {\n            auto &pivotTableName = relationship.pivotTable().tableName();\n            auto pivotTableClassName = nameTransform(pivotTableName, true);\n            relationshipClassNames.push_back(pivotTableClassName);\n        }\n    }\n    std::sort(relationshipClassNames.begin(), relationshipClassNames.end());\n    relationshipClassNames.erase(std::unique(relationshipClassNames.begin(), relationshipClassNames.end()), relationshipClassNames.end());\n    for(std::string &relationshipClassName : relationshipClassNames)\n    {\n%>\nclass {%relationshipClassName%};\n<%c++\n    }\n%>\n\nclass [[className]]\n{\n  public:\n    struct Cols\n    {\n<%c++\nauto cols=@@.get<std::vector<ColumnInfo>>(\"columns\");\n    for(size_t i=0;i<cols.size();i++)\n    {\n        $$<<\"        static const std::string _\"<<cols[i].colName_<<\";\\n\";\n    }\n%>\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n<%c++if(@@.get<int>(\"hasPrimaryKey\")<=1){%>\n    static const std::string primaryKeyName;\n<%c++if(!@@.get<std::string>(\"primaryKeyType\").empty()){%>\n    using PrimaryKeyType = [[primaryKeyType]];\n    const PrimaryKeyType &getPrimaryKey() const;\n<%c++}else{%>\n    using PrimaryKeyType = void;\n    int getPrimaryKey() const { assert(false); return 0; }\n<%c++}%>\n<%c++}else{\n    auto pkTypes=@@.get<std::vector<std::string>>(\"primaryKeyType\");\n    std::string typelist;\n    for(size_t i=0;i<pkTypes.size();i++)\n    {\n        typelist += pkTypes[i];\n        if(i<(pkTypes.size()-1))\n            typelist += \",\";\n    }\n%>\n    static const std::vector<std::string> primaryKeyName;\n    using PrimaryKeyType = std::tuple<{%typelist%}>;//<%c++\n    auto pkName=@@.get<std::vector<std::string>>(\"primaryKeyName\");\n    for(size_t i=0;i<pkName.size();i++)\n    {\n        $$<<pkName[i];\n        if(i<(pkName.size()-1))\n            $$<<\",\";\n    }\n%>\n\n    PrimaryKeyType getPrimaryKey() const;\n<%c++}%>\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column names,\n     * otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select all\n     * columns by an asterisk), please set the offset to -1.\n     */\n    explicit [[className]](const drogon::orm::Row &r, const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit [[className]](const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    [[className]](const Json::Value &pJson, const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    [[className]]() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(const Json::Value &pJson,\n                                 const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson, std::string &err);\n    static bool validateMasqueradedJsonForCreation(const Json::Value &,\n                                                const std::vector<std::string> &pMasqueradingVector,\n                                                    std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson, std::string &err);\n    static bool validateMasqueradedJsonForUpdate(const Json::Value &,\n                                          const std::vector<std::string> &pMasqueradingVector,\n                                          std::string &err);\n    static bool validJsonOfField(size_t index,\n                          const std::string &fieldName,\n                          const Json::Value &pJson,\n                          std::string &err,\n                          bool isForCreation);\n\n<%c++\n    for(const auto &col:cols)\n    {\n        $$<<\"    /**  For column \"<<col.colName_<<\"  */\\n\";\n        if(!col.colType_.empty())\n        {\n            $$<<\"    ///Get the value of the column \"<<col.colName_<<\", returns the default value if the column is null\\n\";\n            $$<<\"    const \"<<col.colType_<<\" &getValueOf\"<<col.colTypeName_<<\"() const noexcept;\\n\";\n            if(col.colType_==\"std::vector<char>\")\n            {\n                $$<<\"    ///Return the column value by std::string with binary data\\n\";\n                $$<<\"    std::string getValueOf\"<<col.colTypeName_<<\"AsString() const noexcept;\\n\";\n            }\n            $$<<\"    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null\\n\";\n            $$<<\"    const std::shared_ptr<\"<<col.colType_<<\"> &get\"<<col.colTypeName_<<\"() const noexcept;\\n\";\n\n            $$<<\"    ///Set the value of the column \"<<col.colName_<<\"\\n\";\n            $$<<\"    void set\"<<col.colTypeName_<<\"(const \"<<col.colType_<<\" &p\"<<col.colTypeName_<<\") noexcept;\\n\";\n                if(col.colType_==\"std::string\")\n                    $$<<\"    void set\"<<col.colTypeName_<<\"(\"<<col.colType_<<\" &&p\"<<col.colTypeName_<<\") noexcept;\\n\";\n                if(col.colType_==\"std::vector<char>\")\n                {\n                    $$<<\"    void set\"<<col.colTypeName_<<\"(const std::string &p\"<<col.colTypeName_<<\") noexcept;\\n\";\n                }\n                if(!col.notNull_)\n                {\n                    $$<<\"    void set\"<<col.colTypeName_<<\"ToNull() noexcept;\\n\";\n                }\n\n        }\n        else\n            $$<<\"    //FIXME!!\"<<\" getValueOf\"<<col.colTypeName_<<\"() const noexcept;\\n\";\n        $$<<\"\\n\";\n    }\n%>\n\n    static size_t getColumnNumber() noexcept {  return {% cols.size() %};  }\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    std::string toString() const;\n    Json::Value toMasqueradedJson(const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n<%c++\n    for(auto &relationship : relationships)\n    {\n        if(relationship.targetKey().empty() || relationship.originalKey().empty())\n        {\n            continue;\n        }\n        auto &name=relationship.targetTableName();\n        auto relationshipClassName=nameTransform(name, true);\n        auto relationshipValName=nameTransform(name, false);\n        auto alias=relationship.targetTableAlias();\n        auto aliasValName=nameTransform(alias, false);\n        if(relationship.type() == Relationship::Type::HasOne)\n        {\n            if(!alias.empty())\n            {\n                if(alias[0] <= 'z' && alias[0] >= 'a')\n                {\n                    alias[0] += ('A' - 'a');\n                }\n                std::string alind(alias.length(), ' ');\n%>\n    {%relationshipClassName%} get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const;\n    void get{%alias%}(const drogon::orm::DbClientPtr &clientPtr,\n            {%alind%} const std::function<void({%relationshipClassName%})> &rcb,\n            {%alind%} const drogon::orm::ExceptionCallback &ecb) const;\n<%c++\n            }\n            else\n            {\n                std::string relationshipClassInde(relationshipClassName.length(), ' ');\n%>\n    {%relationshipClassName%} get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const;\n    void get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr,\n            {%relationshipClassInde%} const std::function<void({%relationshipClassName%})> &rcb,\n            {%relationshipClassInde%} const drogon::orm::ExceptionCallback &ecb) const;\n<%c++\n            }\n        }\n        else if(relationship.type() == Relationship::Type::HasMany)\n        {\n            if(!alias.empty())\n            {\n                if(alias[0] <= 'z' && alias[0] >= 'a')\n                {\n                    alias[0] += ('A' - 'a');\n                }\n                std::string alind(alias.length(), ' ');\n%>\n    std::vector<{%relationshipClassName%}> get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const;\n    void get{%alias%}(const drogon::orm::DbClientPtr &clientPtr,\n            {%alind%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,\n            {%alind%} const drogon::orm::ExceptionCallback &ecb) const;\n<%c++\n            }\n            else\n            {\n                std::string relationshipClassInde(relationshipClassName.length(), ' ');\n%>\n    std::vector<{%relationshipClassName%}> get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const;\n    void get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr,\n            {%relationshipClassInde%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,\n            {%relationshipClassInde%} const drogon::orm::ExceptionCallback &ecb) const;\n<%c++\n            }\n        }\n        else if(relationship.type() == Relationship::Type::ManyToMany)\n        {\n            auto &pivotTableName=relationship.pivotTable().tableName();\n            auto pivotTableClassName=nameTransform(pivotTableName, true);\n            if(!alias.empty())\n            {\n                if(alias[0] <= 'z' && alias[0] >= 'a')\n                {\n                    alias[0] += ('A' - 'a');\n                }\n                std::string alind(alias.length(), ' ');\n%>\n    std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const;\n    void get{%alias%}(const drogon::orm::DbClientPtr &clientPtr,\n            {%alind%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,\n            {%alind%} const drogon::orm::ExceptionCallback &ecb) const;\n<%c++\n            }\n            else\n            {\n                std::string relationshipClassInde(relationshipClassName.length(), ' ');\n%>\n    std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const;\n    void get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr,\n            {%relationshipClassInde%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,\n            {%relationshipClassInde%} const drogon::orm::ExceptionCallback &ecb) const;\n<%c++\n            }\n        }\n    }\n%>\n  private:\n    friend drogon::orm::Mapper<[[className]]>;\n    friend drogon::orm::BaseBuilder<[[className]], true, true>;\n    friend drogon::orm::BaseBuilder<[[className]], true, false>;\n    friend drogon::orm::BaseBuilder<[[className]], false, true>;\n    friend drogon::orm::BaseBuilder<[[className]], false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<[[className]]>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    ///For mysql or sqlite3\n    void updateId(const uint64_t id);\n<%c++\n    for(auto col:cols)\n    {\n        if(!col.colType_.empty())\n            $$<<\"    std::shared_ptr<\"<<col.colType_<<\"> \"<<col.colValName_<<\"_;\\n\";\n    }\n    %>\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[{%cols.size()%}]={ false };\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n<%c++\nauto rdbms=@@.get<std::string>(\"rdbms\");\nif(@@.get<int>(\"hasPrimaryKey\")<=1){\n        if(!@@.get<std::string>(\"primaryKeyType\").empty()){%>\n        static const std::string sql=\"select * from \" + tableName + \" where [[primaryKeyName]] = {%(rdbms==\"postgresql\"?\"$1\":\"?\")%}\";\n<%c++}else{%>\n        static const std::string sql=\"\";\n<%c++}%>\n<%c++}else{\n        auto pkName=@@.get<std::vector<std::string>>(\"primaryKeyName\");\n%>\n        static const std::string sql=\"select * from \" + tableName + \" where <%c++\n        for(size_t i=0;i<pkName.size();i++)\n        {\n            if(rdbms==\"postgresql\")\n            {\n                $$<<pkName[i]<<\" = $\"<<i+1;\n            }\n            else\n            {\n                $$<<pkName[i]<<\" = ?\";\n            }\n            if(i<(pkName.size()-1))\n                $$<<\" and \";\n        }\n        $$<<\"\\\";\\n\";\n    }\n%>\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n<%c++\nif(@@.get<int>(\"hasPrimaryKey\")<=1){\n        if(!@@.get<std::string>(\"primaryKeyType\").empty()){%>\n        static const std::string sql=\"delete from \" + tableName + \" where [[primaryKeyName]] = {%(rdbms==\"postgresql\"?\"$1\":\"?\")%}\";\n<%c++}else{%>\n        static const std::string sql=\"\";\n<%c++}%>\n<%c++}else{\n        auto pkName=@@.get<std::vector<std::string>>(\"primaryKeyName\");\n%>\n        static const std::string sql=\"delete from \" + tableName + \" where <%c++\n        for(size_t i=0;i<pkName.size();i++)\n        {\n            if(rdbms==\"postgresql\")\n            {\n                $$<<pkName[i]<<\" = $\"<<i+1;\n            }\n            else\n            {\n                $$<<pkName[i]<<\" = ?\";\n            }\n            if(i<(pkName.size()-1))\n                $$<<\" and \";\n        }\n        $$<<\"\\\";\\n\";\n    }\n%>\n        return sql;\n    }\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql=\"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n<%c++\n        bool selFlag=false;\n        for(size_t i=0;i<cols.size();i++)\n        {\n            if(cols[i].isAutoVal_)\n            {\n                if(@@.get<std::string>(\"rdbms\")==\"sqlite3\")\n                {\n                    continue;\n                }\n                if(@@.get<int>(\"hasPrimaryKey\")>0)\n                {\n                    selFlag = true;\n                }\n                $$<<\"            sql += \\\"\"<<cols[i].colName_<<\",\\\";\\n\";\n                $$<<\"            ++parametersCount;\\n\";\n                continue;\n            }\n            if(cols[i].colType_.empty())\n                continue;\n            if(cols[i].hasDefaultVal_)\n            {\n                if(@@.get<std::string>(\"rdbms\")!=\"sqlite3\")\n                {\n                    $$<<\"        sql += \\\"\"<<cols[i].colName_<<\",\\\";\\n\";\n                    $$<<\"        ++parametersCount;\\n\";\n                }\n                else\n                {\n%>\n        if(dirtyFlag_[{%i%}])\n        {\n            sql += \"{%cols[i].colName_%},\";\n            ++parametersCount;\n        }\n<%c++\n                }\n                if(@@.get<int>(\"hasPrimaryKey\")>0||@@.get<std::string>(\"rdbms\")==\"postgresql\")\n                {\n%>\n        if(!dirtyFlag_[{%i%}])\n        {\n            needSelection=true;\n        }\n<%c++\n                }\n            }\n            else\n            {\n%>\n        if(dirtyFlag_[{%i%}])\n        {\n            sql += \"{%cols[i].colName_%},\";\n            ++parametersCount;\n        }\n<%c++\n            }\n        }\n        if(selFlag)\n        {\n            $$<<\"        needSelection=true;\\n\";\n        }\n%>\n        if(parametersCount > 0)\n        {\n            sql[sql.length()-1]=')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n<%c++\nif(@@.get<std::string>(\"rdbms\")==\"postgresql\")\n{\n%>\n        int placeholder=1;\n        char placeholderStr[64];\n        size_t n=0;\n<%c++\n}\n        for(size_t i=0;i<cols.size();i++)\n        {\n        if(cols[i].isAutoVal_)\n        {\n            if(@@.get<std::string>(\"rdbms\")!=\"sqlite3\")\n            {\n%>\n        sql +=\"default,\";\n<%c++\n            }\n            continue;\n        }\n        if(cols[i].colType_.empty())\n        continue;\n%>\n        if(dirtyFlag_[{%i%}])\n        {\n<%c++\n                if(@@.get<std::string>(\"rdbms\")==\"postgresql\")\n                {\n%>\n            n = snprintf(placeholderStr,sizeof(placeholderStr),\"$%d,\",placeholder++);\n            sql.append(placeholderStr, n);\n<%c++\n                }else\n                {\n%>\n            sql.append(\"?,\");\n\n<%c++\n                }\n%>\n        }\n<%c++\nif(cols[i].hasDefaultVal_&&@@.get<std::string>(\"rdbms\")!=\"sqlite3\")\n{\n%>\n        else\n        {\n            sql +=\"default,\";\n        }\n<%c++\n}\n        }\n%>\n        if(parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n<%c++\n        if(rdbms==\"postgresql\")\n        {\n%>\n        if(needSelection)\n        {\n            sql.append(\") returning *\");\n        }\n        else\n        {\n            sql.append(1, ')');\n        }\n<%c++\n        }\n        else\n        {\n            $$<<\"        sql.append(1, ')');\\n\";\n        }\n%>\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n<%c++\nif(!schema.empty())\n{\n    $$<<\"} // namespace \"<<schema<<\"\\n\";\n}\n%>\n} // namespace [[dbName]]\n} // namespace drogon_model\n"
  },
  {
    "path": "drogon_ctl/templates/model_json.csp",
    "content": "{\n    //rdbms: server type, postgresql,mysql or sqlite3\n    \"rdbms\": \"postgresql\",\n    //filename: sqlite3 db file name\n    //\"filename\":\"\",\n    //host: server address,localhost by default;\n    \"host\": \"127.0.0.1\",\n    //port: server port, 5432 by default;\n    \"port\": 5432,\n    //dbname: Database name;\n    \"dbname\": \"\",\n    //schema: valid for postgreSQL, \"public\" by default;\n    \"schema\": \"public\",\n    //user: User name\n    \"user\": \"\",\n    //password or passwd: Password\n    \"password\": \"\",\n    //client_encoding: The character set used by drogon_ctl. it is empty string by default which \n    //means use the default character set.\n    //\"client_encoding\": \"\",\n    //table: An array of tables to be modelized. if the array is empty, all revealed tables are modelized.\n    \"tables\": [],\n    //convert: the value can be changed by a function call before it is stored into database or\n    //after it is read from database\n    \"convert\": {\n      \"enabled\": false,\n      \"items\":[{\n          \"table\": \"user\",\n          \"column\": \"password\",\n          \"method\": {\n            //after_db_read: name of the method which is called after reading from database, signature: void([const] std::shared_ptr [&])\n            \"after_db_read\": \"decrypt_password\",\n            //before_db_write: name of the method which is called before writing to database, signature: void([const] std::shared_ptr [&])\n            \"before_db_write\": \"encrypt_password\"\n          },\n          \"includes\": [\n            \"\\\"file_local_search_path.h\\\"\",\"<file_in_global_search_path.h>\"\n          ]\n      }]\n    },\n    \"relationships\": {\n        \"enabled\": false,\n        \"items\": [{\n                \"type\": \"has one\",\n                \"original_table_name\": \"products\",\n                \"original_table_alias\": \"product\",\n                \"original_key\": \"id\",\n                \"target_table_name\": \"skus\",\n                \"target_table_alias\": \"SKU\",\n                \"target_key\": \"product_id\",\n                \"enable_reverse\": true\n            },\n            {\n                \"type\": \"has many\",\n                \"original_table_name\": \"products\",\n                \"original_table_alias\": \"product\",\n                \"original_key\": \"id\",\n                \"target_table_name\": \"reviews\",\n                \"target_table_alias\": \"\",\n                \"target_key\": \"product_id\",\n                \"enable_reverse\": true\n            },\n            {\n                \"type\": \"many to many\",\n                \"original_table_name\": \"products\",\n                \"original_table_alias\": \"\",\n                \"original_key\": \"id\",\n                \"pivot_table\": {\n                    \"table_name\": \"carts_products\",\n                    \"original_key\": \"product_id\",\n                    \"target_key\": \"cart_id\"\n                },\n                \"target_table_name\": \"carts\",\n                \"target_table_alias\": \"\",\n                \"target_key\": \"id\",\n                \"enable_reverse\": true\n            }\n        ]\n    },\n    \"restful_api_controllers\": {\n        \"enabled\": false,\n        // resource_uri: The URI to access the resource, the default value \n        // is '/*' in which the asterisk represents the table name.\n        // If this option is set to a empty string, the URI is composed of the namespaces and the class name.\n        \"resource_uri\": \"/*\",\n        // class_name: \"Restful*Ctrl\" by default, the asterisk represents the table name.\n        // This option can contain namespaces.\n        \"class_name\": \"Restful*Ctrl\",\n        // filters: an array of filter names.\n        \"filters\": [],\n        // db_client: the database client used by the controller. this option must be consistent with\n        // the configuration of the application.\n        \"db_client\": {\n            //name: Name of the client,'default' by default\n            \"name\": \"default\",\n            //is_fast: \n            \"is_fast\": false\n        },\n        // directory: The directory where the controller source files are stored.\n        \"directory\": \"controllers\",\n        // generate_base_only: false by default. Set to true to avoid overwriting custom subclasses.\n        \"generate_base_only\": false\n    }\n}\n"
  },
  {
    "path": "drogon_ctl/templates/plugin_cc.csp",
    "content": "/**\n *\n *  [[filename]].cc\n *\n */\n\n#include \"[[filename]].h\"\n\nusing namespace drogon;\n<%c++auto namespaceStr=@@.get<std::string>(\"namespaceString\");\nif(!namespaceStr.empty())\n$$<<\"using namespace \"<<namespaceStr<<\";\\n\";\n%>\n\nvoid [[className]]::initAndStart(const Json::Value &config)\n{\n    /// Initialize and start the plugin\n}\n\nvoid [[className]]::shutdown() \n{\n    /// Shutdown the plugin\n}\n"
  },
  {
    "path": "drogon_ctl/templates/plugin_h.csp",
    "content": "/**\n *\n *  [[filename]].h\n *\n */\n\n#pragma once\n\n#include <drogon/plugins/Plugin.h>\n<%c++\nauto namespaceVector=@@.get<std::vector<std::string>>(\"namespaceVector\");\nif(namespaceVector.empty())\n$$<<\"\\n\";\nfor(auto &namespaceName:namespaceVector){%>\nnamespace {%namespaceName%}\n\n{\n<%c++}\n$$<<\"\\n\";%>\nclass [[className]] : public drogon::Plugin<[[className]]>\n{\n  public:\n    [[className]]() {}\n    /// This method must be called by drogon to initialize and start the plugin.\n    /// It must be implemented by the user.\n    void initAndStart(const Json::Value &config) override;\n\n    /// This method must be called by drogon to shutdown the plugin.\n    /// It must be implemented by the user.\n    void shutdown() override;\n};\n\n<%c++for(size_t i=0;i<namespaceVector.size();i++){%>\n}\n<%c++}%>"
  },
  {
    "path": "drogon_ctl/templates/restful_controller_base_cc.csp",
    "content": "<%inc#include \"create_model.h\"\nusing namespace drogon_ctl;\n%>\n\n/**\n *\n *  [[fileName]]Base.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl automatically.\n *  Users should implement business logic in the derived class.\n */\n\n#include \"[[fileName]]Base.h\"\n#include <string>\n\n<%c++\nauto tableInfo = @@.get<DrTemplateData>(\"tableInfo\");\nauto modelName = tableInfo.get<std::string>(\"className\");\nbool hasPrimaryKey = (tableInfo.get<int>(\"hasPrimaryKey\")==1);\nauto namespaceVector=@@.get<std::vector<std::string>>(\"namespaceVector\");\nstd::string namespaceStr;\nfor(auto &name:namespaceVector)\n{\n    namespaceStr.append(name);\n    namespaceStr.append(\"::\");\n}\nif(!namespaceStr.empty())\n{\n    namespaceStr.resize(namespaceStr.length()-2);\n    $$<<\"using namespace \"<<namespaceStr<<\";\\n\";\n}\nstd::string indentStr(@@.get<std::string>(\"className\").length(), ' ');\n%>\n<%c++\nif(hasPrimaryKey)\n{%>\nvoid [[className]]Base::getOne(const HttpRequestPtr &req,\n     {%indentStr%}             std::function<void(const HttpResponsePtr &)> &&callback,\n     {%indentStr%}             {%modelName%}::PrimaryKeyType &&id)\n{\n\n    auto dbClientPtr = getDbClient();\n    auto callbackPtr =\n        std::make_shared<std::function<void(const HttpResponsePtr &)>>(\n            std::move(callback));\n    drogon::orm::Mapper<{%modelName%}> mapper(dbClientPtr);\n    mapper.findByPrimaryKey(\n        id,\n        [req, callbackPtr, this]({%modelName%} r) {\n            (*callbackPtr)(HttpResponse::newHttpJsonResponse(makeJson(req, r)));\n        },\n        [callbackPtr](const DrogonDbException &e) {\n            const drogon::orm::UnexpectedRows *s=dynamic_cast<const drogon::orm::UnexpectedRows *>(&e.base());\n            if(s)\n            {\n                auto resp = HttpResponse::newHttpResponse();\n                resp->setStatusCode(k404NotFound);\n                (*callbackPtr)(resp);\n                return;\n            }\n            LOG_ERROR<<e.base().what();\n            Json::Value ret;\n            ret[\"error\"] = \"database error\";\n            auto resp = HttpResponse::newHttpJsonResponse(ret);\n            resp->setStatusCode(k500InternalServerError);\n            (*callbackPtr)(resp);\n        });\n}\n\n\nvoid [[className]]Base::updateOne(const HttpRequestPtr &req,\n     {%indentStr%}                std::function<void(const HttpResponsePtr &)> &&callback,\n     {%indentStr%}                {%modelName%}::PrimaryKeyType &&id)\n{\n    auto jsonPtr=req->jsonObject();\n    if(!jsonPtr)\n    {\n        Json::Value ret;\n        ret[\"error\"]=\"No json object is found in the request\";\n        auto resp= HttpResponse::newHttpJsonResponse(ret);\n        resp->setStatusCode(k400BadRequest);\n        callback(resp);\n        return;\n    }\n    {%modelName%} object;\n    std::string err;\n    if(!doCustomValidations(*jsonPtr, err))\n    {\n        Json::Value ret;\n        ret[\"error\"] = err;\n        auto resp= HttpResponse::newHttpJsonResponse(ret);\n        resp->setStatusCode(k400BadRequest);\n        callback(resp);\n        return;\n    }\n    try\n    {\n        if(isMasquerading())\n        {\n            if(!{%modelName%}::validateMasqueradedJsonForUpdate(*jsonPtr, masqueradingVector(), err))\n            {\n                Json::Value ret;\n                ret[\"error\"] = err;\n                auto resp= HttpResponse::newHttpJsonResponse(ret);\n                resp->setStatusCode(k400BadRequest);\n                callback(resp);\n                return;\n            }\n            object.updateByMasqueradedJson(*jsonPtr, masqueradingVector());\n        }\n        else\n        {\n            if(!{%modelName%}::validateJsonForUpdate(*jsonPtr, err))\n            {\n                Json::Value ret;\n                ret[\"error\"] = err;\n                auto resp= HttpResponse::newHttpJsonResponse(ret);\n                resp->setStatusCode(k400BadRequest);\n                callback(resp);\n                return;\n            }\n            object.updateByJson(*jsonPtr);\n        }\n    }\n    catch(const Json::Exception &e)\n    {\n        LOG_ERROR << e.what();\n        Json::Value ret;\n        ret[\"error\"]=\"Field type error\";\n        auto resp= HttpResponse::newHttpJsonResponse(ret);\n        resp->setStatusCode(k400BadRequest);\n        callback(resp);\n        return;        \n    }\n    if(object.getPrimaryKey() != id)\n    {\n        Json::Value ret;\n        ret[\"error\"]=\"Bad primary key\";\n        auto resp= HttpResponse::newHttpJsonResponse(ret);\n        resp->setStatusCode(k400BadRequest);\n        callback(resp);\n        return;\n    }\n\n    auto dbClientPtr = getDbClient();\n    auto callbackPtr =\n        std::make_shared<std::function<void(const HttpResponsePtr &)>>(\n            std::move(callback));\n    drogon::orm::Mapper<{%modelName%}> mapper(dbClientPtr);\n\n    mapper.update(\n        object,\n        [callbackPtr](const size_t count) \n        {\n            if(count == 1)\n            {\n                auto resp = HttpResponse::newHttpResponse();\n                resp->setStatusCode(k202Accepted);\n                (*callbackPtr)(resp);\n            }\n            else if(count == 0)\n            {\n                Json::Value ret;\n                ret[\"error\"]=\"No resources are updated\";\n                auto resp = HttpResponse::newHttpJsonResponse(ret);\n                resp->setStatusCode(k404NotFound);\n                (*callbackPtr)(resp);\n            }\n            else\n            {\n                LOG_FATAL << \"More than one resource is updated: \" << count;\n                Json::Value ret;\n                ret[\"error\"] = \"database error\";\n                auto resp = HttpResponse::newHttpJsonResponse(ret);\n                resp->setStatusCode(k500InternalServerError);\n                (*callbackPtr)(resp);\n            }\n        },\n        [callbackPtr](const DrogonDbException &e) {\n            LOG_ERROR << e.base().what();\n            Json::Value ret;\n            ret[\"error\"] = \"database error\";\n            auto resp = HttpResponse::newHttpJsonResponse(ret);\n            resp->setStatusCode(k500InternalServerError);\n            (*callbackPtr)(resp);\n        });\n}\n\n\nvoid [[className]]Base::deleteOne(const HttpRequestPtr &req,\n     {%indentStr%}                std::function<void(const HttpResponsePtr &)> &&callback,\n     {%indentStr%}                {%modelName%}::PrimaryKeyType &&id)\n{\n\n    auto dbClientPtr = getDbClient();\n    auto callbackPtr =\n        std::make_shared<std::function<void(const HttpResponsePtr &)>>(\n            std::move(callback));\n    drogon::orm::Mapper<{%modelName%}> mapper(dbClientPtr);\n    mapper.deleteByPrimaryKey(\n        id,\n        [callbackPtr](const size_t count) {\n            if(count == 1)\n            {\n                auto resp = HttpResponse::newHttpResponse();\n                resp->setStatusCode(k204NoContent);\n                (*callbackPtr)(resp);\n            }\n            else if(count == 0)\n            {\n                Json::Value ret;\n                ret[\"error\"] = \"No resources deleted\";\n                auto resp = HttpResponse::newHttpJsonResponse(ret);\n                resp->setStatusCode(k404NotFound);\n                (*callbackPtr)(resp);\n            }\n            else\n            {\n                LOG_FATAL << \"Delete more than one records: \" << count;\n                Json::Value ret;\n                ret[\"error\"] = \"Database error\";\n                auto resp = HttpResponse::newHttpJsonResponse(ret);\n                resp->setStatusCode(k500InternalServerError);\n                (*callbackPtr)(resp);\n            }\n        },\n        [callbackPtr](const DrogonDbException &e) {\n            LOG_ERROR << e.base().what();\n            Json::Value ret;\n            ret[\"error\"] = \"database error\";\n            auto resp = HttpResponse::newHttpJsonResponse(ret);\n            resp->setStatusCode(k500InternalServerError);\n            (*callbackPtr)(resp);\n        });\n}\n<%c++}%>\n\nvoid [[className]]Base::get(const HttpRequestPtr &req,\n     {%indentStr%}          std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    auto dbClientPtr = getDbClient();\n    drogon::orm::Mapper<{%modelName%}> mapper(dbClientPtr);\n    auto &parameters = req->parameters();\n    auto iter = parameters.find(\"sort\");\n    if(iter != parameters.end())\n    {\n        auto sortFields = drogon::utils::splitString(iter->second, \",\");\n        for(auto &field : sortFields)\n        {\n            if(field.empty())\n                continue;\n            if(field[0] == '+')\n            {\n                field = field.substr(1);\n                mapper.orderBy(field, SortOrder::ASC);\n            }\n            else if(field[0] == '-')\n            {\n                field = field.substr(1);\n                mapper.orderBy(field, SortOrder::DESC);\n            }\n            else\n            {\n                mapper.orderBy(field, SortOrder::ASC);\n            }\n        }\n    }\n    iter = parameters.find(\"offset\");\n    if(iter != parameters.end())\n    {\n        try{\n            auto offset = std::stoll(iter->second);\n            mapper.offset(offset);\n        }\n        catch(...)\n        {\n            auto resp = HttpResponse::newHttpResponse();\n            resp->setStatusCode(k400BadRequest);\n            callback(resp);\n            return;\n        }\n    }\n    iter = parameters.find(\"limit\");\n    if(iter != parameters.end())\n    {\n        try{\n            auto limit = std::stoll(iter->second);\n            mapper.limit(limit);\n        }\n        catch(...)\n        {\n            auto resp = HttpResponse::newHttpResponse();\n            resp->setStatusCode(k400BadRequest);\n            callback(resp);\n            return;\n        }\n    }    \n    auto callbackPtr =\n        std::make_shared<std::function<void(const HttpResponsePtr &)>>(\n            std::move(callback));\n    auto jsonPtr = req->jsonObject();\n    if(jsonPtr && jsonPtr->isMember(\"filter\"))\n    {\n        try{\n            auto criteria = makeCriteria((*jsonPtr)[\"filter\"]);\n            mapper.findBy(criteria,\n                [req, callbackPtr, this](const std::vector<{%modelName%}> &v) {\n                    Json::Value ret;\n                    ret.resize(0);\n                    for (auto &obj : v)\n                    {\n                        ret.append(makeJson(req, obj));\n                    }\n                    (*callbackPtr)(HttpResponse::newHttpJsonResponse(ret));\n                },\n                [callbackPtr](const DrogonDbException &e) { \n                    LOG_ERROR << e.base().what();\n                    Json::Value ret;\n                    ret[\"error\"] = \"database error\";\n                    auto resp = HttpResponse::newHttpJsonResponse(ret);\n                    resp->setStatusCode(k500InternalServerError);\n                    (*callbackPtr)(resp);    \n                });\n        }\n        catch(const std::exception &e)\n        {\n            LOG_ERROR << e.what();\n            Json::Value ret;\n            ret[\"error\"] = e.what();\n            auto resp = HttpResponse::newHttpJsonResponse(ret);\n            resp->setStatusCode(k400BadRequest);\n            (*callbackPtr)(resp);\n            return;    \n        }\n    }\n    else\n    {\n        mapper.findAll([req, callbackPtr, this](const std::vector<{%modelName%}> &v) {\n                Json::Value ret;\n                ret.resize(0);\n                for (auto &obj : v)\n                {\n                    ret.append(makeJson(req, obj));\n                }\n                (*callbackPtr)(HttpResponse::newHttpJsonResponse(ret));\n            },\n            [callbackPtr](const DrogonDbException &e) { \n                LOG_ERROR << e.base().what();\n                Json::Value ret;\n                ret[\"error\"] = \"database error\";\n                auto resp = HttpResponse::newHttpJsonResponse(ret);\n                resp->setStatusCode(k500InternalServerError);\n                (*callbackPtr)(resp);    \n            });\n    }\n}\n\nvoid [[className]]Base::create(const HttpRequestPtr &req,\n     {%indentStr%}             std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    auto jsonPtr=req->jsonObject();\n    if(!jsonPtr)\n    {\n        Json::Value ret;\n        ret[\"error\"]=\"No json object is found in the request\";\n        auto resp= HttpResponse::newHttpJsonResponse(ret);\n        resp->setStatusCode(k400BadRequest);\n        callback(resp);\n        return;\n    }\n    std::string err;\n    if(!doCustomValidations(*jsonPtr, err))\n    {\n        Json::Value ret;\n        ret[\"error\"] = err;\n        auto resp= HttpResponse::newHttpJsonResponse(ret);\n        resp->setStatusCode(k400BadRequest);\n        callback(resp);\n        return;\n    }\n    if(isMasquerading())\n    {\n        if(!{%modelName%}::validateMasqueradedJsonForCreation(*jsonPtr, masqueradingVector(), err))\n        {\n            Json::Value ret;\n            ret[\"error\"] = err;\n            auto resp= HttpResponse::newHttpJsonResponse(ret);\n            resp->setStatusCode(k400BadRequest);\n            callback(resp);\n            return;\n        }\n    }\n    else\n    {\n        if(!{%modelName%}::validateJsonForCreation(*jsonPtr, err))\n        {\n            Json::Value ret;\n            ret[\"error\"] = err;\n            auto resp= HttpResponse::newHttpJsonResponse(ret);\n            resp->setStatusCode(k400BadRequest);\n            callback(resp);\n            return;\n        }\n    }   \n    try \n    {\n        {%modelName%} object = \n            (isMasquerading()? \n             {%modelName%}(*jsonPtr, masqueradingVector()) : \n             {%modelName%}(*jsonPtr));\n        auto dbClientPtr = getDbClient();\n        auto callbackPtr =\n            std::make_shared<std::function<void(const HttpResponsePtr &)>>(\n                std::move(callback));\n        drogon::orm::Mapper<{%modelName%}> mapper(dbClientPtr);\n        mapper.insert(\n            object,\n            [req, callbackPtr, this]({%modelName%} newObject){\n                (*callbackPtr)(HttpResponse::newHttpJsonResponse(\n                    makeJson(req, newObject)));\n            },\n            [callbackPtr](const DrogonDbException &e){\n                LOG_ERROR << e.base().what();\n                Json::Value ret;\n                ret[\"error\"] = \"database error\";\n                auto resp = HttpResponse::newHttpJsonResponse(ret);\n                resp->setStatusCode(k500InternalServerError);\n                (*callbackPtr)(resp);   \n            });\n    }\n    catch(const Json::Exception &e)\n    {\n        LOG_ERROR << e.what();\n        Json::Value ret;\n        ret[\"error\"]=\"Field type error\";\n        auto resp= HttpResponse::newHttpJsonResponse(ret);\n        resp->setStatusCode(k400BadRequest);\n        callback(resp);\n        return;        \n    }   \n}\n\n/*\nvoid [[className]]Base::update(const HttpRequestPtr &req,\n     {%indentStr%}             std::function<void(const HttpResponsePtr &)> &&callback)\n{\n\n}*/\n\n[[className]]Base::[[className]]Base()\n    : RestfulController({\n<%c++ \ntableInfo = @@.get<DrTemplateData>(\"tableInfo\");\nconst auto &cols=tableInfo.get<std::vector<ColumnInfo>>(\"columns\");\nfor(size_t i=0; i<cols.size(); ++i)\n{\n    auto &col = cols[i];\n    if(i < (cols.size()-1))\n    {\n%>\n          \"{%col.colName_%}\",\n<%c++\n    }else{\n%>\n          \"{%col.colName_%}\"\n<%c++\n    }\n}\n%>\n      })\n{\n   /**\n    * The items in the vector are aliases of column names in the table.\n    * if one item is set to an empty string, the related column is not sent\n    * to clients.\n    */\n    enableMasquerading({\n<%c++ \nfor(size_t i=0; i<cols.size(); ++i)\n{\n    auto &col = cols[i];\n    if(i < (cols.size()-1))\n    {\n%>\n        \"{%col.colName_%}\", // the alias for the {%col.colName_%} column.\n<%c++\n    }else{\n%>\n        \"{%col.colName_%}\"  // the alias for the {%col.colName_%} column.\n<%c++\n    }\n}\n%>\n    });\n}\n"
  },
  {
    "path": "drogon_ctl/templates/restful_controller_base_h.csp",
    "content": "<%inc#include \"create_model.h\"\nusing namespace drogon_ctl;\n%>\n/**\n *\n *  [[fileName]]Base.h\n *  DO NOT EDIT. This file is generated by drogon_ctl automatically.\n *  Users should implement business logic in the derived class.\n */\n\n#pragma once\n\n#include <drogon/HttpController.h>\n#include <drogon/orm/RestfulController.h>\n\n<%c++\nauto tableInfo = @@.get<DrTemplateData>(\"tableInfo\");\nauto modelName = tableInfo.get<std::string>(\"className\");\n$$<<\"#include \\\"\"<<modelName<<\".h\\\"\\n\";\nbool hasPrimaryKey = (tableInfo.get<int>(\"hasPrimaryKey\")==1);\n$$<<\"using namespace drogon;\\n\";\n$$<<\"using namespace drogon::orm;\\n\";\n\n$$<<\"using namespace drogon_model::\"<<tableInfo.get<std::string>(\"dbName\");\nauto &schema=tableInfo.get<std::string>(\"schema\");\nif(!schema.empty())\n{\n    $$<<\"::\"<<schema<<\";\\n\";\n}\nelse\n{\n    $$<<\";\\n\";\n}\n\nauto namespaceVector=@@.get<std::vector<std::string>>(\"namespaceVector\");\nfor(auto &name:namespaceVector)\n{\n%>\nnamespace {%name%} \n{\n<%c++}%>\n/**\n * @brief this class is created by the drogon_ctl command.\n * this class is a restful API controller for reading and writing the [[tableName]] table.\n */\n\nclass [[className]]Base : public RestfulController\n{\n  public:\n<%c++if(hasPrimaryKey)\n{\n%>\n    void getOne(const HttpRequestPtr &req,\n                std::function<void(const HttpResponsePtr &)> &&callback,\n                {%modelName%}::PrimaryKeyType &&id);\n    void updateOne(const HttpRequestPtr &req,\n                   std::function<void(const HttpResponsePtr &)> &&callback,\n                   {%modelName%}::PrimaryKeyType &&id);\n    void deleteOne(const HttpRequestPtr &req,\n                   std::function<void(const HttpResponsePtr &)> &&callback,\n                   {%modelName%}::PrimaryKeyType &&id);\n<%c++}\n%>\n    void get(const HttpRequestPtr &req,\n             std::function<void(const HttpResponsePtr &)> &&callback);\n    void create(const HttpRequestPtr &req,\n                std::function<void(const HttpResponsePtr &)> &&callback);\n\n\n//  void update(const HttpRequestPtr &req,\n//              std::function<void(const HttpResponsePtr &)> &&callback);\n\n    orm::DbClientPtr getDbClient() \n    {\n        return drogon::app().get{%(@@.get<bool>(\"isFastDbClient\")?\"Fast\":\"\")%}DbClient(dbClientName_);\n    }\n\n  protected:\n    /// Ensure that subclasses inherited from this class are instantiated.\n    [[className]]Base();\n    const std::string dbClientName_{\"[[dbClientName]]\"};\n};\n<%c++ for(size_t i=0;i<namespaceVector.size();++i)\n{\n    $$<<\"}\\n\";\n}\n%>\n"
  },
  {
    "path": "drogon_ctl/templates/restful_controller_cc.csp",
    "content": "/**\n *\n *  [[fileName]].cc\n *  This file is generated by drogon_ctl\n *\n */\n\n#include \"[[fileName]].h\"\n#include <string>\n\n<%c++\nauto namespaceVector=@@.get<std::vector<std::string>>(\"namespaceVector\");\nstd::string namespaceStr;\nfor(auto &name:namespaceVector)\n{\n    namespaceStr.append(name);\n    namespaceStr.append(\"::\");\n}\nif(!namespaceStr.empty())\n{\n    namespaceStr.resize(namespaceStr.length()-2);\n    $$<<\"using namespace \"<<namespaceStr<<\";\\n\";\n}\nstd::string indentStr(@@.get<std::string>(\"className\").length(), ' ');\n%>\n\nvoid [[className]]::getOne(const HttpRequestPtr &req,\n     {%indentStr%}         std::function<void(const HttpResponsePtr &)> &&callback,\n     {%indentStr%}         std::string &&id)\n{\n}\n\nvoid [[className]]::get(const HttpRequestPtr &req,\n     {%indentStr%}      std::function<void(const HttpResponsePtr &)> &&callback)\n{\n}\nvoid [[className]]::create(const HttpRequestPtr &req,\n     {%indentStr%}         std::function<void(const HttpResponsePtr &)> &&callback)\n{\n}\nvoid [[className]]::updateOne(const HttpRequestPtr &req,\n     {%indentStr%}            std::function<void(const HttpResponsePtr &)> &&callback,\n     {%indentStr%}            std::string &&id)\n{\n}\n\n/*\nvoid [[className]]::update(const HttpRequestPtr &req,\n     {%indentStr%}         std::function<void(const HttpResponsePtr &)> &&callback)\n{\n\n}*/\n\nvoid [[className]]::deleteOne(const HttpRequestPtr &req,\n     {%indentStr%}            std::function<void(const HttpResponsePtr &)> &&callback,\n     {%indentStr%}            std::string &&id)\n{\n}\n"
  },
  {
    "path": "drogon_ctl/templates/restful_controller_custom_cc.csp",
    "content": "/**\n *\n *  [[fileName]].cc\n *  This file is generated by drogon_ctl\n *\n */\n\n#include \"[[fileName]].h\"\n#include <string>\n\n<%c++\n\nauto tableInfo = @@.get<DrTemplateData>(\"tableInfo\");\nauto modelName = tableInfo.get<std::string>(\"className\");\nbool hasPrimaryKey = (tableInfo.get<int>(\"hasPrimaryKey\")==1);\n\n\nauto namespaceVector=@@.get<std::vector<std::string>>(\"namespaceVector\");\nstd::string namespaceStr;\nfor(auto &name:namespaceVector)\n{\n    namespaceStr.append(name);\n    namespaceStr.append(\"::\");\n}\nif(!namespaceStr.empty())\n{\n    namespaceStr.resize(namespaceStr.length()-2);\n    $$<<\"using namespace \"<<namespaceStr<<\";\\n\";\n}\nstd::string indentStr(@@.get<std::string>(\"className\").length(), ' ');\n%>\n\n<%c++\nif(hasPrimaryKey)\n{%>\nvoid [[className]]::getOne(const HttpRequestPtr &req,\n     {%indentStr%}         std::function<void(const HttpResponsePtr &)> &&callback,\n     {%indentStr%}         {%modelName%}::PrimaryKeyType &&id)\n{\n    [[className]]Base::getOne(req, std::move(callback), std::move(id));\n}\n\n\nvoid [[className]]::updateOne(const HttpRequestPtr &req,\n     {%indentStr%}            std::function<void(const HttpResponsePtr &)> &&callback,\n     {%indentStr%}            {%modelName%}::PrimaryKeyType &&id)\n{\n    [[className]]Base::updateOne(req, std::move(callback), std::move(id));\n}\n\n\nvoid [[className]]::deleteOne(const HttpRequestPtr &req,\n     {%indentStr%}            std::function<void(const HttpResponsePtr &)> &&callback,\n     {%indentStr%}            {%modelName%}::PrimaryKeyType &&id)\n{\n    [[className]]Base::deleteOne(req, std::move(callback), std::move(id));\n}\n<%c++}%>\n\nvoid [[className]]::get(const HttpRequestPtr &req,\n     {%indentStr%}      std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    [[className]]Base::get(req, std::move(callback));\n}\n\nvoid [[className]]::create(const HttpRequestPtr &req,\n     {%indentStr%}         std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    [[className]]Base::create(req, std::move(callback));\n}\n"
  },
  {
    "path": "drogon_ctl/templates/restful_controller_custom_h.csp",
    "content": "<%inc#include \"create_model.h\"\nusing namespace drogon_ctl;\n%>\n/**\n *\n *  [[fileName]].h\n *  This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n\n#include <drogon/HttpController.h>\n#include \"[[className]]Base.h\"\n\n<%c++\nauto tableInfo = @@.get<DrTemplateData>(\"tableInfo\");\nauto modelName = tableInfo.get<std::string>(\"className\");\n$$<<\"#include \\\"\"<<modelName<<\".h\\\"\\n\";\nbool hasPrimaryKey = (tableInfo.get<int>(\"hasPrimaryKey\")==1);\n$$<<\"using namespace drogon;\\n\";\n\n$$<<\"using namespace drogon_model::\"<<tableInfo.get<std::string>(\"dbName\");\nauto &schema=tableInfo.get<std::string>(\"schema\");\nif(!schema.empty())\n{\n    $$<<\"::\"<<schema<<\";\\n\";\n}\nelse\n{\n    $$<<\";\\n\";\n}\n\nauto namespaceVector=@@.get<std::vector<std::string>>(\"namespaceVector\");\nfor(auto &name:namespaceVector)\n{\n%>\nnamespace {%name%} \n{\n<%c++}%>\n/**\n * @brief this class is created by the drogon_ctl command.\n * this class is a restful API controller for reading and writing the [[tableName]] table.\n */\n\nclass [[className]]: public drogon::HttpController<[[className]]>, public [[className]]Base\n{\n  public:\n    METHOD_LIST_BEGIN\n<%c++\nauto resource=@@.get<std::string>(\"resource\");\nif(resource.empty())\n{    \n    if(hasPrimaryKey)\n    {\n%>\n    METHOD_ADD([[className]]::getOne,\"/{1}\",Get,Options[[filters]]);\n    METHOD_ADD([[className]]::updateOne,\"/{1}\",Put,Options[[filters]]);\n    METHOD_ADD([[className]]::deleteOne,\"/{1}\",Delete,Options[[filters]]);\n<%c++}%>\n    METHOD_ADD([[className]]::get,\"\",Get,Options[[filters]]);\n    METHOD_ADD([[className]]::create,\"\",Post,Options[[filters]]);\n    //METHOD_ADD([[className]]::update,\"\",Put,Options[[filters]]);\n<%c++\n}else\n{\n    if(hasPrimaryKey)\n    {\n%>\n    ADD_METHOD_TO([[className]]::getOne,\"{%resource%}/{1}\",Get,Options[[filters]]);\n    ADD_METHOD_TO([[className]]::updateOne,\"{%resource%}/{1}\",Put,Options[[filters]]);\n    ADD_METHOD_TO([[className]]::deleteOne,\"{%resource%}/{1}\",Delete,Options[[filters]]);\n<%c++}%>\n    ADD_METHOD_TO([[className]]::get,\"{%resource%}\",Get,Options[[filters]]);\n    ADD_METHOD_TO([[className]]::create,\"{%resource%}\",Post,Options[[filters]]);\n    //ADD_METHOD_TO([[className]]::update,\"{%resource%}\",Put,Options[[filters]]);\n<%c++}%>\n    METHOD_LIST_END\n     \n<%c++if(hasPrimaryKey)\n{\n%>\n    void getOne(const HttpRequestPtr &req,\n                std::function<void(const HttpResponsePtr &)> &&callback,\n                {%modelName%}::PrimaryKeyType &&id);\n    void updateOne(const HttpRequestPtr &req,\n                   std::function<void(const HttpResponsePtr &)> &&callback,\n                   {%modelName%}::PrimaryKeyType &&id);\n    void deleteOne(const HttpRequestPtr &req,\n                   std::function<void(const HttpResponsePtr &)> &&callback,\n                   {%modelName%}::PrimaryKeyType &&id);\n<%c++}\n%>\n    void get(const HttpRequestPtr &req,\n             std::function<void(const HttpResponsePtr &)> &&callback);\n    void create(const HttpRequestPtr &req,\n             std::function<void(const HttpResponsePtr &)> &&callback);\n\n};\n<%c++ for(size_t i=0;i<namespaceVector.size();++i)\n{\n    $$<<\"}\\n\";\n}\n%>"
  },
  {
    "path": "drogon_ctl/templates/restful_controller_h.csp",
    "content": "<%inc#include \"create_model.h\"\nusing namespace drogon_ctl;\n%>\n/**\n *\n *  [[fileName]].h\n *  This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n\n#include <drogon/HttpController.h>\n<%c++\n$$<<\"using namespace drogon;\\n\";\nauto namespaceVector=@@.get<std::vector<std::string>>(\"namespaceVector\");\nfor(auto &name:namespaceVector)\n{\n%>\nnamespace {%name%} \n{\n<%c++\n}\n%>\n/**\n * @brief this class is created by the drogon_ctl command ([[ctlCommand]]).\n * this class is a restful API controller.\n */\nclass [[className]]: public drogon::HttpController<[[className]]>\n{\n  public:\n    METHOD_LIST_BEGIN\n    // use METHOD_ADD to add your custom processing function here;\n<%c++\nauto resource=@@.get<std::string>(\"resource\");\nif(resource.empty())\n{\n%>\n    METHOD_ADD([[className]]::getOne,\"/{1}\",Get,Options[[filters]]);\n    METHOD_ADD([[className]]::get,\"\",Get,Options[[filters]]);\n    METHOD_ADD([[className]]::create,\"\",Post,Options[[filters]]);\n    METHOD_ADD([[className]]::updateOne,\"/{1}\",Put,Options[[filters]]);\n    //METHOD_ADD([[className]]::update,\"\",Put,Options[[filters]]);\n    METHOD_ADD([[className]]::deleteOne,\"/{1}\",Delete,Options[[filters]]);\n<%c++\n}else\n{\n%>\n    ADD_METHOD_TO([[className]]::getOne,\"{%resource%}/{1}\",Get,Options[[filters]]);\n    ADD_METHOD_TO([[className]]::updateOne,\"{%resource%}/{1}\",Put,Options[[filters]]);\n    ADD_METHOD_TO([[className]]::deleteOne,\"{%resource%}/{1}\",Delete,Options[[filters]]);\n    ADD_METHOD_TO([[className]]::get,\"{%resource%}\",Get,Options[[filters]]);\n    ADD_METHOD_TO([[className]]::create,\"{%resource%}\",Post,Options[[filters]]);\n    //ADD_METHOD_TO([[className]]::update,\"{%resource%}\",Put,Options[[filters]]);\n<%c++}%>\n    METHOD_LIST_END\n\n    void getOne(const HttpRequestPtr &req,\n                std::function<void(const HttpResponsePtr &)> &&callback,\n                std::string &&id);\n    void updateOne(const HttpRequestPtr &req,\n                std::function<void(const HttpResponsePtr &)> &&callback,\n                std::string &&id);\n    void deleteOne(const HttpRequestPtr &req,\n                   std::function<void(const HttpResponsePtr &)> &&callback,\n                   std::string &&id);\n    void get(const HttpRequestPtr &req,\n             std::function<void(const HttpResponsePtr &)> &&callback);\n    void create(const HttpRequestPtr &req,\n                std::function<void(const HttpResponsePtr &)> &&callback);\n\n//    void update(const HttpRequestPtr &req,\n//                std::function<void(const HttpResponsePtr &)> &&callback);\n\n};\n<%c++ for(size_t i=0;i<namespaceVector.size();++i)\n{\n    $$<<\"}\\n\";\n}\n%>"
  },
  {
    "path": "drogon_ctl/templates/test_cmake.csp",
    "content": "cmake_minimum_required(VERSION 3.5)\nproject([[ProjectName]]_test CXX)\n\nadd_executable(${PROJECT_NAME} test_main.cc)\n\n# ##############################################################################\n# If you include the drogon source code locally in your project, use this method\n# to add drogon \n# target_link_libraries(${PROJECT_NAME} PRIVATE drogon)\n#\n# and comment out the following lines\ntarget_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)\n\nParseAndAddDrogonTests(${PROJECT_NAME})\n"
  },
  {
    "path": "drogon_ctl/templates/test_main.csp",
    "content": "#define DROGON_TEST_MAIN\n#include <drogon/drogon_test.h>\n#include <drogon/drogon.h>\n\nDROGON_TEST(BasicTest)\n{\n    // Add your tests here\n}\n\nint main(int argc, char** argv) \n{\n    using namespace drogon;\n\n    std::promise<void> p1;\n    std::future<void> f1 = p1.get_future();\n\n    // Start the main loop on another thread\n    std::thread thr([&]() {\n        // Queues the promise to be fulfilled after starting the loop\n        app().getLoop()->queueInLoop([&p1]() { p1.set_value(); });\n        app().run();\n    });\n\n    // The future is only satisfied after the event loop started\n    f1.get();\n    int status = test::run(argc, argv);\n\n    // Ask the event loop to shutdown and wait\n    app().getLoop()->queueInLoop([]() { app().quit(); });\n    thr.join();\n    return status;\n}"
  },
  {
    "path": "drogon_ctl/version.cc",
    "content": "/**\n *\n *  version.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"version.h\"\n#include <drogon/config.h>\n#include <drogon/version.h>\n#include <drogon/utils/Utilities.h>\n#include <trantor/net/Resolver.h>\n#include <trantor/utils/Utilities.h>\n#include <iostream>\n\nusing namespace drogon_ctl;\nstatic const char banner[] =\n    R\"(     _\n  __| |_ __ ___   __ _  ___  _ __\n / _` | '__/ _ \\ / _` |/ _ \\| '_ \\\n| (_| | | | (_) | (_| | (_) | | | |\n \\__,_|_|  \\___/ \\__, |\\___/|_| |_|\n                 |___/\n)\";\n\nvoid version::handleCommand(std::vector<std::string> &parameters)\n{\n    const auto tlsBackend = trantor::utils::tlsBackend();\n    const bool tlsSupported = drogon::utils::supportsTls();\n    std::cout << banner << std::endl;\n    std::cout << \"A utility for drogon\" << std::endl;\n    std::cout << \"Version: \" << DROGON_VERSION << std::endl;\n    std::cout << \"Git commit: \" << DROGON_VERSION_SHA1 << std::endl;\n    std::cout << \"Compilation: \\n  Compiler: \" << COMPILER_COMMAND\n              << \"\\n  Compiler ID: \" << COMPILER_ID\n              << \"\\n  Compilation flags: \" << COMPILATION_FLAGS\n              << INCLUDING_DIRS << std::endl;\n    std::cout << \"Libraries: \\n  postgresql: \"\n              << (USE_POSTGRESQL ? \"yes\" : \"no\") << \"  (pipeline mode: \"\n              << (LIBPQ_SUPPORTS_BATCH_MODE ? \"yes)\\n\" : \"no)\\n\")\n              << \"  mariadb: \" << (USE_MYSQL ? \"yes\\n\" : \"no\\n\")\n              << \"  sqlite3: \" << (USE_SQLITE3 ? \"yes\\n\" : \"no\\n\");\n    std::cout << \"  ssl/tls backend: \" << tlsBackend << \"\\n\";\n#ifdef USE_BROTLI\n    std::cout << \"  brotli: yes\\n\";\n#else\n    std::cout << \"  brotli: no\\n\";\n#endif\n#ifdef USE_REDIS\n    std::cout << \"  hiredis: yes\\n\";\n#else\n    std::cout << \"  hiredis: no\\n\";\n#endif\n    std::cout << \"  c-ares: \"\n              << (trantor::Resolver::isCAresUsed() ? \"yes\\n\" : \"no\\n\");\n#ifdef HAS_YAML_CPP\n    std::cout << \"  yaml-cpp: yes\\n\";\n#else\n    std::cout << \"  yaml-cpp: no\\n\";\n#endif\n}\n"
  },
  {
    "path": "drogon_ctl/version.h",
    "content": "/**\n *\n *  version.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include \"CommandHandler.h\"\nusing namespace drogon;\n\nnamespace drogon_ctl\n{\nclass version : public DrObject<version>, public CommandHandler\n{\n  public:\n    void handleCommand(std::vector<std::string> &parameters) override;\n\n    std::string script() override\n    {\n        return \"display version of this tool\";\n    }\n\n    bool isTopCommand() override\n    {\n        return true;\n    }\n\n    version()\n    {\n    }\n};\n}  // namespace drogon_ctl\n"
  },
  {
    "path": "examples/CMakeLists.txt",
    "content": "link_libraries(${PROJECT_NAME})\n\nset(benchmark_sources benchmark/BenchmarkCtrl.cc benchmark/JsonCtrl.cc\n                      benchmark/main.cc)\n\nadd_executable(client client_example/main.cc)\nadd_executable(websocket_client websocket_client/WebSocketClient.cc)\nadd_executable(websocket_server websocket_server/WebSocketServer.cc)\nadd_executable(benchmark ${benchmark_sources})\nadd_executable(helloworld helloworld/main.cc\n                          helloworld/HelloController.cc\n                          helloworld/HelloViewController.cc)\ndrogon_create_views(helloworld\n                    ${CMAKE_CURRENT_SOURCE_DIR}/helloworld\n                    ${CMAKE_CURRENT_BINARY_DIR})\nadd_executable(file_upload file_upload/file_upload.cc)\ndrogon_create_views(file_upload\n                    ${CMAKE_CURRENT_SOURCE_DIR}/file_upload\n                    ${CMAKE_CURRENT_BINARY_DIR})\nadd_executable(login_session login_session/main.cc)\ndrogon_create_views(login_session\n                    ${CMAKE_CURRENT_SOURCE_DIR}/login_session\n                    ${CMAKE_CURRENT_BINARY_DIR})\n\nadd_executable(jsonstore jsonstore/main.cc)\n\nadd_executable(redis_simple redis/main.cc\n                            redis/controllers/Client.cc\n                            redis/controllers/WsClient.cc)\n\nadd_executable(redis_chat redis_chat/main.cc\n                          redis_chat/controllers/Chat.cc)\n\nadd_executable(async_stream async_stream/main.cc\n                            async_stream/RequestStreamExampleCtrl.cc)\n\nadd_executable(cors cors/main.cc)\n\nset(example_targets\n    benchmark\n    client\n    websocket_client\n    websocket_server\n    helloworld\n    file_upload\n    login_session\n    jsonstore\n    redis_simple\n    redis_chat\n    async_stream\n    cors)\n\nforeach(target ${example_targets})\n    set_target_properties(${target} PROPERTIES\n        RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)\nendforeach()\n\ninstall(TARGETS ${example_targets}\n    RUNTIME DESTINATION ${INSTALL_BIN_DIR})\n\n# Add warnings for our example targets--some warnings (such as -Wunused-parameter) only appear\n# when the templated functions are instantiated at their point of use.\nif(NOT ${CMAKE_PLATFORM_NAME} STREQUAL \"Windows\" AND CMAKE_CXX_COMPILER_ID MATCHES GNU)\n    foreach(target ${example_targets})\n        target_compile_options(${target} PRIVATE -Wall -Wextra -Werror)\n    endforeach()\nendif()\n\nset_property(TARGET ${example_targets}\n             PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})\nset_property(TARGET ${example_targets} PROPERTY CXX_STANDARD_REQUIRED ON)\nset_property(TARGET ${example_targets} PROPERTY CXX_EXTENSIONS OFF)\n"
  },
  {
    "path": "examples/README.md",
    "content": "# Drogon Examples\n\nThe following examples can help you understand how to use Drogon:\n\n1. [helloworld](https://github.com/drogonframework/drogon/tree/master/examples/helloworld) - The multiple ways of \"Hello, World!\"\n2. [client_example](https://github.com/drogonframework/drogon/tree/master/examples/client_example/main.cc) - A client example\n3. [websocket_client](https://github.com/drogonframework/drogon/tree/master/examples/websocket_client/WebSocketClient.cc) - An example on how to use the WebSocket client\n4. [login_session](https://github.com/drogonframework/drogon/tree/master/examples/login_session) - How to use the built-in session system to handle login and out\n5. [file_upload](https://github.com/drogonframework/drogon/tree/master/examples/file_upload) - How to handle file uploads in Drogon\n6. [simple_reverse_proxy](https://github.com/drogonframework/drogon/tree/master/examples/simple_reverse_proxy) - An example showing how to use Drogon as a HTTP reverse \nproxy with a simple round robin\n7. [benchmark](https://github.com/drogonframework/drogon/tree/master/examples/benchmark) - Basic benchmark(https://github.com/drogonframework/drogon/wiki/13-Benchmarks) example\n8. [jsonstore](https://github.com/drogonframework/drogon/tree/master/examples/jsonstore) - Implementation of a [jsonstore](https://github.com/bluzi/jsonstore)-like storage service that is concurrent and stores in memory. Serving as a showcase on how to build a minimally useful RESTful APIs in Drogon\n9. [redis](https://github.com/drogonframework/drogon/tree/master/examples/redis) - A simple example of Redis\n10. [websocket_server](https://github.com/drogonframework/drogon/tree/master/examples/websocket_server) - A example websocket chat room server\n11. [redis_cache](https://github.com/drogonframework/drogon/tree/master/examples/redis_cache) - An example for using coroutines of Redis clients\n12. [redis_chat](https://github.com/drogonframework/drogon/tree/master/examples/redis_chat) - A chatroom server built with websocket and Redis pub/sub service\n13. [prometheus_example](https://github.com/drogonframework/drogon/tree/master/examples/prometheus_example) - An example of how to use the Prometheus exporter in Drogon\n14. [cors](https://github.com/drogonframework/drogon/tree/master/examples/cors) - An example demonstrating how to implement CORS (Cross-Origin Resource Sharing) support in Drogon\n\n### [TechEmpower Framework Benchmarks](https://github.com/TechEmpower/FrameworkBenchmarks) test suite\n\nI created a benchmark suite for the `tfb`, see [here](https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/C%2B%2B/drogon) for details.\n\n### Another test suite\n\nI also created a test suite for another web frameworks benchmark repository, see [here](https://github.com/the-benchmarker/web-frameworks/tree/master/cpp/drogon).  In this project, Drogon is used as a sub-module (locally include in the project).\n"
  },
  {
    "path": "examples/async_stream/RequestStreamExampleCtrl.cc",
    "content": "#include <drogon/drogon.h>\n#include <drogon/HttpController.h>\n#include <drogon/HttpRequest.h>\n#include <fstream>\n\nusing namespace drogon;\n\nclass StreamEchoReader : public RequestStreamReader\n{\n  public:\n    StreamEchoReader(ResponseStreamPtr respStream)\n        : respStream_(std::move(respStream))\n    {\n    }\n\n    void onStreamData(const char *data, size_t length) override\n    {\n        LOG_INFO << \"onStreamData[\" << length << \"]\";\n        respStream_->send({data, length});\n    }\n\n    void onStreamFinish(std::exception_ptr ptr) override\n    {\n        if (ptr)\n        {\n            try\n            {\n                std::rethrow_exception(ptr);\n            }\n            catch (const std::exception &e)\n            {\n                LOG_ERROR << \"onStreamError: \" << e.what();\n            }\n        }\n        else\n        {\n            LOG_INFO << \"onStreamFinish\";\n        }\n        respStream_->close();\n    }\n\n  private:\n    ResponseStreamPtr respStream_;\n};\n\nclass RequestStreamExampleCtrl : public HttpController<RequestStreamExampleCtrl>\n{\n  public:\n    METHOD_LIST_BEGIN\n    ADD_METHOD_TO(RequestStreamExampleCtrl::stream_echo, \"/stream_echo\", Post);\n    ADD_METHOD_TO(RequestStreamExampleCtrl::stream_upload,\n                  \"/stream_upload\",\n                  Post);\n    METHOD_LIST_END\n\n    void stream_echo(\n        const HttpRequestPtr &,\n        RequestStreamPtr &&stream,\n        std::function<void(const HttpResponsePtr &)> &&callback) const\n    {\n        auto resp = drogon::HttpResponse::newAsyncStreamResponse(\n            [stream](ResponseStreamPtr respStream) {\n                stream->setStreamReader(\n                    std::make_shared<StreamEchoReader>(std::move(respStream)));\n            });\n        callback(resp);\n    }\n\n    void stream_upload(\n        const HttpRequestPtr &req,\n        RequestStreamPtr &&stream,\n        std::function<void(const HttpResponsePtr &)> &&callback) const\n    {\n        struct Entry\n        {\n            MultipartHeader header;\n            std::string tmpName;\n            std::ofstream file;\n        };\n\n        auto files = std::make_shared<std::vector<Entry>>();\n        auto reader = RequestStreamReader::newMultipartReader(\n            req,\n            [files](MultipartHeader &&header) {\n                LOG_INFO << \"Multipart name: \" << header.name\n                         << \", filename:\" << header.filename\n                         << \", contentType:\" << header.contentType;\n\n                files->push_back({std::move(header)});\n                auto tmpName = drogon::utils::genRandomString(40);\n                if (!files->back().header.filename.empty())\n                {\n                    files->back().tmpName = tmpName;\n                    files->back().file.open(\"uploads/\" + tmpName,\n                                            std::ios::trunc);\n                }\n            },\n            [files](const char *data, size_t length) {\n                if (files->back().tmpName.empty())\n                {\n                    return;\n                }\n                auto &currentFile = files->back().file;\n                if (length == 0)\n                {\n                    LOG_INFO << \"file finish\";\n                    if (currentFile.is_open())\n                    {\n                        currentFile.flush();\n                        currentFile.close();\n                    }\n                    return;\n                }\n                LOG_INFO << \"data[\" << length << \"]: \";\n                if (currentFile.is_open())\n                {\n                    LOG_INFO << \"write file\";\n                    currentFile.write(data, length);\n                }\n                else\n                {\n                    LOG_ERROR << \"file not open\";\n                }\n            },\n            [files, callback = std::move(callback)](std::exception_ptr ex) {\n                if (ex)\n                {\n                    try\n                    {\n                        std::rethrow_exception(std::move(ex));\n                    }\n                    catch (const StreamError &e)\n                    {\n                        LOG_ERROR << \"stream error: \" << e.what();\n                    }\n                    catch (const std::exception &e)\n                    {\n                        LOG_ERROR << \"multipart error: \" << e.what();\n                    }\n                    auto resp = HttpResponse::newHttpResponse();\n                    resp->setStatusCode(k400BadRequest);\n                    resp->setBody(\"error\\n\");\n                    callback(resp);\n                }\n                else\n                {\n                    LOG_INFO << \"stream finish, received \" << files->size()\n                             << \" files\";\n                    Json::Value respJson;\n                    for (const auto &item : *files)\n                    {\n                        if (item.tmpName.empty())\n                            continue;\n                        Json::Value entry;\n                        entry[\"name\"] = item.header.name;\n                        entry[\"filename\"] = item.header.filename;\n                        entry[\"tmpName\"] = item.tmpName;\n                        respJson.append(entry);\n                    }\n                    auto resp = HttpResponse::newHttpJsonResponse(respJson);\n                    callback(resp);\n                }\n            });\n\n        stream->setStreamReader(std::move(reader));\n    }\n};\n"
  },
  {
    "path": "examples/async_stream/main.cc",
    "content": "#include <drogon/drogon.h>\n#include <chrono>\n#include <functional>\n#include <mutex>\n#include <unordered_map>\n#include <trantor/utils/Logger.h>\n#include <trantor/net/callbacks.h>\n#include <trantor/net/TcpConnection.h>\n\nusing namespace drogon;\nusing namespace std::chrono_literals;\n\nstd::mutex mutex;\nstd::unordered_map<trantor::TcpConnectionPtr, std::function<void()>>\n    connMapping;\n\nint main()\n{\n    app().registerHandler(\n        \"/stream\",\n        [](const HttpRequestPtr &req,\n           std::function<void(const HttpResponsePtr &)> &&callback) {\n            const auto &weakConnPtr = req->getConnectionPtr();\n            if (auto connPtr = weakConnPtr.lock())\n            {\n                std::lock_guard lk(mutex);\n                connMapping.emplace(std::move(connPtr), [] {\n                    LOG_INFO << \"call stop or other options!!!!\";\n                });\n            }\n            auto resp = drogon::HttpResponse::newAsyncStreamResponse(\n                [](drogon::ResponseStreamPtr stream) {\n                    std::thread([stream =\n                                     std::shared_ptr<drogon::ResponseStream>{\n                                         std::move(stream)}]() mutable {\n                        std::cout << std::boolalpha << stream->send(\"hello \")\n                                  << std::endl;\n                        std::this_thread::sleep_for(std::chrono::seconds(2));\n                        std::cout << std::boolalpha << stream->send(\"world\");\n                        std::this_thread::sleep_for(std::chrono::seconds(2));\n                        stream->close();\n                    }).detach();\n                });\n            resp->setContentTypeCodeAndCustomString(\n                ContentType::CT_APPLICATION_JSON, \"application/json\");\n            callback(resp);\n        });\n\n    // Example: register a stream-mode function handler\n    app().registerHandler(\n        \"/stream_req\",\n        [](const HttpRequestPtr &req,\n           RequestStreamPtr &&stream,\n           std::function<void(const HttpResponsePtr &)> &&callback) {\n            if (!stream)\n            {\n                LOG_INFO << \"stream mode is not enabled\";\n                auto resp = HttpResponse::newHttpResponse();\n                resp->setStatusCode(k400BadRequest);\n                resp->setBody(\"no stream\");\n                callback(resp);\n                return;\n            }\n\n            auto reader = RequestStreamReader::newReader(\n                [](const char *data, size_t length) {\n                    LOG_INFO << \"piece[\" << length\n                             << \"]: \" << std::string_view{data, length};\n                },\n                [callback = std::move(callback)](std::exception_ptr ex) {\n                    auto resp = HttpResponse::newHttpResponse();\n                    if (ex)\n                    {\n                        try\n                        {\n                            std::rethrow_exception(std::move(ex));\n                        }\n                        catch (const std::exception &e)\n                        {\n                            LOG_ERROR << \"stream error: \" << e.what();\n                        }\n                        resp->setStatusCode(k400BadRequest);\n                        resp->setBody(\"error\\n\");\n                        callback(resp);\n                    }\n                    else\n                    {\n                        LOG_INFO << \"stream finish\";\n                        resp->setBody(\"success\\n\");\n                        callback(resp);\n                    }\n                });\n\n            stream->setStreamReader(std::move(reader));\n        },\n        {Post});\n\n    LOG_INFO << \"Server running on 127.0.0.1:8848\";\n    app().enableRequestStream();  // This is for request stream.\n    app().setConnectionCallback([](const trantor::TcpConnectionPtr &conn) {\n        if (conn->disconnected())\n        {\n            std::lock_guard lk(mutex);\n            if (auto it = connMapping.find(conn); it != connMapping.end())\n            {\n                LOG_INFO << \"disconnect\";\n                connMapping[conn]();\n                connMapping.erase(conn);\n            }\n        }\n    });\n    app().addListener(\"127.0.0.1\", 8848).run();\n}\n"
  },
  {
    "path": "examples/benchmark/BenchmarkCtrl.cc",
    "content": "#include \"BenchmarkCtrl.h\"\n\nvoid BenchmarkCtrl::asyncHandleHttpRequest(\n    const HttpRequestPtr &,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    // write your application logic here\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setBody(\"<p>Hello, world!</p>\");\n    resp->setExpiredTime(0);\n    callback(resp);\n}\n"
  },
  {
    "path": "examples/benchmark/BenchmarkCtrl.h",
    "content": "#pragma once\n#include <drogon/HttpSimpleController.h>\nusing namespace drogon;\n\nclass BenchmarkCtrl : public drogon::HttpSimpleController<BenchmarkCtrl>\n{\n  public:\n    void asyncHandleHttpRequest(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) override;\n    PATH_LIST_BEGIN\n    PATH_ADD(\"/benchmark\", Get);\n    PATH_LIST_END\n};\n"
  },
  {
    "path": "examples/benchmark/JsonCtrl.cc",
    "content": "#include \"JsonCtrl.h\"\n\nvoid JsonCtrl::asyncHandleHttpRequest(\n    const HttpRequestPtr &,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    Json::Value ret;\n    ret[\"message\"] = \"Hello, World!\";\n    auto resp = HttpResponse::newHttpJsonResponse(std::move(ret));\n    callback(resp);\n}\n"
  },
  {
    "path": "examples/benchmark/JsonCtrl.h",
    "content": "#pragma once\n#include <drogon/HttpSimpleController.h>\nusing namespace drogon;\n\nclass JsonCtrl : public drogon::HttpSimpleController<JsonCtrl>\n{\n  public:\n    void asyncHandleHttpRequest(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) override;\n    PATH_LIST_BEGIN\n    // list path definitions here;\n    PATH_ADD(\"/json\", Get);\n    PATH_LIST_END\n};\n"
  },
  {
    "path": "examples/benchmark/main.cc",
    "content": "#include <drogon/drogon.h>\n\nusing namespace drogon;\n\nint main()\n{\n    app()\n        .setLogPath(\"./\")\n        .setLogLevel(trantor::Logger::kWarn)\n        .addListener(\"0.0.0.0\", 7770)\n        .setThreadNum(0)\n        .registerSyncAdvice([](const HttpRequestPtr &req) -> HttpResponsePtr {\n            const auto &path = req->path();\n            if (path.length() == 1 && path[0] == '/')\n            {\n                auto response = HttpResponse::newHttpResponse();\n                response->setBody(\"<p>Hello, world!</p>\");\n                return response;\n            }\n            return nullptr;\n        })\n        .run();\n}\n"
  },
  {
    "path": "examples/client_example/main.cc",
    "content": "#include <drogon/drogon.h>\n\n#include <future>\n#include <iostream>\n\n#ifdef __linux__\n#include <sys/socket.h>\n#include <netinet/tcp.h>\n#endif\n\nusing namespace drogon;\n\nint nth_resp = 0;\n\nint main()\n{\n    trantor::Logger::setLogLevel(trantor::Logger::kTrace);\n    {\n        auto client = HttpClient::newHttpClient(\"http://www.baidu.com\");\n        client->setSockOptCallback([](int fd) {\n            std::cout << \"setSockOptCallback:\" << fd << std::endl;\n#ifdef __linux__\n            int optval = 10;\n            ::setsockopt(fd,\n                         SOL_TCP,\n                         TCP_KEEPCNT,\n                         &optval,\n                         static_cast<socklen_t>(sizeof optval));\n            ::setsockopt(fd,\n                         SOL_TCP,\n                         TCP_KEEPIDLE,\n                         &optval,\n                         static_cast<socklen_t>(sizeof optval));\n            ::setsockopt(fd,\n                         SOL_TCP,\n                         TCP_KEEPINTVL,\n                         &optval,\n                         static_cast<socklen_t>(sizeof optval));\n#endif\n        });\n\n        auto req = HttpRequest::newHttpRequest();\n        req->setMethod(drogon::Get);\n        req->setPath(\"/s\");\n        req->setParameter(\"wd\", \"wx\");\n        req->setParameter(\"oq\", \"wx\");\n\n        for (int i = 0; i < 10; ++i)\n        {\n            client->sendRequest(\n                req, [](ReqResult result, const HttpResponsePtr &response) {\n                    if (result != ReqResult::Ok)\n                    {\n                        std::cout\n                            << \"error while sending request to server! result: \"\n                            << result << std::endl;\n                        return;\n                    }\n\n                    std::cout << \"receive response!\" << std::endl;\n                    // auto headers=response.\n                    ++nth_resp;\n                    std::cout << response->getBody() << std::endl;\n                    auto cookies = response->cookies();\n                    for (auto const &cookie : cookies)\n                    {\n                        std::cout << cookie.first << \"=\"\n                                  << cookie.second.value()\n                                  << \":domain=\" << cookie.second.domain()\n                                  << std::endl;\n                    }\n                    std::cout << \"count=\" << nth_resp << std::endl;\n                });\n        }\n        std::cout << \"requestsBufferSize:\" << client->requestsBufferSize()\n                  << std::endl;\n    }\n\n    app().run();\n}\n"
  },
  {
    "path": "examples/cors/main.cc",
    "content": "#include <drogon/HttpAppFramework.h>\n#include <drogon/HttpResponse.h>\n#include <drogon/drogon.h>\n#include \"trantor/utils/Logger.h\"\n\nusing namespace drogon;\n\n/// Configure Cross-Origin Resource Sharing (CORS) support.\n///\n///  This function registers both synchronous pre-processing advice for handling\n///  OPTIONS preflight requests and post-handling advice to inject CORS headers\n///  into all responses dynamically based on the incoming request headers.\nvoid setupCors()\n{\n    // Register sync advice to handle CORS preflight (OPTIONS) requests\n    drogon::app().registerSyncAdvice([](const drogon::HttpRequestPtr &req)\n                                         -> drogon::HttpResponsePtr {\n        if (req->method() == drogon::HttpMethod::Options)\n        {\n            auto resp = drogon::HttpResponse::newHttpResponse();\n\n            // Set Access-Control-Allow-Origin header based on the Origin\n            // request header\n            const auto &origin = req->getHeader(\"Origin\");\n            if (!origin.empty())\n            {\n                resp->addHeader(\"Access-Control-Allow-Origin\", origin);\n            }\n\n            // Set Access-Control-Allow-Methods based on the requested method\n            const auto &requestMethod =\n                req->getHeader(\"Access-Control-Request-Method\");\n            if (!requestMethod.empty())\n            {\n                resp->addHeader(\"Access-Control-Allow-Methods\", requestMethod);\n            }\n\n            // Allow credentials to be included in cross-origin requests\n            resp->addHeader(\"Access-Control-Allow-Credentials\", \"true\");\n\n            // Set allowed headers from the Access-Control-Request-Headers\n            // header\n            const auto &requestHeaders =\n                req->getHeader(\"Access-Control-Request-Headers\");\n            if (!requestHeaders.empty())\n            {\n                resp->addHeader(\"Access-Control-Allow-Headers\", requestHeaders);\n            }\n\n            return std::move(resp);\n        }\n        return {};\n    });\n\n    // Register post-handling advice to add CORS headers to all responses\n    drogon::app().registerPostHandlingAdvice(\n        [](const drogon::HttpRequestPtr &req,\n           const drogon::HttpResponsePtr &resp) -> void {\n            // Set Access-Control-Allow-Origin based on the Origin request\n            // header\n            const auto &origin = req->getHeader(\"Origin\");\n            if (!origin.empty())\n            {\n                resp->addHeader(\"Access-Control-Allow-Origin\", origin);\n            }\n\n            // Reflect the requested Access-Control-Request-Method back in the\n            // response\n            const auto &requestMethod =\n                req->getHeader(\"Access-Control-Request-Method\");\n            if (!requestMethod.empty())\n            {\n                resp->addHeader(\"Access-Control-Allow-Methods\", requestMethod);\n            }\n\n            // Allow credentials to be included in cross-origin requests\n            resp->addHeader(\"Access-Control-Allow-Credentials\", \"true\");\n\n            // Reflect the requested Access-Control-Request-Headers back\n            const auto &requestHeaders =\n                req->getHeader(\"Access-Control-Request-Headers\");\n            if (!requestHeaders.empty())\n            {\n                resp->addHeader(\"Access-Control-Allow-Headers\", requestHeaders);\n            }\n        });\n}\n\n/**\n * Main function to start the Drogon application with CORS-enabled routes.\n * This example includes:\n * - A simple GET endpoint `/hello` that returns a greeting message.\n * - A POST endpoint `/echo` that echoes back the request body.\n *  You can test with curl to test the CORS support:\n *\n```\n    curl -i -X OPTIONS http://localhost:8000/echo \\\n        -H \"Origin: http://localhost:3000\" \\\n        -H \"Access-Control-Request-Method: POST\" \\\n        -H \"Access-Control-Request-Headers: Content-Type\"\n```\nor\n```\n    curl -i -X POST http://localhost:8000/echo \\\n        -H \"Origin: http://localhost:3000\" \\\n        -H \"Content-Type: application/json\" \\\n        -d '{\"key\":\"value\"}'\n```\n */\nint main()\n{\n    // Listen on port 8000 for all interfaces\n    app().addListener(\"0.0.0.0\", 8000);\n\n    // Setup CORS support\n    setupCors();\n\n    // Register /hello route for GET and OPTIONS methods\n    app().registerHandler(\n        \"/hello\",\n        [](const HttpRequestPtr &req,\n           std::function<void(const HttpResponsePtr &)> &&callback) {\n            auto resp = HttpResponse::newHttpResponse();\n            resp->setBody(\"Hello from Drogon!\");\n\n            // Log client IP address\n            LOG_INFO << \"Request to /hello from \" << req->getPeerAddr().toIp();\n\n            callback(resp);\n        },\n        {Get, Options});\n\n    // Register /echo route for POST and OPTIONS methods\n    app().registerHandler(\n        \"/echo\",\n        [](const HttpRequestPtr &req,\n           std::function<void(const HttpResponsePtr &)> &&callback) {\n            auto resp = HttpResponse::newHttpResponse();\n            resp->setBody(std::string(\"Echo: \").append(req->getBody()));\n\n            // Log client IP and request body\n            LOG_INFO << \"Request to /echo from \" << req->getPeerAddr().toIp();\n            LOG_INFO << \"Echo content: \" << req->getBody();\n\n            callback(resp);\n        },\n        {Post, Options});\n\n    // Start the application main loop\n    app().run();\n\n    return 0;\n}\n"
  },
  {
    "path": "examples/file_upload/FileUpload.csp",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>File upload</title>\n    <script type=\"text/javascript\">\n        var xhr;\n        //File uploading method\n        function UpladFile() {\n            var fileObj = document.getElementById(\"file\").files[0]; // js get file object\n            var url =  \"/upload_endpoint\"; \n\n            var form = new FormData(); // FormData object\n            form.append(\"file\", fileObj); // File object\n\n            xhr = new XMLHttpRequest();  // XMLHttpRequest object\n            xhr.open(\"post\", url, true); //post\n            xhr.onload = uploadComplete; \n            xhr.onerror =  uploadFailed; \n\n            xhr.upload.onprogress = progressFunction;\n            xhr.upload.onloadstart = function(){\n                ot = new Date().getTime();\n                oloaded = 0;\n            };\n\n            xhr.send(form); \n        }\n\n        function uploadComplete(evt) {\n            var data = evt.target.responseText;\n            alert(\"File has been uploaded.\\n\" + data);\n        }\n        \n        function uploadFailed(evt) {\n            alert(\"Upload failed!\");\n        }\n        \n        function cancleUploadFile(){\n            xhr.abort();\n        }\n\n        function progressFunction(evt) {\n            var progressBar = document.getElementById(\"progressBar\");\n            var percentageDiv = document.getElementById(\"percentage\");\n            if (evt.lengthComputable) {//\n                progressBar.max = evt.total;\n                progressBar.value = evt.loaded;\n                percentageDiv.innerHTML = Math.round(evt.loaded / evt.total * 100) + \"%\";\n            }\n            var time = document.getElementById(\"time\");\n            var nt = new Date().getTime();\n            var pertime = (nt-ot)/1000; \n            ot = new Date().getTime(); \n            var perload = evt.loaded - oloaded; \n            oloaded = evt.loaded;\n            var speed = perload/pertime;\n            var bspeed = speed;\n            var units = 'b/s';\n            if(speed/1024>1){\n                speed = speed/1024;\n                units = 'k/s';\n            }\n            if(speed/1024>1){\n                speed = speed/1024;\n                units = 'M/s';\n            }\n            speed = speed.toFixed(1);\n            var resttime = ((evt.total-evt.loaded)/bspeed).toFixed(1);\n            time.innerHTML = ',Speed: '+speed+units+', the remaining time: '+resttime+'s';\n            if(bspeed==0) time.innerHTML = 'Upload cancelled';\n        }\n    </script>\n</head>\n<body>\n<progress id=\"progressBar\" value=\"0\" max=\"100\" style=\"width: 300px;\"></progress>\n<span id=\"percentage\"></span><span id=\"time\"></span>\n<br /><br />\n<input type=\"file\" id=\"file\" name=\"myfile\" />\n<input type=\"button\" onclick=\"UpladFile()\" value=\"Upload\" />\n<input type=\"button\" onclick=\"cancleUploadFile()\" value=\"Cancel\" />\n</body>\n</html>\n"
  },
  {
    "path": "examples/file_upload/file_upload.cc",
    "content": "#include <drogon/drogon.h>\nusing namespace drogon;\n\nint main()\n{\n    app().registerHandler(\n        \"/\",\n        [](const HttpRequestPtr &,\n           std::function<void(const HttpResponsePtr &)> &&callback) {\n            auto resp = HttpResponse::newHttpViewResponse(\"FileUpload\");\n            callback(resp);\n        });\n\n    app().registerHandler(\n        \"/upload_endpoint\",\n        [](const HttpRequestPtr &req,\n           std::function<void(const HttpResponsePtr &)> &&callback) {\n            MultiPartParser fileUpload;\n            if (fileUpload.parse(req) != 0 || fileUpload.getFiles().size() != 1)\n            {\n                auto resp = HttpResponse::newHttpResponse();\n                resp->setBody(\"Must only be one file\");\n                resp->setStatusCode(k403Forbidden);\n                callback(resp);\n                return;\n            }\n\n            auto &file = fileUpload.getFiles()[0];\n            auto md5 = file.getMd5();\n            auto resp = HttpResponse::newHttpResponse();\n            resp->setBody(\n                \"The server has calculated the file's MD5 hash to be \" + md5);\n            file.save();\n            LOG_INFO << \"The uploaded file has been saved to the ./uploads \"\n                        \"directory\";\n            callback(resp);\n        },\n        {Post});\n\n    LOG_INFO << \"Server running on 127.0.0.1:8848\";\n    app()\n        .setClientMaxBodySize(20 * 2000 * 2000)\n        .setUploadPath(\"./uploads\")\n        .addListener(\"127.0.0.1\", 8848)\n        .run();\n}\n"
  },
  {
    "path": "examples/helloworld/HelloController.cc",
    "content": "#include <drogon/HttpController.h>\n\nusing namespace drogon;\n\n// HttpControllers are automatically added to Drogon upon Drogon initializing.\nclass SayHello : public HttpController<SayHello>\n{\n  public:\n    METHOD_LIST_BEGIN\n    // Drogon automatically appends the namespace and name of the controller to\n    // the handlers of the controller. In this example, although we are adding\n    // a handler to /. But because it is a part of the SayHello controller. It\n    // ends up in path /SayHello/ (IMPORTANT! It is /SayHello/ not /SayHello\n    // as they are different paths).\n    METHOD_ADD(SayHello::genericHello, \"/\", Get);\n    // Same for /hello. It ends up at /SayHello/hello\n    METHOD_ADD(SayHello::personalizedHello, \"/hello\", Get);\n    METHOD_LIST_END\n\n  protected:\n    void genericHello(const HttpRequestPtr &,\n                      std::function<void(const HttpResponsePtr &)> &&callback)\n    {\n        auto resp = HttpResponse::newHttpResponse();\n        resp->setBody(\n            \"Hello, this is a generic hello message from the SayHello \"\n            \"controller\");\n        callback(resp);\n    }\n\n    void personalizedHello(\n        const HttpRequestPtr &,\n        std::function<void(const HttpResponsePtr &)> &&callback)\n    {\n        auto resp = HttpResponse::newHttpResponse();\n        resp->setBody(\n            \"Hi there, this is another hello from the SayHello Controller\");\n        callback(resp);\n    }\n};\n"
  },
  {
    "path": "examples/helloworld/HelloView.csp",
    "content": "<!DOCTYPE html>\n<html>\n<%c++\n    auto name=@@.get<std::string>(\"name\");\n\tbool nameIsEmpty = name == \"\";\n\tif (nameIsEmpty)\n\t\tname = \"anonymous\";\n\tauto message = \"Hello, \" + name + \" from a CSP template\";\n%>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>[[ name ]]</title>\n</head>\n<body>\n    <%c++ $$<<message; %>\n\t<%c++\n\tif (nameIsEmpty)\n\t{\n\t\t$$ << \"<br>\"\n\t\t\t<< \"You can revisit the same page and append ?name=<i>your_name</i> to change the name\";\n\t}\n\t%>\n</body>\n</html>\n"
  },
  {
    "path": "examples/helloworld/HelloViewController.cc",
    "content": "#include <drogon/HttpSimpleController.h>\n#include <drogon/HttpResponse.h>\n\nusing namespace drogon;\n\n// HttpSimpleController does not allow registration of multiple handlers.\n// Instead, it has one handler - asyncHandleHttpRequest. The\n// HttpSimpleController is a lightweight class designed to handle really simple\n// cases.\nclass HelloViewController : public HttpSimpleController<HelloViewController>\n{\n  public:\n    PATH_LIST_BEGIN\n    // Also unlike HttpContoller, HttpSimpleController does not automatically\n    // prepend the namespace and class name to the path. Thus the path of this\n    // controller is at \"/view\".\n    PATH_ADD(\"/view\");\n    PATH_LIST_END\n\n    void asyncHandleHttpRequest(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) override\n    {\n        HttpViewData data;\n        data[\"name\"] = req->getParameter(\"name\");\n        auto resp = HttpResponse::newHttpViewResponse(\"HelloView\", data);\n        callback(resp);\n    }\n};\n"
  },
  {
    "path": "examples/helloworld/main.cc",
    "content": "#include <trantor/utils/Logger.h>\n#ifdef _WIN32\n#include <ws2tcpip.h>\n#else\n#include <netinet/tcp.h>\n#include <sys/socket.h>\n#endif\n\n#include <drogon/drogon.h>\nusing namespace drogon;\n\nint main()\n{\n    // `registerHandler()` adds a handler to the desired path. The handler is\n    // responsible for generating a HTTP response upon an HTTP request being\n    // sent to Drogon\n    app().registerHandler(\n        \"/\",\n        [](const HttpRequestPtr &request,\n           std::function<void(const HttpResponsePtr &)> &&callback) {\n            LOG_INFO << \"connected:\"\n                     << (request->connected() ? \"true\" : \"false\");\n            auto resp = HttpResponse::newHttpResponse();\n            resp->setBody(\"Hello, World!\");\n            callback(resp);\n        },\n        {Get});\n\n    // `registerHandler()` also supports parsing and passing the path as\n    // parameters to the handler. Parameters are specified using {}. The text\n    // inside the {} does not correspond to the index of parameter passed to the\n    // handler (nor it has any meaning). Instead, it is only to make it easier\n    // for users to recognize the function of each parameter.\n    app().registerHandler(\n        \"/user/{user-name}\",\n        [](const HttpRequestPtr &,\n           std::function<void(const HttpResponsePtr &)> &&callback,\n           const std::string &name) {\n            auto resp = HttpResponse::newHttpResponse();\n            resp->setBody(\"Hello, \" + name + \"!\");\n            callback(resp);\n        },\n        {Get});\n\n    // You can also specify that the parameter is in the query section of the\n    // URL!\n    app().registerHandler(\n        \"/hello?user={user-name}\",\n        [](const HttpRequestPtr &,\n           std::function<void(const HttpResponsePtr &)> &&callback,\n           const std::string &name) {\n            auto resp = HttpResponse::newHttpResponse();\n            resp->setBody(\"Hello, \" + name + \"!\");\n            callback(resp);\n        },\n        {Get});\n\n    // Or, if you want to, instead of asking drogon to parse it for you. You can\n    // parse the request yourselves.\n    app().registerHandler(\n        \"/hello_user\",\n        [](const HttpRequestPtr &req,\n           std::function<void(const HttpResponsePtr &)> &&callback) {\n            auto resp = HttpResponse::newHttpResponse();\n            auto name = req->getOptionalParameter<std::string>(\"user\");\n            if (!name)\n                resp->setBody(\"Please tell me your name\");\n            else\n                resp->setBody(\"Hello, \" + name.value() + \"!\");\n            callback(resp);\n        },\n        {Get});\n\n    app()\n        .setBeforeListenSockOptCallback([](int fd) {\n            LOG_INFO << \"setBeforeListenSockOptCallback:\" << fd;\n#ifdef _WIN32\n#elif __linux__\n            int enable = 1;\n            if (setsockopt(\n                    fd, IPPROTO_TCP, TCP_FASTOPEN, &enable, sizeof(enable)) ==\n                -1)\n            {\n                LOG_INFO << \"setsockopt TCP_FASTOPEN failed\";\n            }\n#else\n#endif\n        })\n        .setAfterAcceptSockOptCallback([](int) {});\n\n    // Ask Drogon to listen on 127.0.0.1 port 8848. Drogon supports listening\n    // on multiple IP addresses by adding multiple listeners. For example, if\n    // you want the server also listen on 127.0.0.1 port 5555. Just add another\n    // line of addListener(\"127.0.0.1\", 5555)\n    LOG_INFO << \"Server running on 127.0.0.1:8848\";\n    app().addListener(\"127.0.0.1\", 8848).run();\n}\n"
  },
  {
    "path": "examples/jsonstore/README.md",
    "content": "This folder contains a [jsonstore](https://github.com/bluzi/jsonstore)-like storage service. But is multi-threaded and stores everything in memory. Serving as a showcase on how to build a minimally useful RESTful APIs in Drogon.\n\n## API\n\n#### /get-token\nGenerate a token that the user can use to create, read, modify and delete records.\n\n* **method**: GET\n* **URL params**: None\n* **Body**: None\n* **Success response**\n  * **Code**: 200\n  * **Content**: `{\"token\":\"3a322920d42ef0763152a6efff2ed51985530aedd45370f92fd0f0b8dcc30220\"}`\n* **Sample call**\n\n```bash\n❯ curl -XGET http://localhost:8848/get-token\n{\"token\":\"3a322920d42ef0763152a6efff2ed51985530aedd45370f92fd0f0b8dcc30220\"}\n```\n\n#### /{token}\n\nCreate a new JSON object associated with the token\n\n* **method**: POST\n* **URL params**: None\n* **Body**: The initial JSON object to store\n* **Success response**\n  * **Code**: 200\n  * **Content**: `{\"ok\":true}`\n* **Failed response**:\n  * **Code**: 500\n  * **Why**: Either the token already is associated with the data or request body/content-type is not JSON.\n  * **Content**: `{\"ok\":false}`\n* **Sample call**\n\n```bash\n❯ curl -XPOST http://localhost:8848/3a322920d42ef0763152a6efff2ed51985530aedd45370f92fd0f0b8dcc30220 \\\n        -H 'content-type: application/json' -d '{\"foo\":{\"bar\":42}}'\n{\"ok\":true}\n```\n\nDelete the JSON object associated with the token\n\n* **method**: DELETE\n* **URL params**: None\n* **Body**: None\n* **Success response**\n  * **Code**: 200\n  * **Content**: `{\"ok\":true}`\n* **Sample call**\n\n```bash\n❯ curl -XDELETE http://localhost:8848/3a322920d42ef0763152a6efff2ed51985530aedd45370f92fd0f0b8dcc30220\n{\"ok\":true}\n```\n\n#### /{token}/{some/path/to/data}\n\nRetrieve data at and below the specified path\n* **method**: GET\n* **URL params**: None\n* **Body**: None\n* **Success response**\n  * **Code**: 200\n  * **Content**: `{\"foo\":{\"bar\":42}}`\n* **Failed response**:\n  * **Code**: 500\n  * **Why**: No data associated with the provided token, or it does not exist in the JSON object.\n  * **Content**: `{\"ok\":false}`\n* **Sample call**\n\n```bash\n❯ curl -XGET http://localhost:8848/3a322920d42ef0763152a6efff2ed51985530aedd45370f92fd0f0b8dcc30220/\n{\"foo\":{\"bar\":42}}\n```\n\nUpdate data at the specified path\n* **method**: PUT\n* **URL params**: None\n* **Body**: The JSON object you wish to replace to\n* **Success response**\n  * **Code**: 200\n  * **Content**: `{\"ok\":true}`\n* **Failed response**:\n  * **Code**: 500\n  * **Why**: No data associated with the provided token or it does not exist in the JSON object.\n  * **Content**: `{\"ok\":false}`\n* **Sample call**\n\n```bash\n❯ curl -XPUT http://localhost:8848/3a322920d42ef0763152a6efff2ed51985530aedd45370f92fd0f0b8dcc30220/foo \\\n        -H 'content-type: application/json' -d '{\"fruit\":\"apple\"}'\n{\"ok\":true}\n```\n\n## Example use\n\n```bash\nexport URL=\"http://localhost:8848\"\nexport TOKEN=`curl $URL/get-token -s | sed 's/.*\"\\([0-9a-f]*\\)\".*/\\1/'`\nprintf \"Token is: $TOKEN\\n\" \n\nprintf 'Creating new data \\n> '\ncurl -XPOST $URL/$TOKEN -H 'content-type: application/json' -d '{\"foo\":{\"bar\":42}}'\nprintf '\\nRetrieving value of data[\"foo\"][\"bar\"] \\n> '\ncurl $URL/$TOKEN/foo/bar\nprintf '\\nModifing data \\n> '\ncurl -XPUT $URL/$TOKEN/foo -H 'content-type: application/json' -d '{\"zoo\":\"zebra\"}'\nprintf '\\nNow data[\"foo\"][\"bar\"] no longer exists \\n> '\ncurl $URL/$TOKEN/foo/bar\nprintf '\\nDelete data \\n> '\ncurl -XDELETE $URL/$TOKEN\necho\n```\n\nOutput:\n```\nToken is: 5e73ba044b45e68b4856925faea268391091f39fc62ab8c58955cf20957018fa\nCreating new data \n> {\"ok\":true}\nRetrieving value of data[\"foo\"][\"bar\"] \n> 42\nModifying data \n> {\"ok\":true}\nNow data[\"foo\"][\"bar\"] no longer exists \n> {\"ok\":false}\nDelete data \n> {\"ok\":true}\n```"
  },
  {
    "path": "examples/jsonstore/main.cc",
    "content": "#include <drogon/drogon.h>\n\nusing namespace drogon;\n\nHttpResponsePtr makeFailedResponse()\n{\n    Json::Value json;\n    json[\"ok\"] = false;\n    auto resp = HttpResponse::newHttpJsonResponse(json);\n    resp->setStatusCode(k500InternalServerError);\n    return resp;\n}\n\nHttpResponsePtr makeSuccessResponse()\n{\n    Json::Value json;\n    json[\"ok\"] = true;\n    auto resp = HttpResponse::newHttpJsonResponse(json);\n    return resp;\n}\n\nstd::string getRandomString(size_t n)\n{\n    std::vector<unsigned char> random(n);\n    utils::secureRandomBytes(random.data(), random.size());\n\n    // This is cryptographically safe as 256 mod 16 == 0\n    static const std::string alphabets = \"0123456789abcdef\";\n    assert(256 % alphabets.size() == 0);\n    std::string randomString(n, '\\0');\n    for (size_t i = 0; i < n; i++)\n        randomString[i] = alphabets[random[i] % alphabets.size()];\n    return randomString;\n}\n\nstruct DataItem\n{\n    Json::Value item;\n    std::mutex mtx;\n};\n\nclass JsonStore : public HttpController<JsonStore>\n{\n  public:\n    METHOD_LIST_BEGIN\n    ADD_METHOD_TO(JsonStore::getToken, \"/get-token\", Get);\n    ADD_METHOD_VIA_REGEX(JsonStore::getItem, \"/([a-f0-9]{64})/(.*)\", Get);\n    ADD_METHOD_VIA_REGEX(JsonStore::createItem, \"/([a-f0-9]{64})\", Post);\n    ADD_METHOD_VIA_REGEX(JsonStore::deleteItem, \"/([a-f0-9]{64})\", Delete);\n    ADD_METHOD_VIA_REGEX(JsonStore::updateItem, \"/([a-f0-9]{64})/(.*)\", Put);\n    METHOD_LIST_END\n\n    void getToken(const HttpRequestPtr &,\n                  std::function<void(const HttpResponsePtr &)> &&callback)\n    {\n        std::string randomString = getRandomString(64);\n        Json::Value res;\n        res[\"token\"] = randomString;\n\n        callback(HttpResponse::newHttpJsonResponse(std::move(res)));\n    }\n\n    void getItem(const HttpRequestPtr &,\n                 std::function<void(const HttpResponsePtr &)> &&callback,\n                 const std::string &token,\n                 const std::string &path)\n    {\n        auto itemPtr = [this, &token]() -> std::shared_ptr<DataItem> {\n            // It is possible that the item is being removed while another\n            // thread tries to look it up. The mutex here prevents that from\n            // happening.\n            std::lock_guard<std::mutex> lock(storageMtx_);\n            auto it = dataStore_.find(token);\n            if (it == dataStore_.end())\n                return nullptr;\n            return it->second;\n        }();\n        if (itemPtr == nullptr)\n        {\n            callback(makeFailedResponse());\n            return;\n        }\n\n        auto &item = *itemPtr;\n        // Prevents another thread from writing to the same item while this\n        // thread reads. Could cause blockage if multiple clients are asking to\n        // read the same object. But that should be rare.\n        std::lock_guard<std::mutex> lock(item.mtx);\n        Json::Value *valuePtr = walkJson(item.item, path);\n\n        if (valuePtr == nullptr)\n        {\n            callback(makeFailedResponse());\n            return;\n        }\n\n        auto resp = HttpResponse::newHttpJsonResponse(*valuePtr);\n        callback(resp);\n    }\n\n    void updateItem(const HttpRequestPtr &req,\n                    std::function<void(const HttpResponsePtr &)> &&callback,\n                    const std::string &token,\n                    const std::string &path)\n    {\n        auto jsonPtr = req->jsonObject();\n        auto itemPtr = [this, &token]() -> std::shared_ptr<DataItem> {\n            // It is possible that the item is being removed while another\n            // thread tries to look it up. The mutex here prevents that from\n            // happening.\n            std::lock_guard<std::mutex> lock(storageMtx_);\n            auto it = dataStore_.find(token);\n            if (it == dataStore_.end())\n                return nullptr;\n            return it->second;\n        }();\n\n        if (itemPtr == nullptr || jsonPtr == nullptr)\n        {\n            callback(makeFailedResponse());\n            return;\n        }\n\n        auto &item = *itemPtr;\n        std::lock_guard<std::mutex> lock(item.mtx);\n        Json::Value *valuePtr = walkJson(item.item, path, 1);\n\n        if (valuePtr == nullptr)\n        {\n            callback(makeFailedResponse());\n            return;\n        }\n\n        std::string key = utils::splitString(path, \"/\").back();\n        (*valuePtr)[key] = *jsonPtr;\n\n        callback(makeSuccessResponse());\n    }\n\n    void createItem(const HttpRequestPtr &req,\n                    std::function<void(const HttpResponsePtr &)> &&callback,\n                    const std::string &token)\n    {\n        auto jsonPtr = req->jsonObject();\n        if (jsonPtr == nullptr)\n        {\n            callback(makeFailedResponse());\n            return;\n        }\n\n        std::lock_guard<std::mutex> lock(storageMtx_);\n        if (dataStore_.find(token) == dataStore_.end())\n        {\n            auto item = std::make_shared<DataItem>();\n            item->item = std::move(*jsonPtr);\n            dataStore_.insert({token, std::move(item)});\n\n            callback(makeSuccessResponse());\n        }\n        else\n        {\n            callback(makeFailedResponse());\n        }\n    }\n\n    void deleteItem(const HttpRequestPtr &,\n                    std::function<void(const HttpResponsePtr &)> &&callback,\n                    const std::string &token)\n    {\n        std::lock_guard<std::mutex> lock(storageMtx_);\n        dataStore_.erase(token);\n\n        callback(makeSuccessResponse());\n    }\n\n  protected:\n    static Json::Value *walkJson(Json::Value &json,\n                                 const std::string &path,\n                                 size_t ignore_back = 0)\n    {\n        auto pathElem = utils::splitString(path, \"/\", false);\n        if (pathElem.size() >= ignore_back)\n            pathElem.resize(pathElem.size() - ignore_back);\n        Json::Value *valuePtr = &json;\n        for (const auto &elem : pathElem)\n        {\n            if (valuePtr->isArray())\n            {\n                Json::Value &value = (*valuePtr)[std::stoi(elem)];\n                if (value.isNull())\n                    return nullptr;\n\n                valuePtr = &value;\n            }\n            else\n            {\n                Json::Value &value = (*valuePtr)[elem];\n                if (value.isNull())\n                    return nullptr;\n\n                valuePtr = &value;\n            }\n        }\n        return valuePtr;\n    }\n\n    std::unordered_map<std::string, std::shared_ptr<DataItem>> dataStore_;\n    std::mutex storageMtx_;\n};\n\nint main()\n{\n    app().addListener(\"127.0.0.1\", 8848).run();\n}\n"
  },
  {
    "path": "examples/login_session/LoginPage.csp",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>session example</title>\n</head>\n\t<body>\n\t<form action=\"/login\" method=\"post\">\n\t\t<div class=\"container\">\n\t\t\t<label for=\"user\"><b>Username</b></label>\n\t\t\t<input type=\"text\" placeholder=\"Enter Username\" name=\"user\" required>\n\n\t\t\t<label for=\"passwd\"><b>Password</b></label>\n\t\t\t<input type=\"password\" placeholder=\"Enter Password\" name=\"passwd\" required>\n\n\t\t\t<button type=\"submit\">Login</button>\n\t\t</div>\n\t</form> \n\n\t<p>Please login with the credentials <br>Username: user<br>Password: password123</p>\n</body>\n</html>\n"
  },
  {
    "path": "examples/login_session/LogoutPage.csp",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>session example</title>\n</head>\n\t<body>\n\t<form action=\"/logout\" method=\"post\">\n\t\t<div class=\"container\">\n\t\t\t<button type=\"submit\">Logout</button>\n\t\t</div>\n\t</form> \n\n\t<p>You can logout now</p>\n</body>\n</html>\n"
  },
  {
    "path": "examples/login_session/main.cc",
    "content": "#include <drogon/drogon.h>\n#include <chrono>\nusing namespace drogon;\nusing namespace std::chrono_literals;\n\nint main()\n{\n    app().registerHandler(\n        \"/\",\n        [](const HttpRequestPtr &req,\n           std::function<void(const HttpResponsePtr &)> &&callback) {\n            bool loggedIn =\n                req->session()->getOptional<bool>(\"loggedIn\").value_or(false);\n            HttpResponsePtr resp;\n            if (loggedIn == false)\n                resp = HttpResponse::newHttpViewResponse(\"LoginPage\");\n            else\n                resp = HttpResponse::newHttpViewResponse(\"LogoutPage\");\n            callback(resp);\n        });\n\n    app().registerHandler(\n        \"/logout\",\n        [](const HttpRequestPtr &req,\n           std::function<void(const HttpResponsePtr &)> &&callback) {\n            HttpResponsePtr resp = HttpResponse::newHttpResponse();\n            req->session()->erase(\"loggedIn\");\n            resp->setBody(\"<script>window.location.href = \\\"/\\\";</script>\");\n            callback(resp);\n        },\n        {Post});\n\n    app().registerHandler(\n        \"/login\",\n        [](const HttpRequestPtr &req,\n           std::function<void(const HttpResponsePtr &)> &&callback) {\n            HttpResponsePtr resp = HttpResponse::newHttpResponse();\n            std::string user = req->getParameter(\"user\");\n            std::string passwd = req->getParameter(\"passwd\");\n\n            // NOTE: Do not use MD5 for the password hash under any\n            // circumstances. We only use it because Drogon is not a\n            // cryptography library, so it does not include a better hash\n            // algorithm. Use Argon2 or BCrypt in a real product. username:\n            // user, password: password123\n            if (user == \"user\" && utils::getMd5(\"jadsjhdsajkh\" + passwd) ==\n                                      \"5B5299CF4CEAE2D523315694B82573C9\")\n            {\n                req->session()->insert(\"loggedIn\", true);\n                resp->setBody(\"<script>window.location.href = \\\"/\\\";</script>\");\n                callback(resp);\n            }\n            else\n            {\n                resp->setStatusCode(k401Unauthorized);\n                resp->setBody(\"<script>window.location.href = \\\"/\\\";</script>\");\n                callback(resp);\n            }\n        },\n        {Post});\n\n    LOG_INFO << \"Server running on 127.0.0.1:8848\";\n    app()\n        // All sessions are stored for 24 Hours\n        .enableSession(24h)\n        .addListener(\"127.0.0.1\", 8848)\n        .run();\n}\n"
  },
  {
    "path": "examples/prometheus_example/.gitignore",
    "content": "# Created by https://www.toptal.com/developers/gitignore/api/intellij+all,visualstudio,visualstudiocode,cmake,c,c++\n# Edit at https://www.toptal.com/developers/gitignore?templates=intellij+all,visualstudio,visualstudiocode,cmake,c,c++\n\n### C ###\n# Prerequisites\n*.d\n\n# Object files\n*.o\n*.ko\n*.obj\n*.elf\n\n# Linker output\n*.ilk\n*.map\n*.exp\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Libraries\n*.lib\n*.a\n*.la\n*.lo\n\n# Shared objects (inc. Windows DLLs)\n*.dll\n*.so\n*.so.*\n*.dylib\n\n# Executables\n*.exe\n*.out\n*.app\n*.i*86\n*.x86_64\n*.hex\n\n# Debug files\n*.dSYM/\n*.su\n*.idb\n*.pdb\n\n# Kernel Module Compile Results\n*.mod*\n*.cmd\n.tmp_versions/\nmodules.order\nModule.symvers\nMkfile.old\ndkms.conf\n\n### C++ ###\n# Prerequisites\n\n# Compiled Object files\n*.slo\n\n# Precompiled Headers\n\n# Linker files\n\n# Debugger Files\n\n# Compiled Dynamic libraries\n\n# Fortran module files\n*.mod\n*.smod\n\n# Compiled Static libraries\n*.lai\n\n# Executables\n\n### CMake ###\nCMakeLists.txt.user\nCMakeCache.txt\nCMakeFiles\nCMakeScripts\nTesting\nMakefile\ncmake_install.cmake\ninstall_manifest.txt\ncompile_commands.json\nCTestTestfile.cmake\n_deps\nCMakeUserPresets.json\n\n### CMake Patch ###\n# External projects\n*-prefix/\n\n### Intellij+all ###\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider\n# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\n\n# User-specific stuff\n.idea/**/workspace.xml\n.idea/**/tasks.xml\n.idea/**/usage.statistics.xml\n.idea/**/dictionaries\n.idea/**/shelf\n\n# Generated files\n.idea/**/contentModel.xml\n\n# Sensitive or high-churn files\n.idea/**/dataSources/\n.idea/**/dataSources.ids\n.idea/**/dataSources.local.xml\n.idea/**/sqlDataSources.xml\n.idea/**/dynamic.xml\n.idea/**/uiDesigner.xml\n.idea/**/dbnavigator.xml\n\n# Gradle\n.idea/**/gradle.xml\n.idea/**/libraries\n\n# Gradle and Maven with auto-import\n# When using Gradle or Maven with auto-import, you should exclude module files,\n# since they will be recreated, and may cause churn.  Uncomment if using\n# auto-import.\n# .idea/artifacts\n# .idea/compiler.xml\n# .idea/jarRepositories.xml\n# .idea/modules.xml\n# .idea/*.iml\n# .idea/modules\n# *.iml\n# *.ipr\n\n# CMake\ncmake-build-*/\n\n# Mongo Explorer plugin\n.idea/**/mongoSettings.xml\n\n# File-based project format\n*.iws\n\n# IntelliJ\nout/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# Cursive Clojure plugin\n.idea/replstate.xml\n\n# Crashlytics plugin (for Android Studio and IntelliJ)\ncom_crashlytics_export_strings.xml\ncrashlytics.properties\ncrashlytics-build.properties\nfabric.properties\n\n# Editor-based Rest Client\n.idea/httpRequests\n\n# Android studio 3.1+ serialized cache file\n.idea/caches/build_file_checksums.ser\n\n### Intellij+all Patch ###\n# Ignores the whole .idea folder and all .iml files\n# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360\n\n.idea/\n\n# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023\n\n*.iml\nmodules.xml\n.idea/misc.xml\n*.ipr\n\n# Sonarlint plugin\n.idea/sonarlint\n\n### VisualStudioCode ###\n.vscode/*\n!.vscode/tasks.json\n!.vscode/launch.json\n*.code-workspace\n\n### VisualStudioCode Patch ###\n# Ignore all local history of files\n.history\n.ionide\n\n### VisualStudio ###\n## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Mono auto generated files\nmono_crash.*\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Ww][Ii][Nn]32/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n[Ll]ogs/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUnit\n*.VisualState.xml\nTestResult.xml\nnunit-*.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# ASP.NET Scaffolding\nScaffoldingReadMe.txt\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.meta\n*.iobj\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Coverlet is a free, cross platform Code Coverage Tool\ncoverage*[.json, .xml, .info]\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# NuGet Symbol Packages\n*.snupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n*.appxbundle\n*.appxupload\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- [Bb]ackup.rdl\n*- [Bb]ackup ([0-9]).rdl\n*- [Bb]ackup ([0-9][0-9]).rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\nMigrationBackup/\n\n# Ionide (cross platform F# VS Code tools) working folder\n.ionide/\n\n# Fody - auto-generated XML schema\nFodyWeavers.xsd\n\n### VisualStudio Patch ###\n# Additional files built by Visual Studio\n*.tlog\n\n# End of https://www.toptal.com/developers/gitignore/api/intellij+all,visualstudio,visualstudiocode,cmake,c,c++\n"
  },
  {
    "path": "examples/prometheus_example/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.5)\nproject(prometheus_example CXX)\n\ninclude(CheckIncludeFileCXX)\n\ncheck_include_file_cxx(any HAS_ANY)\ncheck_include_file_cxx(string_view HAS_STRING_VIEW)\ncheck_include_file_cxx(coroutine HAS_COROUTINE)\nif (NOT \"${CMAKE_CXX_STANDARD}\" STREQUAL \"\")\n    # Do nothing\nelseif (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)\n    set(CMAKE_CXX_STANDARD 20)\nelseif (HAS_ANY AND HAS_STRING_VIEW)\n    set(CMAKE_CXX_STANDARD 17)\nelse ()\n    set(CMAKE_CXX_STANDARD 14)\nendif ()\n\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nset(CMAKE_CXX_EXTENSIONS OFF)\n\nadd_executable(${PROJECT_NAME} main.cc)\n\n# ##############################################################################\n# If you include the drogon source code locally in your project, use this method\n# to add drogon \n# add_subdirectory(drogon) \n# target_link_libraries(${PROJECT_NAME} PRIVATE drogon)\n#\n# and comment out the following lines\nfind_package(Drogon CONFIG REQUIRED)\ntarget_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)\n\n# ##############################################################################\n\nif (CMAKE_CXX_STANDARD LESS 17)\n    message(FATAL_ERROR \"c++17 or higher is required\")\nelseif (CMAKE_CXX_STANDARD LESS 20)\n    message(STATUS \"use c++17\")\nelse ()\n    message(STATUS \"use c++20\")\nendif ()\n\naux_source_directory(controllers CTL_SRC)\naux_source_directory(filters FILTER_SRC)\naux_source_directory(plugins PLUGIN_SRC)\naux_source_directory(models MODEL_SRC)\n\ndrogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views\n                    ${CMAKE_CURRENT_BINARY_DIR})\n# use the following line to create views with namespaces.\n# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views\n#                     ${CMAKE_CURRENT_BINARY_DIR} TRUE)\n# use the following line to create views with namespace CHANGE_ME prefixed\n# and path namespaces.\n# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views\n#                     ${CMAKE_CURRENT_BINARY_DIR} TRUE CHANGE_ME)\n\ntarget_include_directories(${PROJECT_NAME}\n                           PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}\n                                   ${CMAKE_CURRENT_SOURCE_DIR}/models)\ntarget_sources(${PROJECT_NAME}\n               PRIVATE\n               ${SRC_DIR}\n               ${CTL_SRC}\n               ${FILTER_SRC}\n               ${PLUGIN_SRC}\n               ${MODEL_SRC})\n# ##############################################################################\n# uncomment the following line for dynamically loading views \n# set_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS ON)\n\n# ##############################################################################\n\nadd_subdirectory(test)\n"
  },
  {
    "path": "examples/prometheus_example/config.json",
    "content": "{\n\n    \"listeners\": [\n        {\n            \"address\": \"0.0.0.0\",\n            \"port\": 5555,\n            \"https\": false\n        }\n    ],\n    \"plugins\": [\n        {\n            \"name\": \"drogon::plugin::PromExporter\",\n            \"dependencies\": [],\n            \"config\": {\n                \"path\": \"/metrics\",\n                \"collectors\":[\n                    {\n                        \"name\": \"http_requests_total\",\n                        \"help\": \"The total number of http requests\",\n                        \"type\": \"counter\",\n                        \"labels\": [\"method\", \"path\"]\n                    },\n                    {\n                        \"name\": \"http_request_duration_seconds\",\n                        \"help\": \"The processing time of http requests, in seconds\",\n                        \"type\": \"histogram\",\n                        \"labels\": [\"method\", \"path\"]\n                    }\n                ]\n            }\n        }\n    ]\n}\n"
  },
  {
    "path": "examples/prometheus_example/controllers/PromTestCtrl.cc",
    "content": "#include \"PromTestCtrl.h\"\n\nusing namespace drogon;\n\nvoid PromTestCtrl::fast(const HttpRequestPtr &req,\n                        std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setBody(\"Hello, world!\");\n    callback(resp);\n}\n\ndrogon::AsyncTask PromTestCtrl::slow(\n    const HttpRequestPtr req,\n    std::function<void(const HttpResponsePtr &)> callback)\n{\n    // sleep for a random time between 1 and 3 seconds\n    static std::once_flag flag;\n    std::call_once(flag, []() { srand(time(nullptr)); });\n    auto duration = 1 + (rand() % 3);\n    auto loop = trantor::EventLoop::getEventLoopOfCurrentThread();\n    co_await drogon::sleepCoro(loop, std::chrono::seconds(duration));\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setBody(\"Hello, world!\");\n    callback(resp);\n    co_return;\n}\n"
  },
  {
    "path": "examples/prometheus_example/controllers/PromTestCtrl.h",
    "content": "#pragma once\n\n#include <drogon/HttpController.h>\n#include <drogon/utils/coroutine.h>\n\nusing namespace drogon;\n\nclass PromTestCtrl : public drogon::HttpController<PromTestCtrl>\n{\n  public:\n    METHOD_LIST_BEGIN\n    ADD_METHOD_TO(PromTestCtrl::fast, \"/fast\", \"PromStat\");\n    ADD_METHOD_TO(PromTestCtrl::slow, \"/slow\", \"PromStat\");\n    METHOD_LIST_END\n\n    void fast(const HttpRequestPtr &req,\n              std::function<void(const HttpResponsePtr &)> &&callback);\n    drogon::AsyncTask slow(\n        const HttpRequestPtr req,\n        std::function<void(const HttpResponsePtr &)> callback);\n};\n"
  },
  {
    "path": "examples/prometheus_example/filters/PromStat.cc",
    "content": "/**\n *\n *  PromStat.cc\n *\n */\n\n#include \"PromStat.h\"\n#include <drogon/plugins/PromExporter.h>\n#include <drogon/utils/monitoring/Counter.h>\n#include <drogon/utils/monitoring/Histogram.h>\n#include <drogon/HttpAppFramework.h>\n#include <chrono>\n\nusing namespace std::literals::chrono_literals;\nusing namespace drogon;\n\nTask<HttpResponsePtr> PromStat::invoke(const HttpRequestPtr &req,\n                                       MiddlewareNextAwaiter &&next)\n{\n    std::string path{req->matchedPathPattern()};\n    auto method = req->methodString();\n    auto promExporter = app().getPlugin<drogon::plugin::PromExporter>();\n    if (promExporter)\n    {\n        auto collector =\n            promExporter->getCollector<drogon::monitoring::Counter>(\n                \"http_requests_total\");\n        if (collector)\n        {\n            collector->metric({method, path})->increment();\n        }\n    }\n    auto start = trantor::Date::date();\n    auto resp = co_await next;\n    if (promExporter)\n    {\n        auto collector =\n            promExporter->getCollector<drogon::monitoring::Histogram>(\n                \"http_request_duration_seconds\");\n        if (collector)\n        {\n            static const std::vector<double> boundaries{\n                0.0001, 0.001, 0.01, 0.1, 0.5, 1, 2, 3};\n            auto end = trantor::Date::date();\n            auto duration =\n                end.microSecondsSinceEpoch() - start.microSecondsSinceEpoch();\n            collector->metric({method, path}, boundaries, 1h, 6)\n                ->observe((double)duration / 1000000);\n        }\n    }\n    co_return resp;\n}\n"
  },
  {
    "path": "examples/prometheus_example/filters/PromStat.h",
    "content": "/**\n *\n *  PromStat.h\n *\n */\n\n#pragma once\n\n#include <drogon/HttpMiddleware.h>\n\nusing namespace drogon;\n\nclass PromStat : public HttpCoroMiddleware<PromStat>\n{\n  public:\n    PromStat()\n    {\n        void(0);\n    }\n\n    virtual ~PromStat()\n    {\n    }\n\n    Task<HttpResponsePtr> invoke(const HttpRequestPtr &req,\n                                 MiddlewareNextAwaiter &&next) override;\n};\n"
  },
  {
    "path": "examples/prometheus_example/main.cc",
    "content": "#include <drogon/drogon.h>\n\nint main()\n{\n    drogon::app().loadConfigFile(\"../config.json\").run();\n    return 0;\n}\n"
  },
  {
    "path": "examples/prometheus_example/test/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.5)\nproject(prometheus_example_test CXX)\n\nadd_executable(${PROJECT_NAME} test_main.cc)\n\n# ##############################################################################\n# If you include the drogon source code locally in your project, use this method\n# to add drogon \n# target_link_libraries(${PROJECT_NAME} PRIVATE drogon)\n#\n# and comment out the following lines\ntarget_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)\n\nParseAndAddDrogonTests(${PROJECT_NAME})\n"
  },
  {
    "path": "examples/prometheus_example/test/test_main.cc",
    "content": "#define DROGON_TEST_MAIN\n#include <drogon/drogon_test.h>\n#include <drogon/drogon.h>\n\nDROGON_TEST(BasicTest)\n{\n    // Add your tests here\n}\n\nint main(int argc, char **argv)\n{\n    using namespace drogon;\n\n    std::promise<void> p1;\n    std::future<void> f1 = p1.get_future();\n\n    // Start the main loop on another thread\n    std::thread thr([&]() {\n        // Queues the promise to be fulfilled after starting the loop\n        app().getLoop()->queueInLoop([&p1]() { p1.set_value(); });\n        app().run();\n    });\n\n    // The future is only satisfied after the event loop started\n    f1.get();\n    int status = test::run(argc, argv);\n\n    // Ask the event loop to shutdown and wait\n    app().getLoop()->queueInLoop([]() { app().quit(); });\n    thr.join();\n    return status;\n}\n"
  },
  {
    "path": "examples/redis/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.5)\nproject(redis CXX)\n\ninclude(CheckIncludeFileCXX)\n\ncheck_include_file_cxx(any HAS_ANY)\ncheck_include_file_cxx(string_view HAS_STRING_VIEW)\ncheck_include_file_cxx(coroutine HAS_COROUTINE)\nif (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)\n    set(CMAKE_CXX_STANDARD 20)\nelseif (HAS_ANY AND HAS_STRING_VIEW)\n    set(CMAKE_CXX_STANDARD 17)\nelse ()\n    set(CMAKE_CXX_STANDARD 14)\nendif ()\n\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nset(CMAKE_CXX_EXTENSIONS OFF)\n\nadd_executable(${PROJECT_NAME} main.cc)\n\n# ##############################################################################\n# If you include the drogon source code locally in your project, use this method\n# to add drogon \n# add_subdirectory(drogon) \n# target_link_libraries(${PROJECT_NAME} PRIVATE drogon)\n#\n# and comment out the following lines\nfind_package(Drogon CONFIG REQUIRED)\ntarget_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)\n\n# ##############################################################################\n\nif (CMAKE_CXX_STANDARD LESS 17)\n    # With C++14, use boost to support any and std::string_view\n    message(STATUS \"use c++14\")\n    find_package(Boost 1.61.0 REQUIRED)\n    target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS})\nelseif (CMAKE_CXX_STANDARD LESS 20)\n    message(STATUS \"use c++17\")\nelse ()\n    message(STATUS \"use c++20\")\nendif ()\n\naux_source_directory(controllers CTL_SRC)\naux_source_directory(filters FILTER_SRC)\naux_source_directory(plugins PLUGIN_SRC)\naux_source_directory(models MODEL_SRC)\n\ndrogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views\n                    ${CMAKE_CURRENT_BINARY_DIR})\n# use the following line to create views with namespaces.\n# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views\n#                     ${CMAKE_CURRENT_BINARY_DIR} TRUE)\n\ntarget_include_directories(${PROJECT_NAME}\n                           PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}\n                                   ${CMAKE_CURRENT_SOURCE_DIR}/models)\ntarget_sources(${PROJECT_NAME}\n               PRIVATE\n               ${SRC_DIR}\n               ${CTL_SRC}\n               ${FILTER_SRC}\n               ${PLUGIN_SRC}\n               ${MODEL_SRC})\n# ##############################################################################\n# uncomment the following line for dynamically loading views \n# set_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS ON)\n"
  },
  {
    "path": "examples/redis/README.md",
    "content": "# Redis example\n\nA simple redis example\n\n## Usage\n\nFirst of all you need redis running on the port 6379\n\n### Post\n\n```\ncurl --location --request POST 'localhost:8080/client/foo' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n  \"value\": \"bar\"\n}'\n```\n\n### Get\n\n```\ncurl --location --request GET 'localhost:8080/client/foo'\n```\n\n## Subscribe and Publish\n\nGo to a websocket test website, such as https://wstool.js.org\n\n### Subscribe\n\nConnect to ws://localhost:8080/sub\n\nTo subscribe to a channel, send channel name: `mychannel`\n\nTo unsubscribe from a channel, send 'unsub ' + channel name: `unsub mychannel`\n\n### Publish\n\nConnect to ws://localhost:8080/pub\n\nTo publish message, send channel name and message: `mychannel anything follows will be the message.`\n"
  },
  {
    "path": "examples/redis/controllers/Client.cc",
    "content": "#include \"Client.h\"\n\nusing namespace drogon;\n\nvoid Client::get(const HttpRequestPtr &,\n                 std::function<void(const HttpResponsePtr &)> &&callback,\n                 std::string key)\n{\n    nosql::RedisClientPtr redisClient = app().getRedisClient();\n\n    redisClient->execCommandAsync(\n        [callback](const nosql::RedisResult &r) {\n            if (r.type() == nosql::RedisResultType::kNil)\n            {\n                auto resp = HttpResponse::newHttpResponse();\n                resp->setStatusCode(k404NotFound);\n                callback(resp);\n                return;\n            }\n\n            std::string redisResponse = r.asString();\n            Json::Value response;\n\n            Json::CharReaderBuilder builder;\n            Json::CharReader *reader = builder.newCharReader();\n\n            Json::Value json;\n            std::string errors;\n\n            bool parsingSuccessful =\n                reader->parse(redisResponse.c_str(),\n                              redisResponse.c_str() + redisResponse.size(),\n                              &json,\n                              &errors);\n\n            delete reader;\n\n            response[\"response\"] = redisResponse;\n            if (parsingSuccessful)\n            {\n                response[\"response\"] = json;\n            }\n\n            auto resp = HttpResponse::newHttpJsonResponse(response);\n\n            callback(resp);\n        },\n        [](const std::exception &err) {\n            LOG_ERROR << \"something failed!!! \" << err.what();\n        },\n        \"get %s\",\n        key.c_str());\n}\n\nvoid Client::post(const HttpRequestPtr &req,\n                  std::function<void(const HttpResponsePtr &)> &&callback,\n                  std::string key)\n{\n    nosql::RedisClientPtr redisClient = app().getRedisClient();\n    auto json = req->getJsonObject();\n\n    if (!json)\n    {\n        auto resp = HttpResponse::newHttpResponse();\n        resp->setBody(\"missing 'value' in body\");\n        resp->setStatusCode(k400BadRequest);\n        callback(resp);\n        return;\n    }\n\n    std::string value = (*json)[\"value\"].asString();\n\n    redisClient->execCommandAsync(\n        [callback](const nosql::RedisResult &) {\n            auto resp = HttpResponse::newHttpResponse();\n\n            resp->setStatusCode(k201Created);\n\n            callback(resp);\n        },\n        [](const std::exception &err) {\n            LOG_ERROR << \"something failed!!! \" << err.what();\n        },\n        \"set %s %s\",\n        key.c_str(),\n        value.c_str());\n}\n"
  },
  {
    "path": "examples/redis/controllers/Client.h",
    "content": "#pragma once\n#include <drogon/HttpController.h>\nusing namespace drogon;\n\nclass Client : public drogon::HttpController<Client>\n{\n  public:\n    METHOD_LIST_BEGIN\n    METHOD_ADD(Client::get, \"{1}\", Get);\n    METHOD_ADD(Client::post, \"{1}\", Post);\n    METHOD_LIST_END\n\n    void get(const HttpRequestPtr &req,\n             std::function<void(const HttpResponsePtr &)> &&callback,\n             std::string key);\n    void post(const HttpRequestPtr &req,\n              std::function<void(const HttpResponsePtr &)> &&callback,\n              std::string key);\n};\n"
  },
  {
    "path": "examples/redis/controllers/WsClient.cc",
    "content": "#include \"WsClient.h\"\n\n#include <memory>\n#include <unordered_set>\n\nstruct ClientContext\n{\n    std::unordered_set<std::string> channels_;\n    std::shared_ptr<nosql::RedisSubscriber> subscriber_;\n};\n\nvoid WsClient::handleNewMessage(const WebSocketConnectionPtr &wsConnPtr,\n                                std::string &&message,\n                                const WebSocketMessageType &type)\n{\n    if (type == WebSocketMessageType::Ping ||\n        type == WebSocketMessageType::Pong ||\n        type == WebSocketMessageType::Close)\n    {\n        return;\n    }\n\n    if (type != WebSocketMessageType::Text)\n    {\n        LOG_ERROR << \"Unsupported message type \" << (int)type;\n        return;\n    }\n    LOG_DEBUG << \"WsClient new message from \"\n              << wsConnPtr->peerAddr().toIpPort();\n\n    auto context = wsConnPtr->getContext<ClientContext>();\n    if (!context)\n    {\n        auto pos = message.find(' ');\n        if (pos == std::string::npos)\n        {\n            wsConnPtr->send(\"Invalid publish message.\");\n            return;\n        }\n\n        std::string channel = message.substr(0, pos);\n        std::string msg = message.substr(pos + 1);\n        LOG_INFO << \"PUBLISH \" << channel << \" \" << msg;\n\n        // Publisher\n        drogon::app().getRedisClient()->execCommandAsync(\n            [wsConnPtr](const nosql::RedisResult &result) {\n                std::string nSubs = std::to_string(result.asInteger());\n                LOG_INFO << \"PUBLISH success to \" << nSubs << \" subscribers.\";\n                wsConnPtr->send(\"PUBLISH success to \" + nSubs +\n                                \" subscribers.\");\n            },\n            [wsConnPtr](const nosql::RedisException &ex) {\n                LOG_INFO << \"PUBLISH failed, \" << ex.what();\n                wsConnPtr->send(std::string(\"PUBLISH failed: \") + ex.what());\n            },\n            \"PUBLISH %s %s\",\n            channel.c_str(),\n            msg.c_str());\n        return;\n    }\n\n    std::string channel = std::move(message);\n    if (channel.empty())\n    {\n        wsConnPtr->send(\"Channel not provided\");\n        return;\n    }\n\n    bool subscribe = true;\n    if (channel.compare(0, 6, \"unsub \") == 0)\n    {\n        channel = channel.substr(6);\n        subscribe = false;\n    }\n\n    if (subscribe)\n    {\n        if (context->channels_.find(channel) != context->channels_.end())\n        {\n            wsConnPtr->send(\"Already subscribed to channel \" + channel);\n            return;\n        }\n\n        context->subscriber_->subscribe(\n            channel,\n            [channel, wsConnPtr](const std::string &subChannel,\n                                 const std::string &subMessage) {\n                assert(subChannel == channel);\n                LOG_INFO << \"Receive channel message \" << subMessage;\n                std::string resp = \"{\\\"channel\\\":\\\"\" + subChannel +\n                                   \"\\\",\\\"message\\\":\\\"\" + subMessage + \"\\\"}\";\n                wsConnPtr->send(resp);\n            });\n\n        context->channels_.insert(channel);\n        wsConnPtr->send(\"Subscribe to channel: \" + channel);\n    }\n    else\n    {\n        if (context->channels_.find(channel) == context->channels_.end())\n        {\n            wsConnPtr->send(\"Channel not subscribed.\");\n            return;\n        }\n        context->channels_.erase(channel);\n        context->subscriber_->unsubscribe(channel);\n        wsConnPtr->send(\"Unsubscribe from channel: \" + channel);\n    }\n}\n\nvoid WsClient::handleNewConnection(const HttpRequestPtr &req,\n                                   const WebSocketConnectionPtr &wsConnPtr)\n{\n    if (req->getPath() == \"/sub\")\n    {\n        LOG_DEBUG << \"WsClient new subscriber connection from \"\n                  << wsConnPtr->peerAddr().toIpPort();\n        std::shared_ptr<ClientContext> context =\n            std::make_shared<ClientContext>();\n        context->subscriber_ = drogon::app().getRedisClient()->newSubscriber();\n        wsConnPtr->setContext(context);\n    }\n    else\n    {\n        LOG_DEBUG << \"WsClient new publisher connection from \"\n                  << wsConnPtr->peerAddr().toIpPort();\n    }\n}\n\nvoid WsClient::handleConnectionClosed(const WebSocketConnectionPtr &wsConnPtr)\n{\n    LOG_DEBUG << \"WsClient close connection from \"\n              << wsConnPtr->peerAddr().toIpPort();\n    // Channels will be auto unsubscribed when subscriber destructed.\n    // auto context = wsConnPtr->getContext<ClientContext>();\n    // for (auto& channel : context->channels_)\n    // {\n    //     context->subscriber_->unsubscribe(channel);\n    // }\n    wsConnPtr->clearContext();\n}\n"
  },
  {
    "path": "examples/redis/controllers/WsClient.h",
    "content": "#pragma once\n\n#include <drogon/WebSocketController.h>\n\nusing namespace drogon;\n\nclass WsClient : public drogon::WebSocketController<WsClient>\n{\n  public:\n    void handleNewMessage(const WebSocketConnectionPtr &,\n                          std::string &&,\n                          const WebSocketMessageType &) override;\n    void handleNewConnection(const HttpRequestPtr &,\n                             const WebSocketConnectionPtr &) override;\n    void handleConnectionClosed(const WebSocketConnectionPtr &) override;\n    WS_PATH_LIST_BEGIN\n    WS_PATH_ADD(\"/sub\");\n    WS_PATH_ADD(\"/pub\");\n    WS_PATH_LIST_END\n};\n"
  },
  {
    "path": "examples/redis/main.cc",
    "content": "#include <drogon/drogon.h>\n\nint main()\n{\n    // Set HTTP listener address and port\n    drogon::app().addListener(\"0.0.0.0\", 8080);\n    // Run HTTP framework,the method will block in the internal event loop\n    drogon::app().createRedisClient(\"127.0.0.1\", 6379);\n    drogon::app().run();\n    return 0;\n}\n"
  },
  {
    "path": "examples/redis_cache/.gitignore",
    "content": "# Created by https://www.toptal.com/developers/gitignore/api/intellij+all,visualstudio,visualstudiocode,cmake,c,c++\n# Edit at https://www.toptal.com/developers/gitignore?templates=intellij+all,visualstudio,visualstudiocode,cmake,c,c++\n\n### C ###\n# Prerequisites\n*.d\n\n# Object files\n*.o\n*.ko\n*.obj\n*.elf\n\n# Linker output\n*.ilk\n*.map\n*.exp\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Libraries\n*.lib\n*.a\n*.la\n*.lo\n\n# Shared objects (inc. Windows DLLs)\n*.dll\n*.so\n*.so.*\n*.dylib\n\n# Executables\n*.exe\n*.out\n*.app\n*.i*86\n*.x86_64\n*.hex\n\n# Debug files\n*.dSYM/\n*.su\n*.idb\n*.pdb\n\n# Kernel Module Compile Results\n*.mod*\n*.cmd\n.tmp_versions/\nmodules.order\nModule.symvers\nMkfile.old\ndkms.conf\n\n### C++ ###\n# Prerequisites\n\n# Compiled Object files\n*.slo\n\n# Precompiled Headers\n\n# Linker files\n\n# Debugger Files\n\n# Compiled Dynamic libraries\n\n# Fortran module files\n*.mod\n*.smod\n\n# Compiled Static libraries\n*.lai\n\n# Executables\n\n### CMake ###\nCMakeLists.txt.user\nCMakeCache.txt\nCMakeFiles\nCMakeScripts\nTesting\nMakefile\ncmake_install.cmake\ninstall_manifest.txt\ncompile_commands.json\nCTestTestfile.cmake\n_deps\nCMakeUserPresets.json\n\n### CMake Patch ###\n# External projects\n*-prefix/\n\n### Intellij+all ###\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider\n# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\n\n# User-specific stuff\n.idea/**/workspace.xml\n.idea/**/tasks.xml\n.idea/**/usage.statistics.xml\n.idea/**/dictionaries\n.idea/**/shelf\n\n# Generated files\n.idea/**/contentModel.xml\n\n# Sensitive or high-churn files\n.idea/**/dataSources/\n.idea/**/dataSources.ids\n.idea/**/dataSources.local.xml\n.idea/**/sqlDataSources.xml\n.idea/**/dynamic.xml\n.idea/**/uiDesigner.xml\n.idea/**/dbnavigator.xml\n\n# Gradle\n.idea/**/gradle.xml\n.idea/**/libraries\n\n# Gradle and Maven with auto-import\n# When using Gradle or Maven with auto-import, you should exclude module files,\n# since they will be recreated, and may cause churn.  Uncomment if using\n# auto-import.\n# .idea/artifacts\n# .idea/compiler.xml\n# .idea/jarRepositories.xml\n# .idea/modules.xml\n# .idea/*.iml\n# .idea/modules\n# *.iml\n# *.ipr\n\n# CMake\ncmake-build-*/\n\n# Mongo Explorer plugin\n.idea/**/mongoSettings.xml\n\n# File-based project format\n*.iws\n\n# IntelliJ\nout/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# Cursive Clojure plugin\n.idea/replstate.xml\n\n# Crashlytics plugin (for Android Studio and IntelliJ)\ncom_crashlytics_export_strings.xml\ncrashlytics.properties\ncrashlytics-build.properties\nfabric.properties\n\n# Editor-based Rest Client\n.idea/httpRequests\n\n# Android studio 3.1+ serialized cache file\n.idea/caches/build_file_checksums.ser\n\n### Intellij+all Patch ###\n# Ignores the whole .idea folder and all .iml files\n# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360\n\n.idea/\n\n# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023\n\n*.iml\nmodules.xml\n.idea/misc.xml\n*.ipr\n\n# Sonarlint plugin\n.idea/sonarlint\n\n### VisualStudioCode ###\n.vscode/*\n!.vscode/tasks.json\n!.vscode/launch.json\n*.code-workspace\n\n### VisualStudioCode Patch ###\n# Ignore all local history of files\n.history\n.ionide\n\n### VisualStudio ###\n## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Mono auto generated files\nmono_crash.*\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Ww][Ii][Nn]32/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n[Ll]ogs/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUnit\n*.VisualState.xml\nTestResult.xml\nnunit-*.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# ASP.NET Scaffolding\nScaffoldingReadMe.txt\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.meta\n*.iobj\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Coverlet is a free, cross platform Code Coverage Tool\ncoverage*[.json, .xml, .info]\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# NuGet Symbol Packages\n*.snupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n*.appxbundle\n*.appxupload\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- [Bb]ackup.rdl\n*- [Bb]ackup ([0-9]).rdl\n*- [Bb]ackup ([0-9][0-9]).rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\nMigrationBackup/\n\n# Ionide (cross platform F# VS Code tools) working folder\n.ionide/\n\n# Fody - auto-generated XML schema\nFodyWeavers.xsd\n\n### VisualStudio Patch ###\n# Additional files built by Visual Studio\n*.tlog\n\n# End of https://www.toptal.com/developers/gitignore/api/intellij+all,visualstudio,visualstudiocode,cmake,c,c++\n"
  },
  {
    "path": "examples/redis_cache/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.5)\nproject(redis_cache CXX)\n\ninclude(CheckIncludeFileCXX)\n\nset(CMAKE_CXX_STANDARD 20)\n\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nset(CMAKE_CXX_EXTENSIONS OFF)\n\nadd_executable(${PROJECT_NAME} main.cc)\n\nfind_package(Drogon CONFIG REQUIRED)\ntarget_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)\n\naux_source_directory(controllers CTL_SRC)\naux_source_directory(filters FILTER_SRC)\ntarget_include_directories(${PROJECT_NAME}\n    PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})\ntarget_sources(${PROJECT_NAME}\n    PRIVATE\n    ${SRC_DIR}\n    ${CTL_SRC}\n    ${FILTER_SRC})\n\nadd_subdirectory(test)\n"
  },
  {
    "path": "examples/redis_cache/DateFuncs.h",
    "content": "#pragma once\n#include \"RedisCache.h\"\n\ntemplate <>\ninline trantor::Date fromString<trantor::Date>(const std::string &str)\n{\n    return trantor::Date(std::atoll(str.data()));\n}\n\ntemplate <>\ninline std::string toString<trantor::Date>(const trantor::Date &date)\n{\n    return std::to_string(date.microSecondsSinceEpoch());\n}\n"
  },
  {
    "path": "examples/redis_cache/README.md",
    "content": "# Redis example\n\nAn example for using coroutines of redis clients. This example is a redis implementation of\nthe [local sessions example](https://github.com/drogonframework/drogon/wiki/ENG-07-Session#examples-of-sessions) on wiki.\n\n## Usage\n\nFirst of all you need redis running on the port 6379;\n\nPlease use new compilers that support coroutines. Configure the project with the following\ncommand:\n\n```shell\ncmake .. -DCMAKE_CXX_FLAGS=\"-std=c++20 -fcoroutines\" #for GCC 10\ncmake .. -DCMAKE_CXX_FLAGS=\"-std=c++20\" #for GCC >= 11\ncmake .. -DCMAKE_CXX_FLAGS=\"/std:c++20\" #for MSVC >= 16.25\n```\n"
  },
  {
    "path": "examples/redis_cache/RedisCache.h",
    "content": "#pragma once\n#include <drogon/utils/coroutine.h>\n#include <drogon/nosql/RedisClient.h>\n\ntemplate <typename T>\nT fromString(const std::string &);\n\ntemplate <typename T>\nstd::string toString(const T &);\n\ntemplate <typename T>\ndrogon::Task<T> getFromCache(\n    const std::string &key,\n    const drogon::nosql::RedisClientPtr &client) noexcept(false)\n{\n    auto value = co_await client->execCommandCoro(\"get %s\", key.data());\n    co_return fromString<T>(value.asString());\n}\n\ntemplate <typename T>\ndrogon::Task<> updateCache(\n    const std::string &key,\n    T &&value,\n    const drogon::nosql::RedisClientPtr &client) noexcept(false)\n{\n    std::string vstr = toString(std::forward<T>(value));\n    co_await client->execCommandCoro(\"set %s %s\", key.data(), vstr.data());\n}\n"
  },
  {
    "path": "examples/redis_cache/config.json",
    "content": "/* This is a JSON format configuration file\n */\n{\n    \"listeners\": [\n        {\n            //address: Ip address,0.0.0.0 by default\n            \"address\": \"0.0.0.0\",\n            //port: Port number\n            \"port\": 8848,\n            //https: If true, use https for security,false by default\n            \"https\": false\n        }\n    ],\n    \"redis_clients\": [\n        {\n            //name: Name of the client,'default' by default\n            //\"name\":\"\",\n            //host: Server IP, 127.0.0.1 by default\n            \"host\": \"127.0.0.1\",\n            //port: Server port, 6379 by default\n            \"port\": 6379,\n            //passwd: '' by default\n            \"passwd\": \"\",\n            //db index: 0 by default\n            \"db\": 0,\n            //is_fast: false by default, if it is true, the client is faster but user can't call\n            //any synchronous interface of it.\n            \"is_fast\": true,\n            //number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of  \n            //connections per IO thread, otherwise it is the total number of all connections.  \n            \"number_of_connections\": 1,\n            //timeout: -1.0 by default, in seconds, the timeout for executing a command.\n            //zero or negative value means no timeout.\n            \"timeout\": -1.0\n        }\n    ],\n    \"app\": {\n        //number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads\n        //is the number of CPU cores\n        \"number_of_threads\": 1,\n        //enable_session: False by default\n        \"enable_session\": false,\n        \"session_timeout\": 0,\n        //session_cookie_key: The cookie key of the session, \"JSESSIONID\" by default\n        \"session_cookie_key\": \"JSESSIONID\",\n        //session_max_age: The max age of the session cookie, -1 by default\n        \"session_max_age\": -1,\n        //document_root: Root path of HTTP document, default path is ./\n        \"document_root\": \"./\",\n        //home_page: Set the HTML file of the home page, the default value is \"index.html\"\n        //If there isn't any handler registered to the path \"/\", the home page file in the \"document_root\" is send to clients as a response\n        //to the request for \"/\".\n        \"home_page\": \"index.html\",\n        //use_implicit_page: enable implicit pages if true, true by default\n        \"use_implicit_page\": true,\n        //implicit_page: Set the file which would the server access in a directory that a user accessed.\n        //For example, by default, http://localhost/a-directory resolves to http://localhost/a-directory/index.html.\n        \"implicit_page\": \"index.html\",\n        //static_file_headers: Headers for static files\n        /*\"static_file_headers\": [\n          {\n                \"name\": \"field-name\",\n                \"value\": \"field-value\"\n          }\n        ],*/\n        //upload_path: The path to save the uploaded file. \"uploads\" by default. \n        //If the path isn't prefixed with /, ./ or ../,\n        //it is relative path of document_root path\n        \"upload_path\": \"uploads\",\n        /* file_types:\n         * HTTP download file types,The file types supported by drogon\n         * by default are \"html\", \"js\", \"css\", \"xml\", \"xsl\", \"txt\", \"svg\",\n         * \"ttf\", \"otf\", \"woff2\", \"woff\" , \"eot\", \"png\", \"jpg\", \"jpeg\",\n         * \"gif\", \"bmp\", \"ico\", \"icns\", etc. */\n        \"file_types\": [\n            \"gif\",\n            \"png\",\n            \"jpg\",\n            \"js\",\n            \"css\",\n            \"html\",\n            \"ico\",\n            \"swf\",\n            \"xap\",\n            \"apk\",\n            \"cur\",\n            \"xml\"\n        ],\n        //locations: An array of locations of static files for GET requests.\n        \"locations\": [\n            {\n                //uri_prefix: The URI prefix of the location prefixed with \"/\", the default value is \"\" that disables the location.\n                //\"uri_prefix\": \"/.well-known/acme-challenge/\",\n                //default_content_type: The default content type of the static files without\n                //an extension. empty string by default.\n                \"default_content_type\": \"text/plain\",\n                //alias: The location in file system, if it is prefixed with \"/\", it \n                //presents an absolute path, otherwise it presents a relative path to \n                //the document_root path. \n                //The default value is \"\" which means use the document root path as the location base path.\n                \"alias\": \"\",\n                //is_case_sensitive: indicates whether the URI prefix is case sensitive.\n                \"is_case_sensitive\": false,\n                //allow_all: true by default. If it is set to false, only static files with a valid extension can be accessed.\n                \"allow_all\": true,\n                //is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.\n                \"is_recursive\": true,\n                //filters: string array, the filters applied to the location.\n                \"filters\": []\n            }\n        ],\n        //max_connections: maximum number of connections, 100000 by default\n        \"max_connections\": 100000,\n        //max_connections_per_ip: maximum number of connections per client, 0 by default which means no limit\n        \"max_connections_per_ip\": 0,\n        //Load_dynamic_views: False by default, when set to true, drogon\n        //compiles and loads dynamically \"CSP View Files\" in directories defined\n        //by \"dynamic_views_path\"\n        \"load_dynamic_views\": false,\n        //dynamic_views_path: If the path isn't prefixed with /, ./ or ../,\n        //it is relative path of document_root path\n        \"dynamic_views_path\": [\n            \"./views\"\n        ],\n        //dynamic_views_output_path: Default by an empty string which means the output path of source \n        //files is the path where the csp files locate. If the path isn't prefixed with /, it is relative \n        //path of the current working directory.\n        \"dynamic_views_output_path\": \"\",\n        //enable_unicode_escaping_in_json: true by default, enable unicode escaping in json.\n        \"enable_unicode_escaping_in_json\": true,\n        //float_precision_in_json: set precision of float number in json. \n        \"float_precision_in_json\": {\n            //precision: 0 by default, 0 means use the default precision of the jsoncpp lib. \n            \"precision\": 0,\n            //precision_type: must be \"significant\" or \"decimal\", defaults to \"significant\" that means \n            //setting max number of significant digits in string, \"decimal\" means setting max number of \n            //digits after \".\" in string\n            \"precision_type\": \"significant\"\n        },\n        //log: Set log output, drogon output logs to stdout by default\n        \"log\": {\n            //log_path: Log file path,empty by default,in which case,logs are output to the stdout\n            //\"log_path\": \"./\",\n            //logfile_base_name: Log file base name,empty by default which means drogon names logfile as\n            //drogon.log ...\n            \"logfile_base_name\": \"\",\n            //log_size_limit: 100000000 bytes by default,\n            //When the log file size reaches \"log_size_limit\", the log file is switched.\n            \"log_size_limit\": 100000000,\n            //log_level: \"DEBUG\" by default,options:\"TRACE\",\"DEBUG\",\"INFO\",\"WARN\"\n            //The TRACE level is only valid when built in DEBUG mode.\n            \"log_level\": \"DEBUG\"\n        },\n        //run_as_daemon: False by default\n        \"run_as_daemon\": false,\n        //handle_sig_term: True by default\n        \"handle_sig_term\": true,\n        //relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;\n        \"relaunch_on_error\": false,\n        //use_sendfile: True by default, if true, the program \n        //uses sendfile() system-call to send static files to clients;\n        \"use_sendfile\": true,\n        //use_gzip: True by default, use gzip to compress the response body's content;\n        \"use_gzip\": true,\n        //use_brotli: False by default, use brotli to compress the response body's content;\n        \"use_brotli\": false,\n        //static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,\n        //0 means cache forever, the negative value means no cache\n        \"static_files_cache_time\": 5,\n        //simple_controllers_map: Used to configure mapping from path to simple controller\n        \"simple_controllers_map\": [\n        ],\n        //idle_connection_timeout: Defaults to 60 seconds, the lifetime \n        //of the connection without read or write\n        \"idle_connection_timeout\": 60,\n        //server_header_field: Set the 'Server' header field in each response sent by drogon,\n        //empty string by default with which the 'Server' header field is set to \"Server: drogon/version string\\r\\n\"\n        \"server_header_field\": \"\",\n        //enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default \n        //value is true.\n        \"enable_server_header\": true,\n        //enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default \n        //value is true.\n        \"enable_date_header\": true,\n        //keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection. \n        //After the maximum number of requests are made, the connection is closed.\n        //The default value of 0 means no limit.\n        \"keepalive_requests\": 0,\n        //pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer. \n        //After the maximum number of requests are made, the connection is closed.\n        //The default value of 0 means no limit.\n        \"pipelining_requests\": 0,\n        //gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed \n        //file with the extension \".gz\" in the same path and send the compressed file to the client.\n        //The default value of gzip_static is true.\n        \"gzip_static\": true,\n        //br_static: If it is set to true, when the client requests a static file, drogon first finds the compressed \n        //file with the extension \".br\" in the same path and send the compressed file to the client.\n        //The default value of br_static is true.\n        \"br_static\": true,\n        //client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is \"1M\".\n        //One can set it to \"1024\", \"1k\", \"10M\", \"1G\", etc. Setting it to \"\" means no limit.\n        \"client_max_body_size\": \"1M\",\n        //max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is \"64K\" bytes.\n        //If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.\n        //Setting it to \"\" means no limit.\n        \"client_max_memory_body_size\": \"64K\",\n        //client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is \"128K\".\n        //One can set it to \"1024\", \"1k\", \"10M\", \"1G\", etc. Setting it to \"\" means no limit.\n        \"client_max_websocket_message_size\": \"128K\",\n        //reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time.\n        \"reuse_port\": false\n    },\n    //plugins: Define all plugins running in the application\n    \"plugins\": [\n        {\n            //name: The class name of the plugin\n            //\"name\": \"drogon::plugin::SecureSSLRedirector\",\n            //dependencies: Plugins that the plugin depends on. It can be commented out\n            \"dependencies\": [],\n            //config: The configuration of the plugin. This json object is the parameter to initialize the plugin.\n            //It can be commented out\n            \"config\": {\n                \"ssl_redirect_exempt\": [\n                    \".*\\\\.jpg\"\n                ],\n                \"secure_ssl_host\": \"localhost:8849\"\n            }\n        }\n    ],\n    //custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method. \n    \"custom_config\": {}\n}\n"
  },
  {
    "path": "examples/redis_cache/controllers/SlowCtrl.cc",
    "content": "#include \"SlowCtrl.h\"\n#include \"RedisCache.h\"\n#include \"DateFuncs.h\"\n#include <drogon/drogon.h>\n#define VDate \"visitDate\"\n\nvoid SlowCtrl::hello(const HttpRequestPtr &req,\n                     std::function<void(const HttpResponsePtr &)> &&callback,\n                     std::string &&userid)\n{\n    auto resp = drogon::HttpResponse::newHttpResponse();\n    resp->setBody(\"hello, \" + userid);\n    callback(resp);\n}\n\ndrogon::AsyncTask SlowCtrl::observe(\n    HttpRequestPtr req,\n    std::function<void(const HttpResponsePtr &)> callback,\n    std::string userid)\n{\n    auto key = userid + \".\" + VDate;\n    auto redisPtr = drogon::app().getFastRedisClient();\n    try\n    {\n        auto date = co_await getFromCache<trantor::Date>(key, redisPtr);\n        auto resp = drogon::HttpResponse::newHttpResponse();\n        std::string body{\"your last visit time: \"};\n        body.append(date.toFormattedStringLocal(false));\n        resp->setBody(std::move(body));\n        callback(resp);\n    }\n    catch (const std::exception &err)\n    {\n        LOG_ERROR << err.what();\n        callback(drogon::HttpResponse::newNotFoundResponse());\n    }\n}\n"
  },
  {
    "path": "examples/redis_cache/controllers/SlowCtrl.h",
    "content": "#pragma once\n#include <drogon/HttpController.h>\nusing namespace drogon;\n\nclass SlowCtrl : public drogon::HttpController<SlowCtrl>\n{\n  public:\n    METHOD_LIST_BEGIN\n    ADD_METHOD_TO(SlowCtrl::hello, \"/slow?userid={userid}\", Get, \"TimeFilter\");\n    ADD_METHOD_TO(SlowCtrl::observe, \"/observe?userid={userid}\", Get);\n    METHOD_LIST_END\n\n    void hello(const HttpRequestPtr &req,\n               std::function<void(const HttpResponsePtr &)> &&callback,\n               std::string &&userid);\n    drogon::AsyncTask observe(\n        HttpRequestPtr req,\n        std::function<void(const HttpResponsePtr &)> callback,\n        std::string userid);\n};\n"
  },
  {
    "path": "examples/redis_cache/filters/TimeFilter.cc",
    "content": "//\n// Created by antao on 2018/5/22.\n//\n\n#include \"TimeFilter.h\"\n#include <drogon/utils/coroutine.h>\n#include <drogon/drogon.h>\n#include <string>\n#include \"RedisCache.h\"\n#include \"DateFuncs.h\"\n#define VDate \"visitDate\"\n\nvoid TimeFilter::doFilter(const HttpRequestPtr &req,\n                          FilterCallback &&cb,\n                          FilterChainCallback &&ccb)\n{\n    drogon::async_run([req,\n                       cb = std::move(cb),\n                       ccb = std::move(ccb)]() -> drogon::Task<> {\n        auto userid = req->getParameter(\"userid\");\n        if (!userid.empty())\n        {\n            auto key = userid + \".\" + VDate;\n            const trantor::Date now = trantor::Date::date();\n            auto redisClient = drogon::app().getFastRedisClient();\n            try\n            {\n                auto lastDate =\n                    co_await getFromCache<trantor::Date>(key, redisClient);\n                LOG_TRACE << \"last:\" << lastDate.toFormattedString(false);\n                co_await updateCache(key, now, redisClient);\n                if (now > lastDate.after(10))\n                {\n                    // 10 sec later can visit again;\n                    ccb();\n                    co_return;\n                }\n                else\n                {\n                    Json::Value json;\n                    json[\"result\"] = \"error\";\n                    json[\"message\"] =\n                        \"Access interval should be at least 10 seconds\";\n                    auto res = HttpResponse::newHttpJsonResponse(json);\n                    cb(res);\n                    co_return;\n                }\n            }\n            catch (const std::exception &err)\n            {\n                LOG_TRACE << \"first visit,insert visitDate\";\n                try\n                {\n                    co_await updateCache(userid + \".\" VDate, now, redisClient);\n                }\n                catch (const std::exception &err)\n                {\n                    LOG_ERROR << err.what();\n                    cb(HttpResponse::newHttpJsonResponse(err.what()));\n                    co_return;\n                }\n                ccb();\n                co_return;\n            }\n        }\n        else\n        {\n            auto resp = HttpResponse::newNotFoundResponse();\n            cb(resp);\n            co_return;\n        }\n    });\n}\n"
  },
  {
    "path": "examples/redis_cache/filters/TimeFilter.h",
    "content": "//\n// Created by antao on 2018/5/22.\n//\n\n#pragma once\n\n#include <drogon/HttpFilter.h>\nusing namespace drogon;\n\nclass TimeFilter : public drogon::HttpFilter<TimeFilter>\n{\n  public:\n    void doFilter(const HttpRequestPtr &req,\n                  FilterCallback &&cb,\n                  FilterChainCallback &&ccb) override;\n\n    TimeFilter()\n    {\n        LOG_DEBUG << \"TimeFilter constructor\";\n    }\n};\n"
  },
  {
    "path": "examples/redis_cache/main.cc",
    "content": "#include <drogon/drogon.h>\n\nint main()\n{\n    drogon::app().loadConfigFile(\"../config.json\");\n    drogon::app().run();\n    return 0;\n}\n"
  },
  {
    "path": "examples/redis_cache/test/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.5)\nproject(redis_cache_test CXX)\n\nadd_executable(${PROJECT_NAME} test_main.cc)\n\n# ##############################################################################\n# If you include the drogon source code locally in your project, use this method\n# to add drogon \n# target_link_libraries(${PROJECT_NAME}_test PRIVATE drogon)\n#\n# and comment out the following lines\ntarget_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)\n\nParseAndAddDrogonTests(${PROJECT_NAME})\n"
  },
  {
    "path": "examples/redis_cache/test/test_main.cc",
    "content": "#define DROGON_TEST_MAIN\n#include <drogon/drogon_test.h>\n#include <drogon/drogon.h>\n\nDROGON_TEST(BasicTest)\n{\n    // Add your tests here\n}\n\nint main(int argc, char **argv)\n{\n    using namespace drogon;\n\n    std::promise<void> p1;\n    std::future<void> f1 = p1.get_future();\n\n    // Start the main loop on another thread\n    std::thread thr([&]() {\n        // Queues the promise to be fulfilled after starting the loop\n        app().getLoop()->queueInLoop([&p1]() { p1.set_value(); });\n        app().run();\n    });\n\n    // The future is only satisfied after the event loop started\n    f1.get();\n    int status = test::run(argc, argv);\n\n    // Ask the event loop to shutdown and wait\n    app().getLoop()->queueInLoop([]() { app().quit(); });\n    thr.join();\n    return status;\n}\n"
  },
  {
    "path": "examples/redis_chat/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.5)\nproject(redis_chat CXX)\n\ninclude(CheckIncludeFileCXX)\n\ncheck_include_file_cxx(any HAS_ANY)\ncheck_include_file_cxx(string_view HAS_STRING_VIEW)\ncheck_include_file_cxx(coroutine HAS_COROUTINE)\nif (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)\n    set(CMAKE_CXX_STANDARD 20)\nelseif (HAS_ANY AND HAS_STRING_VIEW)\n    set(CMAKE_CXX_STANDARD 17)\nelse ()\n    set(CMAKE_CXX_STANDARD 14)\nendif ()\n\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nset(CMAKE_CXX_EXTENSIONS OFF)\n\nadd_executable(${PROJECT_NAME} main.cc)\n\n# ##############################################################################\n# If you include the drogon source code locally in your project, use this method\n# to add drogon\n# add_subdirectory(drogon)\n# target_link_libraries(${PROJECT_NAME} PRIVATE drogon)\n#\n# and comment out the following lines\nfind_package(Drogon CONFIG REQUIRED)\ntarget_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)\n\n# ##############################################################################\n\nif (CMAKE_CXX_STANDARD LESS 17)\n    # With C++14, use boost to support any and std::string_view\n    message(STATUS \"use c++14\")\n    find_package(Boost 1.61.0 REQUIRED)\n    target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS})\nelseif (CMAKE_CXX_STANDARD LESS 20)\n    message(STATUS \"use c++17\")\nelse ()\n    message(STATUS \"use c++20\")\nendif ()\n\naux_source_directory(controllers CTL_SRC)\naux_source_directory(filters FILTER_SRC)\naux_source_directory(plugins PLUGIN_SRC)\naux_source_directory(models MODEL_SRC)\n\ndrogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views\n                    ${CMAKE_CURRENT_BINARY_DIR})\n# use the following line to create views with namespaces.\n# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views\n#                     ${CMAKE_CURRENT_BINARY_DIR} TRUE)\n\ntarget_include_directories(${PROJECT_NAME}\n                           PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}\n                                   ${CMAKE_CURRENT_SOURCE_DIR}/models)\ntarget_sources(${PROJECT_NAME}\n               PRIVATE\n               ${SRC_DIR}\n               ${CTL_SRC}\n               ${FILTER_SRC}\n               ${PLUGIN_SRC}\n               ${MODEL_SRC})\n# ##############################################################################\n# uncomment the following line for dynamically loading views\n# set_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS ON)\n"
  },
  {
    "path": "examples/redis_chat/README.md",
    "content": "# Redis example\n\nA simple redis chat server\n\n## Usage\n\nFirst you need redis running on the port 6379.\n\n### Connect with username\n\nws://localhost:8080/chat?name=<your-name>\n\n### Enter room\n\nSend message `ENTER <roomNo>` to enter a room.\n\nroom number should be 0 - 99.\n\n### Quit room\n\nSend message `QUIT` to quit room.\n\n### Send message\n\nSend whatever else will be a message."
  },
  {
    "path": "examples/redis_chat/controllers/Chat.cc",
    "content": "#include \"Chat.h\"\n\n#include <memory>\n#include <unordered_set>\n\nstatic void redisLogin(std::function<void(int)> &&callback,\n                       const std::string &loginKey,\n                       unsigned int timeout);\nstatic void redisLogout(std::function<void(int)> &&callback,\n                        const std::string &loginKey);\n\nstruct ClientContext\n{\n    std::string name_;\n    std::string loginKey_;\n    std::string room_;\n    std::shared_ptr<nosql::RedisSubscriber> subscriber_;\n};\n\nstatic bool checkRoomNumber(const std::string &room)\n{\n    if (room.empty() || room.size() > 2 || (room.size() == 2 && room[0] == '0'))\n    {\n        return false;\n    }\n    for (char c : room)\n    {\n        if (c < '0' || c > '9')\n        {\n            return false;\n        }\n    }\n    return true;\n}\n\nvoid Chat::handleNewMessage(const WebSocketConnectionPtr &wsConnPtr,\n                            std::string &&message,\n                            const WebSocketMessageType &type)\n{\n    if (type == WebSocketMessageType::Close ||\n        type == WebSocketMessageType::Ping)\n    {\n        return;\n    }\n    if (type != WebSocketMessageType::Text &&\n        type != WebSocketMessageType::Pong)\n    {\n        LOG_ERROR << \"Unsupported message type \" << (int)type;\n        return;\n    }\n    LOG_DEBUG << \"WsClient new message from \"\n              << wsConnPtr->peerAddr().toIpPort();\n\n    auto context = wsConnPtr->getContext<ClientContext>();\n    if (!context || context->name_.empty())\n    {\n        wsConnPtr->send(\"ERROR: You are not logged in.\");\n        wsConnPtr->forceClose();\n        return;\n    }\n\n    auto redisClient = drogon::app().getRedisClient();\n    if (type == WebSocketMessageType::Pong)\n    {\n        redisClient->execCommandAsync(\n            [wsConnPtr](const nosql::RedisResult &) {\n                // Do nothing\n            },\n            [wsConnPtr](const nosql::RedisException &ex) {\n                LOG_ERROR << \"Update user status failed: \" << ex.what();\n                wsConnPtr->send(\"ERROR: Service unavailable.\");\n                wsConnPtr->forceClose();\n            },\n            \"SET %s 1 EX 120\",\n            context->loginKey_.c_str());\n        return;\n    }\n\n    int operation = 0;\n    std::string room;\n    if (message.compare(0, 6, \"ENTER \") == 0)\n    {\n        room = message.substr(6, message.find_last_not_of(\" \\n\") - 5);\n        if (!checkRoomNumber(room))\n        {\n            wsConnPtr->send(\"ERROR: Invalid room number, should be [0-99].\");\n            return;\n        }\n        operation = 1;\n    }\n    else if (message == \"QUIT\")\n    {\n        operation = 2;\n    }\n\n    switch (operation)\n    {\n        case 0:  // Message\n        {\n            if (context->room_.empty())\n            {\n                wsConnPtr->send(\n                    \"ERROR: Not in room. Send 'ENTER roomNo' to enter a \"\n                    \"room first.\");\n                return;\n            }\n            // Publish message\n            std::string msg =\n                \"[\" + context->room_ + \"][\" + context->name_ + \"] \" + message;\n\n            // NOTICE: Dangerous to concat username into redis command!!!\n            // Do not use in production.\n            redisClient->execCommandAsync(\n                [](const nosql::RedisResult &) {},\n                [wsConnPtr](const nosql::RedisException &ex) {\n                    wsConnPtr->send(std::string(\"ERROR: \") + ex.what());\n                },\n                \"publish %s %s\",\n                context->room_.c_str(),\n                msg.c_str());\n            break;\n        }\n        case 1:  // Enter room\n        {\n            if (context->room_ == room)\n            {\n                wsConnPtr->send(\"ERROR: Already in room \" + context->room_);\n                return;\n            }\n            if (!context->room_.empty())\n            {\n                context->subscriber_->unsubscribe(context->room_);\n            }\n            wsConnPtr->send(\"INFO: Enter room \" + room);\n            context->subscriber_->subscribe(\n                room, [wsConnPtr](const std::string &, const std::string &msg) {\n                    wsConnPtr->send(msg);\n                });\n            context->room_ = room;\n            break;\n        }\n        case 2:  // Quit room\n        {\n            if (!context->room_.empty())\n            {\n                context->subscriber_->unsubscribe(context->room_);\n                wsConnPtr->send(\"INFO: Quit room \" + context->room_);\n                context->room_.clear();\n            }\n            else\n            {\n                wsConnPtr->send(\n                    \"ERROR: Not in room. Send 'ENTER roomNo' to enter a \"\n                    \"room first.\");\n            }\n            break;\n        }\n        default:\n            break;\n    }\n}\n\nvoid Chat::handleNewConnection(const HttpRequestPtr &req,\n                               const WebSocketConnectionPtr &wsConnPtr)\n{\n    LOG_DEBUG << \"WsClient new connection from \"\n              << wsConnPtr->peerAddr().toIpPort();\n    const std::string name = req->getParameter(\"name\");\n    if (name.empty())\n    {\n        wsConnPtr->send(\"Please give your name in parameters.\");\n        wsConnPtr->forceClose();\n    }\n\n    std::string loginKey = \"redis_chat:user:\" + drogon::utils::getMd5(name);\n    redisLogin(\n        [wsConnPtr, name, loginKey](int status) {\n            if (status < 0)\n            {\n                wsConnPtr->send(\"ERROR: Service unavailable.\");\n                wsConnPtr->shutdown();\n                return;\n            }\n            if (status == 0)\n            {\n                wsConnPtr->send(\"ERROR: User [\" + name +\n                                \"] already logged in.\");\n                wsConnPtr->shutdown();\n                return;\n            }\n            std::shared_ptr<ClientContext> context =\n                std::make_shared<ClientContext>();\n            context->subscriber_ =\n                drogon::app().getRedisClient()->newSubscriber();\n            context->name_ = name;\n            context->loginKey_ = loginKey;\n            wsConnPtr->setContext(context);\n            wsConnPtr->send(\"Hello, \" + name + \"!\");\n        },\n        loginKey,\n        120);\n}\n\nvoid Chat::handleConnectionClosed(const WebSocketConnectionPtr &wsConnPtr)\n{\n    LOG_DEBUG << \"WsClient close connection from \"\n              << wsConnPtr->peerAddr().toIpPort();\n    auto context = wsConnPtr->getContext<ClientContext>();\n    // Channels will be auto unsubscribed when subscriber destructed.\n    // for (auto& channel : context->channels_)\n    // {\n    //     context->subscriber_->unsubscribe(channel);\n    // }\n    if (context)\n    {\n        redisLogout([](int) {}, context->loginKey_);\n    }\n}\n\nstatic void redisLogin(std::function<void(int)> &&callback,\n                       const std::string &loginKey,\n                       unsigned int timeout)\n{\n    static const char script[] = R\"(\nlocal exists = redis.call('GET', KEYS[1]);\nif exists then return 0 end;\nredis.call('SET', KEYS[1], 1, 'EX', ARGV[1]);\nreturn 1;\n)\";\n\n    drogon::app().getRedisClient()->execCommandAsync(\n        [callback](const nosql::RedisResult &result) {\n            callback((int)result.asInteger());\n        },\n        [callback](const nosql::RedisException &ex) {\n            LOG_ERROR << \"Login error: \" << ex.what();\n            callback(-1);\n        },\n        \"EVAL %s 1 %s %u\",\n        script,\n        loginKey.c_str(),\n        timeout);\n}\n\nstatic void redisLogout(std::function<void(int)> &&callback,\n                        const std::string &loginKey)\n{\n    drogon::app().getRedisClient()->execCommandAsync(\n        [callback](const nosql::RedisResult &result) {\n            callback((int)result.asInteger());\n        },\n        [callback](const nosql::RedisException &ex) {\n            LOG_ERROR << \"Logout error: \" << ex.what();\n            callback(-1);\n        },\n        \"DEL %s\",\n        loginKey.c_str());\n}\n"
  },
  {
    "path": "examples/redis_chat/controllers/Chat.h",
    "content": "#pragma once\n\n#include <drogon/WebSocketController.h>\n\nusing namespace drogon;\n\nclass Chat : public drogon::WebSocketController<Chat>\n{\n  public:\n    void handleNewMessage(const WebSocketConnectionPtr &,\n                          std::string &&,\n                          const WebSocketMessageType &) override;\n    void handleNewConnection(const HttpRequestPtr &,\n                             const WebSocketConnectionPtr &) override;\n    void handleConnectionClosed(const WebSocketConnectionPtr &) override;\n    WS_PATH_LIST_BEGIN\n    WS_PATH_ADD(\"/chat\");\n    WS_PATH_LIST_END\n};\n"
  },
  {
    "path": "examples/redis_chat/main.cc",
    "content": "#include <drogon/drogon.h>\n\nint main()\n{\n    drogon::app().addListener(\"0.0.0.0\", 8080);\n    drogon::app().createRedisClient(\"127.0.0.1\", 6379);\n    drogon::app().run();\n    return 0;\n}\n"
  },
  {
    "path": "examples/simple_reverse_proxy/.gitignore",
    "content": "# Prerequisites\n*.d\n\n# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n*.dll\n\n# Fortran module files\n*.mod\n*.smod\n\n# Compiled Static libraries\n*.lai\n*.la\n*.a\n*.lib\n\n# Executables\n*.exe\n*.out\n*.app\n\nbuild\ncmake-build-debug\n.idea\n"
  },
  {
    "path": "examples/simple_reverse_proxy/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 3.5)\nproject(simple_reverse_proxy CXX)\n\ninclude(CheckIncludeFileCXX)\n\ncheck_include_file_cxx(any HAS_ANY)\ncheck_include_file_cxx(string_view HAS_STRING_VIEW)\nif(HAS_ANY AND HAS_STRING_VIEW)\n    set(CMAKE_CXX_STANDARD 17)\nelse()\n    set(CMAKE_CXX_STANDARD 14)\nendif()\n\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nset(CMAKE_CXX_EXTENSIONS OFF)\n\nadd_executable(simple_reverse_proxy main.cc)\n\n##########\n# If you include the drogon source code locally in your project, use this method to add drogon\n# add_subdirectory(drogon)\n# target_link_libraries(simple_reverse_proxy PRIVATE drogon)\n##########\n\nfind_package(Drogon CONFIG REQUIRED)\ntarget_link_libraries(simple_reverse_proxy PRIVATE Drogon::Drogon)\n\nif(CMAKE_CXX_STANDARD LESS 17)\n#With C++14, use boost to support any and string_view\n    message(STATUS \"use c++14\")\n    find_package(Boost 1.61.0 REQUIRED)\n    target_include_directories(simple_reverse_proxy PRIVATE ${Boost_INCLUDE_DIRS})\nelse()\n    message(STATUS \"use c++17\")\nendif()\n\naux_source_directory(controllers CTL_SRC)\naux_source_directory(filters FILTER_SRC)\naux_source_directory(plugins PLUGIN_SRC)\naux_source_directory(models MODEL_SRC)\n\nfile(GLOB SCP_LIST ${CMAKE_CURRENT_SOURCE_DIR}/views/*.csp)\nforeach(cspFile ${SCP_LIST})\n    message(STATUS \"cspFile:\" ${cspFile})\n    EXEC_PROGRAM(basename ARGS \"${cspFile} .csp\" OUTPUT_VARIABLE classname)\n    message(STATUS \"view classname:\" ${classname})\n    ADD_CUSTOM_COMMAND(OUTPUT ${classname}.h ${classname}.cc\n            COMMAND drogon_ctl\n            ARGS create view ${cspFile}\n            DEPENDS ${cspFile}\n            VERBATIM )\n    set(VIEWSRC ${VIEWSRC} ${classname}.cc)\nendforeach()\n\ntarget_include_directories(simple_reverse_proxy PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models)\ntarget_sources(simple_reverse_proxy PRIVATE ${SRC_DIR} ${CTL_SRC} ${FILTER_SRC} ${VIEWSRC} ${PLUGIN_SRC} ${MODEL_SRC})\n"
  },
  {
    "path": "examples/simple_reverse_proxy/README.md",
    "content": "### An example that shows how to use drogon as an http reverse proxy with a simple round robin.\n\nThis project is created with the drogon_ctl command, please compile it after installing drogon."
  },
  {
    "path": "examples/simple_reverse_proxy/config.json",
    "content": "/* This is a JSON format configuration file\n */\n{\n    /*\n    //ssl: The global ssl files setting\n    \"ssl\": {\n        \"cert\": \"../../trantor/trantor/tests/server.crt\",\n        \"key\": \"../../trantor/trantor/tests/server.key\"\n    },*/\n    \"listeners\": [{\n        //address: Ip address,0.0.0.0 by default\n        \"address\": \"0.0.0.0\",\n        //port: Port number\n        \"port\": 8088,\n        //https: If true, use https for security, false by default\n        \"https\": false\n    }],\n    \"app\": {\n        //threads_num: The number of IO threads, 1 by default, if the value is set to 0, the number of threads\n        //is the number of CPU cores\n        \"threads_num\": 2,\n        //enable_session: False by default\n        \"enable_session\": false,\n        \"session_timeout\": 0,\n        //session_cookie_key: The cookie key of the session, \"JSESSIONID\" by default\n        \"session_cookie_key\": \"JSESSIONID\",\n        //session_max_age: The max age of the session cookie, -1 by default\n        \"session_max_age\": -1,\n        //document_root: Root path of HTTP document, default path is ./\n        \"document_root\": \"./\",\n        //home_page: Set the HTML file of the home page, the default value is \"index.html\"\n        //If there isn't any handler registered to the path \"/\", the home page file in the \"document_root\" is send to clients as a response\n        //to the request for \"/\".\n        \"home_page\": \"index.html\",\n        //static_file_headers: Headers for static files\n        /*\"static_file_headers\": [\n          {\n                \"name\": \"field-name\",\n                \"value\": \"field-value\"\n          }\n        ],*/\n        //upload_path: The path to save the uploaded file. \"uploads\" by default. \n        //If the path isn't prefixed with /, ./ or ../,\n        //it is relative path of document_root path\n        \"upload_path\": \"uploads\",\n        /* file_types:\n         * HTTP download file types, the file types supported by drogon\n         * by default are \"html\", \"js\", \"css\", \"xml\", \"xsl\", \"txt\", \"svg\",\n         * \"ttf\", \"otf\", \"woff2\", \"woff\" , \"eot\", \"png\", \"jpg\", \"jpeg\",\n         * \"gif\", \"bmp\", \"ico\", \"icns\", etc. */\n        \"file_types\": [\n            \"gif\",\n            \"png\",\n            \"jpg\",\n            \"js\",\n            \"css\",\n            \"html\",\n            \"ico\",\n            \"swf\",\n            \"xap\",\n            \"apk\",\n            \"cur\",\n            \"xml\"\n        ],\n        //max_connections: maximum connections number, 100000 by default\n        \"max_connections\": 100000,\n        //max_connections_per_ip: maximum connections number per client,0 by default which means no limit\n        \"max_connections_per_ip\": 0,\n        //Load_dynamic_views: False by default, when set to true, drogon\n        //compiles and loads dynamically \"CSP View Files\" in directories defined\n        //by \"dynamic_views_path\"\n        \"load_dynamic_views\": false,\n        //dynamic_views_path: If the path isn't prefixed with /, ./ or ../,\n        //it is relative path of document_root path\n        \"dynamic_views_path\": [\n            \"./views\"\n        ],\n        //log: Set log output, drogon output logs to stdout by default\n        \"log\": {\n            //log_path: Log file path,empty by default,in which case,logs are output to the stdout\n            //\"log_path\": \"./\",\n            //logfile_base_name: Log file base name,empty by default which means drogon names logfile as\n            //drogon.log ...\n            \"logfile_base_name\": \"\",\n            //log_size_limit: 100000000 bytes by default,\n            //When the log file size reaches \"log_size_limit\", the log file is switched.\n            \"log_size_limit\": 100000000,\n            //log_level: \"DEBUG\" by default,options:\"TRACE\",\"DEBUG\",\"INFO\",\"WARN\"\n            //The TRACE level is only valid when built in DEBUG mode.\n            \"log_level\": \"DEBUG\"\n        },\n        //run_as_daemon: False by default\n        \"run_as_daemon\": false,\n        //handle_sig_term: True by default\n        \"handle_sig_term\": true,\n        //relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;\n        \"relaunch_on_error\": false,\n        //use_sendfile: True by default, if true, the program \n        //uses sendfile() system-call to send static files to clients;\n        \"use_sendfile\": true,\n        //use_gzip: True by default, use gzip to compress the response body's content;\n        \"use_gzip\": true,\n        //static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,\n        //0 means cache forever, the negative value means no cache\n        \"static_files_cache_time\": 5,\n        //idle_connection_timeout: Defaults to 60 seconds, the lifetime \n        //of the connection without read or write\n        \"idle_connection_timeout\": 60,\n        //server_header_field: Set the 'Server' header field in each response sent by drogon,\n        //empty string by default with which the 'Server' header field is set to \"Server: drogon/version string\\r\\n\"\n        \"server_header_field\": \"\",\n        //enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default \n        //value is true.\n        \"enable_server_header\": false,\n        //enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default \n        //value is true.\n        \"enable_date_header\": false,\n        //keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection. \n        //After the maximum number of requests are made, the connection is closed.\n        //The default value of 0 means no limit.\n        \"keepalive_requests\": 0,\n        //pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer. \n        //After the maximum number of requests are made, the connection is closed.\n        //The default value of 0 means no limit.\n        \"pipelining_requests\": 0,\n        //gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed \n        //file with the extension \".gz\" in the same path and send the compressed file to the client.\n        //The default value of gzip_static is true.\n        \"gzip_static\": true,\n        //client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is \"1M\".\n        //One can set it to \"1024\", \"1k\", \"10M\", \"1G\", etc. Setting it to \"\" means no limit.\n        \"client_max_body_size\": \"1M\",\n        //max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is \"64K\" bytes.\n        //If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.\n        //Setting it to \"\" means no limit.\n        \"client_max_memory_body_size\": \"64K\",\n        //client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is \"128K\".\n        //One can set it to \"1024\", \"1k\", \"10M\", \"1G\", etc. Setting it to \"\" means no limit.\n        \"client_max_websocket_message_size\": \"128K\"\n    },\n    //plugins: Define all plugins running in the application\n    \"plugins\": [{\n        //name: The class name of the plugin\n        \"name\": \"my_plugin::SimpleReverseProxy\",\n        //dependencies: Plugins that the plugin depends on. It can be commented out\n        \"dependencies\": [],\n        //config: The configuration of the plugin. This json object is the parameter to initialize the plugin.\n        //It can be commented out\n        \"config\": {\n            // The pipelining depth of HTTP clients.\n            \"pipelining\": 16,\n            \"backends\": [\"http://127.0.0.1:8848\"],\n            \"same_client_to_same_backend\": false,\n            //The number of connections created by proxy for each backend in very event loop (IO thread).\n            \"connection_factor\": 1\n        }\n    }],\n    //custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method. \n    \"custom_config\": {}\n}\n"
  },
  {
    "path": "examples/simple_reverse_proxy/main.cc",
    "content": "#include <drogon/drogon.h>\n\nint main()\n{\n    // Set HTTP listener address and port\n    drogon::app().loadConfigFile(\"../config.json\");\n    // Run HTTP framework, the method will block in the internal event loop\n    drogon::app().run();\n    return 0;\n}\n"
  },
  {
    "path": "examples/simple_reverse_proxy/plugins/SimpleReverseProxy.cc",
    "content": "/**\n *\n *  @file SimpleReverseProxy.cc\n *\n */\n\n#include \"SimpleReverseProxy.h\"\n\nusing namespace drogon;\nusing namespace my_plugin;\n\nvoid SimpleReverseProxy::initAndStart(const Json::Value &config)\n{\n    /// Initialize and start the plugin\n    if (config.isMember(\"backends\") && config[\"backends\"].isArray())\n    {\n        for (auto &backend : config[\"backends\"])\n        {\n            backendAddrs_.emplace_back(backend.asString());\n        }\n        if (backendAddrs_.empty())\n        {\n            LOG_ERROR << \"You must set at least one backend\";\n            abort();\n        }\n    }\n    else\n    {\n        LOG_ERROR << \"Error in configuration\";\n        abort();\n    }\n    pipeliningDepth_ = config.get(\"pipelining\", 0).asInt();\n    sameClientToSameBackend_ =\n        config.get(\"same_client_to_same_backend\", false).asBool();\n    connectionFactor_ = config.get(\"connection_factor\", 1).asInt();\n    if (connectionFactor_ == 0 || connectionFactor_ > 100)\n    {\n        LOG_ERROR << \"invalid number of connection factor\";\n        abort();\n    }\n    clients_.init(\n        [this](std::vector<HttpClientPtr> &clients, size_t ioLoopIndex) {\n            clients.resize(backendAddrs_.size() * connectionFactor_);\n        });\n    clientIndex_.init(\n        [this](size_t &index, size_t ioLoopIndex) { index = ioLoopIndex; });\n    drogon::app().registerPreRoutingAdvice([this](const HttpRequestPtr &req,\n                                                  AdviceCallback &&callback,\n                                                  AdviceChainCallback &&pass) {\n        preRouting(req, std::move(callback), std::move(pass));\n    });\n}\n\nvoid SimpleReverseProxy::shutdown()\n{\n}\n\nvoid SimpleReverseProxy::preRouting(const HttpRequestPtr &req,\n                                    AdviceCallback &&callback,\n                                    AdviceChainCallback &&)\n{\n    size_t index;\n    auto &clientsVector = *clients_;\n    if (sameClientToSameBackend_)\n    {\n        index = std::hash<uint32_t>{}(req->getPeerAddr().ipNetEndian()) %\n                clientsVector.size();\n        index = (index + (++(*clientIndex_)) * backendAddrs_.size()) %\n                clientsVector.size();\n    }\n    else\n    {\n        index = ++(*clientIndex_) % clientsVector.size();\n    }\n    auto &clientPtr = clientsVector[index];\n    if (!clientPtr)\n    {\n        auto &addr = backendAddrs_[index % backendAddrs_.size()];\n        clientPtr = HttpClient::newHttpClient(\n            addr, trantor::EventLoop::getEventLoopOfCurrentThread());\n        clientPtr->setPipeliningDepth(pipeliningDepth_);\n    }\n    req->setPassThrough(true);\n    clientPtr->sendRequest(\n        req,\n        [callback = std::move(callback)](ReqResult result,\n                                         const HttpResponsePtr &resp) {\n            if (result == ReqResult::Ok)\n            {\n                resp->setPassThrough(true);\n                callback(resp);\n            }\n            else\n            {\n                auto errResp = HttpResponse::newHttpResponse();\n                errResp->setStatusCode(k500InternalServerError);\n                callback(errResp);\n            }\n        });\n}\n"
  },
  {
    "path": "examples/simple_reverse_proxy/plugins/SimpleReverseProxy.h",
    "content": "/**\n *\n *  @file SimpleReverseProxy.h\n *\n */\n\n#pragma once\n\n#include <drogon/plugins/Plugin.h>\n#include <drogon/drogon.h>\n#include <vector>\n#include <memory>\n\nnamespace my_plugin\n{\nclass SimpleReverseProxy : public drogon::Plugin<SimpleReverseProxy>\n{\n  public:\n    SimpleReverseProxy()\n    {\n    }\n\n    /// This method must be called by drogon to initialize and start the plugin.\n    /// It must be implemented by the user.\n    void initAndStart(const Json::Value &config) override;\n\n    /// This method must be called by drogon to shutdown the plugin.\n    /// It must be implemented by the user.\n    void shutdown() override;\n\n  private:\n    // Create 'connectionFactor_' HTTP clients for every backend in every IO\n    // event loop.\n    drogon::IOThreadStorage<std::vector<drogon::HttpClientPtr>> clients_;\n    drogon::IOThreadStorage<size_t> clientIndex_{0};\n    std::vector<std::string> backendAddrs_;\n    bool sameClientToSameBackend_{false};\n    size_t pipeliningDepth_{0};\n    void preRouting(const drogon::HttpRequestPtr &,\n                    drogon::AdviceCallback &&,\n                    drogon::AdviceChainCallback &&);\n    size_t connectionFactor_{1};\n};\n}  // namespace my_plugin\n"
  },
  {
    "path": "examples/websocket_client/WebSocketClient.cc",
    "content": "#include <drogon/WebSocketClient.h>\n#include <drogon/HttpAppFramework.h>\n\n#include <iostream>\n\nusing namespace drogon;\nusing namespace std::chrono_literals;\n\nint main(int argc, char *argv[])\n{\n    std::string server;\n    std::string path;\n    std::optional<uint16_t> port;\n    // Connect to a public echo server\n    if (argc > 1 && std::string(argv[1]) == \"-p\")\n    {\n        server = \"wss://echo.websocket.events/.ws\";\n        path = \"/\";\n    }\n    else\n    {\n        server = \"ws://127.0.0.1\";\n        port = 8848;\n        path = \"/chat\";\n    }\n\n    std::string serverString;\n    if (port.value_or(0) != 0)\n        serverString = server + \":\" + std::to_string(port.value());\n    else\n        serverString = server;\n    auto wsPtr = WebSocketClient::newWebSocketClient(serverString);\n    auto req = HttpRequest::newHttpRequest();\n    req->setPath(path);\n\n    wsPtr->setMessageHandler([](const std::string &message,\n                                const WebSocketClientPtr &,\n                                const WebSocketMessageType &type) {\n        std::string messageType = \"Unknown\";\n        if (type == WebSocketMessageType::Text)\n            messageType = \"text\";\n        else if (type == WebSocketMessageType::Pong)\n            messageType = \"pong\";\n        else if (type == WebSocketMessageType::Ping)\n            messageType = \"ping\";\n        else if (type == WebSocketMessageType::Binary)\n            messageType = \"binary\";\n        else if (type == WebSocketMessageType::Close)\n            messageType = \"Close\";\n\n        LOG_INFO << \"new message (\" << messageType << \"): \" << message;\n    });\n\n    wsPtr->setConnectionClosedHandler([](const WebSocketClientPtr &) {\n        LOG_INFO << \"WebSocket connection closed!\";\n    });\n\n    LOG_INFO << \"Connecting to WebSocket at \" << server;\n    wsPtr->connectToServer(\n        req,\n        [](ReqResult r,\n           const HttpResponsePtr &,\n           const WebSocketClientPtr &wsPtr) {\n            if (r != ReqResult::Ok)\n            {\n                LOG_ERROR << \"Failed to establish WebSocket connection!\";\n                wsPtr->stop();\n                return;\n            }\n            LOG_INFO << \"WebSocket connected!\";\n            wsPtr->getConnection()->setPingMessage(\"\", 2s);\n            wsPtr->getConnection()->send(\"hello!\");\n        });\n\n    // Quit the application after 15 seconds\n    app().getLoop()->runAfter(15, []() { app().quit(); });\n\n    app().setLogLevel(trantor::Logger::kDebug);\n    app().run();\n    LOG_INFO << \"bye!\";\n    return 0;\n}\n"
  },
  {
    "path": "examples/websocket_server/WebSocketServer.cc",
    "content": "#include <drogon/WebSocketController.h>\n#include <drogon/PubSubService.h>\n#include <drogon/HttpAppFramework.h>\nusing namespace drogon;\n\nclass WebSocketChat : public drogon::WebSocketController<WebSocketChat>\n{\n  public:\n    void handleNewMessage(const WebSocketConnectionPtr &,\n                          std::string &&,\n                          const WebSocketMessageType &) override;\n    void handleConnectionClosed(const WebSocketConnectionPtr &) override;\n    void handleNewConnection(const HttpRequestPtr &,\n                             const WebSocketConnectionPtr &) override;\n    WS_PATH_LIST_BEGIN\n    WS_PATH_ADD(\"/chat\", Get);\n    WS_ADD_PATH_VIA_REGEX(\"/[^/]*\", Get);\n    WS_PATH_LIST_END\n  private:\n    PubSubService<std::string> chatRooms_;\n};\n\nstruct Subscriber\n{\n    std::string chatRoomName_;\n    drogon::SubscriberID id_;\n};\n\nvoid WebSocketChat::handleNewMessage(const WebSocketConnectionPtr &wsConnPtr,\n                                     std::string &&message,\n                                     const WebSocketMessageType &type)\n{\n    // write your application logic here\n    LOG_DEBUG << \"new websocket message:\" << message;\n    if (type == WebSocketMessageType::Ping)\n    {\n        LOG_DEBUG << \"recv a ping\";\n    }\n    else if (type == WebSocketMessageType::Text)\n    {\n        auto &s = wsConnPtr->getContextRef<Subscriber>();\n        chatRooms_.publish(s.chatRoomName_, message);\n    }\n}\n\nvoid WebSocketChat::handleConnectionClosed(const WebSocketConnectionPtr &conn)\n{\n    LOG_DEBUG << \"websocket closed!\";\n    auto &s = conn->getContextRef<Subscriber>();\n    chatRooms_.unsubscribe(s.chatRoomName_, s.id_);\n}\n\nvoid WebSocketChat::handleNewConnection(const HttpRequestPtr &req,\n                                        const WebSocketConnectionPtr &conn)\n{\n    LOG_DEBUG << \"new websocket connection!\";\n    conn->send(\"haha!!!\");\n    Subscriber s;\n    s.chatRoomName_ = req->getParameter(\"room_name\");\n    s.id_ = chatRooms_.subscribe(s.chatRoomName_,\n                                 [conn](const std::string &topic,\n                                        const std::string &message) {\n                                     // Suppress unused variable warning\n                                     (void)topic;\n                                     conn->send(message);\n                                 });\n    conn->setContext(std::make_shared<Subscriber>(std::move(s)));\n}\n\nint main()\n{\n    app().addListener(\"127.0.0.1\", 8848).run();\n}\n"
  },
  {
    "path": "format.sh",
    "content": "#!/usr/bin/env bash\n\n# You can customize the clang-format path by setting the CLANG_FORMAT environment variable\nCLANG_FORMAT=${CLANG_FORMAT:-clang-format}\n\n# Check if clang-format version is 17 to avoid inconsistent formatting\n$CLANG_FORMAT --version\nif [[ ! $($CLANG_FORMAT --version) =~ \"version 17\" ]]; then\n    echo \"Error: clang-format version must be 17\"\n    exit 1\nfi\n\nfind lib orm_lib nosql_lib examples drogon_ctl -name *.h -o -name *.cc -exec dos2unix {} \\;\nfind lib orm_lib nosql_lib examples drogon_ctl -name *.h -o -name *.cc|xargs $CLANG_FORMAT -i -style=file\n"
  },
  {
    "path": "lib/inc/drogon/Attribute.h",
    "content": "/**\n *\n *  Attribute.h\n *  armstrong@sweelia.com\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <trantor/utils/Logger.h>\n#include <map>\n#include <memory>\n#include <any>\n\nnamespace drogon\n{\n/**\n * @brief This class represents the attributes stored in the HTTP request.\n * One can add/get any type of data to/from an Attributes object.\n */\nclass Attributes\n{\n  public:\n    /**\n     * @brief Get the data identified by the key parameter.\n     * @note if the data is not found, a default value is returned.\n     * For example:\n     * @code\n       auto &userName = attributesPtr->get<std::string>(\"user name\");\n       @endcode\n     */\n    template <typename T>\n    const T &get(const std::string &key) const\n    {\n        static const T nullVal = T();\n        auto it = attributesMap_.find(key);\n        if (it != attributesMap_.end())\n        {\n            if (typeid(T) == it->second.type())\n            {\n                return *(std::any_cast<T>(&(it->second)));\n            }\n            else\n            {\n                LOG_ERROR << \"Bad type\";\n            }\n        }\n        return nullVal;\n    }\n\n    /**\n     * @brief Get the 'any' object identified by the given key\n     */\n    std::any &operator[](const std::string &key)\n    {\n        return attributesMap_[key];\n    }\n\n    /**\n     * @brief Insert a key-value pair\n     * @note here the any object can be created implicitly. for example\n     * @code\n       attributesPtr->insert(\"user name\", userNameString);\n       @endcode\n     */\n    void insert(const std::string &key, const std::any &obj)\n    {\n        attributesMap_[key] = obj;\n    }\n\n    /**\n     * @brief Insert a key-value pair\n     * @note here the any object can be created implicitly. for example\n     * @code\n       attributesPtr->insert(\"user name\", userNameString);\n       @endcode\n     */\n    void insert(const std::string &key, std::any &&obj)\n    {\n        attributesMap_[key] = std::move(obj);\n    }\n\n    /**\n     * @brief Erase the data identified by the given key.\n     */\n    void erase(const std::string &key)\n    {\n        attributesMap_.erase(key);\n    }\n\n    /**\n     * @brief Return true if the data identified by the key exists.\n     */\n    bool find(const std::string &key)\n    {\n        if (attributesMap_.find(key) == attributesMap_.end())\n        {\n            return false;\n        }\n        return true;\n    }\n\n    /**\n     * @brief Clear all attributes.\n     */\n    void clear()\n    {\n        attributesMap_.clear();\n    }\n\n    /**\n     * @brief Constructor, usually called by the framework\n     */\n    Attributes() = default;\n\n  private:\n    using AttributesMap = std::map<std::string, std::any>;\n    AttributesMap attributesMap_;\n};\n\nusing AttributesPtr = std::shared_ptr<Attributes>;\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/CacheMap.h",
    "content": "/**\n *\n *  @file CacheMap.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <trantor/net/EventLoop.h>\n#include <trantor/utils/Logger.h>\n#include <atomic>\n#include <deque>\n#include <map>\n#include <mutex>\n#include <set>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n#include <future>\n#include <assert.h>\n\n#define WHEELS_NUM 4\n#define BUCKET_NUM_PER_WHEEL 200\n#define TICK_INTERVAL 1.0\n\nnamespace drogon\n{\n/**\n * @brief A utility class for CacheMap\n */\nclass CallbackEntry\n{\n  public:\n    CallbackEntry(std::function<void()> cb) : cb_(std::move(cb))\n    {\n    }\n\n    ~CallbackEntry()\n    {\n        cb_();\n    }\n\n  private:\n    std::function<void()> cb_;\n};\n\nusing CallbackEntryPtr = std::shared_ptr<CallbackEntry>;\nusing WeakCallbackEntryPtr = std::weak_ptr<CallbackEntry>;\n\nusing CallbackBucket = std::unordered_set<CallbackEntryPtr>;\nusing CallbackBucketQueue = std::deque<CallbackBucket>;\n\n/**\n * @brief Cache Map\n *\n * @tparam T1 The keyword type.\n * @tparam T2 The value type.\n * @note\n * Four wheels with 200 buckets per wheel means the cache map can work with a\n * timeout up to 200^4 seconds (about 50 years).\n */\ntemplate <typename T1, typename T2>\nclass CacheMap\n{\n  public:\n    /// constructor\n    /**\n     * @param loop\n     * eventloop pointer\n     * @param tickInterval\n     * second\n     * @param wheelsNum\n     * number of wheels\n     * @param bucketsNumPerWheel\n     * buckets number per wheel\n     * @param fnOnInsert\n     * function to execute on insertion\n     * @param fnOnErase\n     * function to execute on erase\n     * @details The max delay of the CacheMap is about\n     * tickInterval*(bucketsNumPerWheel^wheelsNum) seconds.\n     */\n    CacheMap(trantor::EventLoop *loop,\n             float tickInterval = TICK_INTERVAL,\n             size_t wheelsNum = WHEELS_NUM,\n             size_t bucketsNumPerWheel = BUCKET_NUM_PER_WHEEL,\n             std::function<void(const T1 &)> fnOnInsert = nullptr,\n             std::function<void(const T1 &)> fnOnErase = nullptr)\n        : loop_(loop),\n          tickInterval_(tickInterval),\n          wheelsNumber_(wheelsNum),\n          bucketsNumPerWheel_(bucketsNumPerWheel),\n          ctrlBlockPtr_(std::make_shared<ControlBlock>()),\n          fnOnInsert_(fnOnInsert),\n          fnOnErase_(fnOnErase)\n    {\n        wheels_.resize(wheelsNumber_);\n        for (size_t i = 0; i < wheelsNumber_; ++i)\n        {\n            wheels_[i].resize(bucketsNumPerWheel_);\n        }\n        if (tickInterval_ > 0 && wheelsNumber_ > 0 && bucketsNumPerWheel_ > 0)\n        {\n            timerId_ = loop_->runEvery(\n                tickInterval_, [this, ctrlBlockPtr = ctrlBlockPtr_]() {\n                    std::lock_guard<std::mutex> lock(ctrlBlockPtr->mtx);\n                    if (ctrlBlockPtr->destructed)\n                        return;\n\n                    size_t t = ++ticksCounter_;\n                    size_t pow = 1;\n                    for (size_t i = 0; i < wheelsNumber_; ++i)\n                    {\n                        if ((t % pow) == 0)\n                        {\n                            CallbackBucket tmp;\n                            {\n                                std::lock_guard<std::mutex> lock(bucketMutex_);\n                                // use tmp val to make this critical area as\n                                // short as possible.\n                                wheels_[i].front().swap(tmp);\n                                wheels_[i].pop_front();\n                                wheels_[i].push_back(CallbackBucket());\n                            }\n                        }\n                        pow = pow * bucketsNumPerWheel_;\n                    }\n                });\n            loop_->runOnQuit([ctrlBlockPtr = ctrlBlockPtr_] {\n                std::lock_guard<std::mutex> lock(ctrlBlockPtr->mtx);\n                ctrlBlockPtr->loopEnded = true;\n            });\n        }\n        else\n        {\n            noWheels_ = true;\n        }\n    };\n\n    ~CacheMap()\n    {\n        std::lock_guard<std::mutex> lock(ctrlBlockPtr_->mtx);\n        ctrlBlockPtr_->destructed = true;\n        map_.clear();\n        if (!ctrlBlockPtr_->loopEnded)\n        {\n            loop_->invalidateTimer(timerId_);\n        }\n        for (auto iter = wheels_.rbegin(); iter != wheels_.rend(); ++iter)\n        {\n            iter->clear();\n        }\n        LOG_TRACE << \"CacheMap destruct!\";\n    }\n\n    struct MapValue\n    {\n        MapValue(const T2 &value,\n                 size_t timeout,\n                 std::function<void()> &&callback)\n            : value_(value),\n              timeout_(timeout),\n              timeoutCallback_(std::move(callback))\n        {\n        }\n\n        MapValue(T2 &&value, size_t timeout, std::function<void()> &&callback)\n            : value_(std::move(value)),\n              timeout_(timeout),\n              timeoutCallback_(std::move(callback))\n        {\n        }\n\n        MapValue(T2 &&value, size_t timeout)\n            : value_(std::move(value)), timeout_(timeout)\n        {\n        }\n\n        MapValue(const T2 &value, size_t timeout)\n            : value_(value), timeout_(timeout)\n        {\n        }\n\n        MapValue(T2 &&value) : value_(std::move(value))\n        {\n        }\n\n        MapValue(const T2 &value) : value_(value)\n        {\n        }\n\n        MapValue() = default;\n        T2 value_;\n        size_t timeout_{0};\n        std::function<void()> timeoutCallback_;\n        WeakCallbackEntryPtr weakEntryPtr_;\n    };\n\n    /**\n     * @brief Insert a key-value pair into the cache.\n     *\n     * @param key The key\n     * @param value The value\n     * @param timeout The timeout in seconds, if timeout > 0, the value will be\n     * erased within the 'timeout' seconds after the last access. If the timeout\n     * is zero, the value exists until being removed explicitly.\n     * @param timeoutCallback is called when the timeout expires.\n     */\n    void insert(const T1 &key,\n                T2 &&value,\n                size_t timeout = 0,\n                std::function<void()> timeoutCallback = std::function<void()>())\n    {\n        if (timeout > 0)\n        {\n            MapValue v{std::move(value), timeout, std::move(timeoutCallback)};\n            std::lock_guard<std::mutex> lock(mtx_);\n            map_.insert(std::make_pair(key, std::move(v)));\n            eraseAfter(timeout, key);\n        }\n        else\n        {\n            MapValue v{std::move(value)};\n            std::lock_guard<std::mutex> lock(mtx_);\n            map_.insert(std::make_pair(key, std::move(v)));\n        }\n        if (fnOnInsert_)\n            fnOnInsert_(key);\n    }\n\n    /**\n     * @brief Insert a key-value pair into the cache.\n     *\n     * @param key The key\n     * @param value The value\n     * @param timeout The timeout in seconds, if timeout > 0, the value will be\n     * erased within the 'timeout' seconds after the last access. If the timeout\n     * is zero, the value exists until being removed explicitly.\n     * @param timeoutCallback is called when the timeout expires.\n     */\n    void insert(const T1 &key,\n                const T2 &value,\n                size_t timeout = 0,\n                std::function<void()> timeoutCallback = std::function<void()>())\n    {\n        if (timeout > 0)\n        {\n            MapValue v{value, timeout, std::move(timeoutCallback)};\n            std::lock_guard<std::mutex> lock(mtx_);\n            map_.insert(std::make_pair(key, std::move(v)));\n            eraseAfter(timeout, key);\n        }\n        else\n        {\n            MapValue v{value};\n            std::lock_guard<std::mutex> lock(mtx_);\n            map_.insert(std::make_pair(key, std::move(v)));\n        }\n        if (fnOnInsert_)\n            fnOnInsert_(key);\n    }\n\n    /**\n     * @brief Return the value of the keyword.\n     *\n     * @param key\n     * @return T2\n     * @note This function returns a copy of the data in the cache. If the data\n     * is not found, a default T2 type value is returned and nothing is inserted\n     * into the cache.\n     */\n    T2 operator[](const T1 &key)\n    {\n        size_t timeout = 0;\n        std::lock_guard<std::mutex> lock(mtx_);\n        auto iter = map_.find(key);\n        if (iter != map_.end())\n        {\n            timeout = iter->second.timeout_;\n            if (timeout > 0)\n                eraseAfter(timeout, key);\n            return iter->second.value_;\n        }\n        return T2();\n    }\n\n    /**\n     * @brief Modify or visit the data identified by the key parameter.\n     *\n     * @tparam Callable the type of the handler.\n     * @param key\n     * @param handler A callable that can modify or visit the data. The\n     * signature of the handler should be equivalent to 'void(T2&)' or\n     * 'void(const T2&)'\n     * @param timeout In seconds.\n     *\n     * @note This function is multiple-thread safe. if the data identified by\n     * the key doesn't exist, a new one is created and passed to the handler and\n     * stored in the cache with the timeout parameter. The changing of the data\n     * is protected by the mutex of the cache.\n     *\n     */\n    template <typename Callable>\n    void modify(const T1 &key, Callable &&handler, size_t timeout = 0)\n    {\n        {\n            std::lock_guard<std::mutex> lock(mtx_);\n            auto iter = map_.find(key);\n            if (iter != map_.end())\n            {\n                timeout = iter->second.timeout_;\n                handler(iter->second.value_);\n                if (timeout > 0)\n                    eraseAfter(timeout, key);\n                return;\n            }\n\n            MapValue v{T2(), timeout};\n            handler(v.value_);\n            map_.insert(std::make_pair(key, std::move(v)));\n            if (timeout > 0)\n            {\n                eraseAfter(timeout, key);\n            }\n        }\n        if (fnOnInsert_)\n            fnOnInsert_(key);\n    }\n\n    /// Check if the value of the keyword exists\n    bool find(const T1 &key)\n    {\n        size_t timeout = 0;\n        bool flag = false;\n\n        std::lock_guard<std::mutex> lock(mtx_);\n        auto iter = map_.find(key);\n        if (iter != map_.end())\n        {\n            timeout = iter->second.timeout_;\n            flag = true;\n        }\n\n        if (timeout > 0)\n            eraseAfter(timeout, key);\n\n        return flag;\n    }\n\n    /// Atomically find and get the value of a keyword\n    /**\n     * Return true when the value is found, and the value\n     * is assigned to the value argument.\n     */\n    bool findAndFetch(const T1 &key, T2 &value)\n    {\n        size_t timeout = 0;\n        bool flag = false;\n        std::lock_guard<std::mutex> lock(mtx_);\n        auto iter = map_.find(key);\n        if (iter != map_.end())\n        {\n            timeout = iter->second.timeout_;\n            flag = true;\n            value = iter->second.value_;\n        }\n\n        if (timeout > 0)\n            eraseAfter(timeout, key);\n\n        return flag;\n    }\n\n    /// Erase the value of the keyword.\n    /**\n     * @param key the keyword.\n     * @note This function does not cause the timeout callback to be executed.\n     */\n    void erase(const T1 &key)\n    {\n        // in this case,we don't evoke the timeout callback;\n        {\n            std::lock_guard<std::mutex> lock(mtx_);\n            map_.erase(key);\n        }\n        if (fnOnErase_)\n            fnOnErase_(key);\n    }\n\n    /**\n     * @brief Get the event loop object\n     *\n     * @return trantor::EventLoop*\n     */\n    trantor::EventLoop *getLoop()\n    {\n        return loop_;\n    }\n\n    /**\n     * @brief run the task function after a period of time.\n     *\n     * @param delay in seconds\n     * @param task\n     * @note This timer is a low-precision timer whose accuracy depends on the\n     * tickInterval parameter of the cache. The advantage of the timer is its\n     * low cost.\n     */\n    void runAfter(size_t delay, std::function<void()> &&task)\n    {\n        std::lock_guard<std::mutex> lock(bucketMutex_);\n        insertEntry(delay, std::make_shared<CallbackEntry>(std::move(task)));\n    }\n\n    void runAfter(size_t delay, const std::function<void()> &task)\n    {\n        std::lock_guard<std::mutex> lock(bucketMutex_);\n        insertEntry(delay, std::make_shared<CallbackEntry>(task));\n    }\n\n  private:\n    /**\n     * @brief ControlBlock in a internal structure that deals with synchronizing\n     * CacheMap destructing, event loop destructing and updating the CacheMap.\n     * It is possible that the EventLoop destructed before the CacheMap (ex:\n     * both CacheMap and the EventLoop being globals, the order of destruction\n     * is not defined), thus we shouldn't invalidate the time. Or CacheMap\n     * destructed before the event loop but the timer is still active. Thus we\n     * should avoid updating the CacheMap.\n     */\n    struct ControlBlock\n    {\n        ControlBlock() : destructed(false), loopEnded(false)\n        {\n        }\n\n        bool destructed;\n        bool loopEnded;\n        std::mutex mtx;\n    };\n\n    std::unordered_map<T1, MapValue> map_;\n\n    std::vector<CallbackBucketQueue> wheels_;\n\n    std::atomic<size_t> ticksCounter_{0};\n\n    std::mutex mtx_;\n    std::mutex bucketMutex_;\n    trantor::TimerId timerId_;\n    trantor::EventLoop *loop_;\n\n    float tickInterval_;\n    size_t wheelsNumber_;\n    size_t bucketsNumPerWheel_;\n    std::shared_ptr<ControlBlock> ctrlBlockPtr_;\n    std::function<void(const T1 &)> fnOnInsert_;\n    std::function<void(const T1 &)> fnOnErase_;\n\n    bool noWheels_{false};\n\n    void insertEntry(size_t delay, CallbackEntryPtr entryPtr)\n    {\n        // protected by bucketMutex;\n        if (delay <= 0)\n            return;\n        delay = static_cast<size_t>(delay / tickInterval_ + 1);\n        size_t t = ticksCounter_;\n        for (size_t i = 0; i < wheelsNumber_; ++i)\n        {\n            if (delay <= bucketsNumPerWheel_)\n            {\n                wheels_[i][delay - 1].insert(entryPtr);\n                break;\n            }\n            if (i < (wheelsNumber_ - 1))\n            {\n                entryPtr = std::make_shared<CallbackEntry>(\n                    [this, delay, i, t, entryPtr]() {\n                        if (delay > 0)\n                        {\n                            std::lock_guard<std::mutex> lock(bucketMutex_);\n                            wheels_[i][(delay + (t % bucketsNumPerWheel_) - 1) %\n                                       bucketsNumPerWheel_]\n                                .insert(entryPtr);\n                        }\n                    });\n            }\n            else\n            {\n                // delay is too long to put entry at valid position in wheels;\n                wheels_[i][bucketsNumPerWheel_ - 1].insert(entryPtr);\n            }\n            delay =\n                (delay + (t % bucketsNumPerWheel_) - 1) / bucketsNumPerWheel_;\n            t = t / bucketsNumPerWheel_;\n        }\n    }\n\n    void eraseAfter(size_t delay, const T1 &key)\n    {\n        if (noWheels_)\n            return;\n        assert(map_.find(key) != map_.end());\n\n        CallbackEntryPtr entryPtr;\n\n        if (map_.find(key) != map_.end())\n        {\n            entryPtr = map_[key].weakEntryPtr_.lock();\n        }\n\n        if (entryPtr)\n        {\n            std::lock_guard<std::mutex> lock(bucketMutex_);\n            insertEntry(delay, entryPtr);\n        }\n        else\n        {\n            std::function<void()> cb = [this, key]() {\n                bool erased{false};\n                std::function<void()> timeoutCallback;\n                {\n                    std::lock_guard<std::mutex> lock(mtx_);\n                    auto iter = map_.find(key);\n                    if (iter != map_.end())\n                    {\n                        auto &value = iter->second;\n                        auto entryPtr = value.weakEntryPtr_.lock();\n                        // entryPtr is used to avoid race conditions\n                        if (value.timeout_ > 0 && !entryPtr)\n                        {\n                            erased = true;\n                            timeoutCallback = std::move(value.timeoutCallback_);\n                            map_.erase(key);\n                        }\n                    }\n                }\n                if (erased && fnOnErase_)\n                    fnOnErase_(key);\n                if (erased && timeoutCallback)\n                    timeoutCallback();\n            };\n            entryPtr = std::make_shared<CallbackEntry>(std::move(cb));\n            map_[key].weakEntryPtr_ = entryPtr;\n            {\n                std::lock_guard<std::mutex> lock(bucketMutex_);\n                insertEntry(delay, entryPtr);\n            }\n        }\n    }\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/Cookie.h",
    "content": "/**\n *\n *  @file Cookie.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include <drogon/exports.h>\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <drogon/utils/Utilities.h>\n#include <cctype>\n#include <string>\n#include <limits>\n#include <optional>\n#include <string_view>\n\nnamespace drogon\n{\n/**\n * @brief this class represents a cookie entity.\n */\nclass DROGON_EXPORT Cookie\n{\n  public:\n    /// Constructor\n    /**\n     * @param key key of the cookie\n     * @param value value of the cookie\n     */\n    Cookie(std::string key, std::string value)\n        : key_(std::move(key)), value_(std::move(value))\n    {\n    }\n\n    Cookie() = default;\n    enum class SameSite\n    {\n        kNull,\n        kLax,\n        kStrict,\n        kNone\n    };\n\n    /**\n     * @brief Set the Expires Date\n     *\n     * @param date The expiration date\n     */\n    void setExpiresDate(const trantor::Date &date)\n    {\n        expiresDate_ = date;\n    }\n\n    /**\n     * @brief Set if the cookie is HTTP only.\n     */\n    void setHttpOnly(bool only)\n    {\n        httpOnly_ = only;\n    }\n\n    /**\n     * @brief Set if the cookie is secure.\n     */\n    void setSecure(bool secure)\n    {\n        secure_ = secure;\n    }\n\n    /**\n     * @brief Set the domain of the cookie.\n     */\n    void setDomain(const std::string &domain)\n    {\n        domain_ = domain;\n    }\n\n    /**\n     * @brief Set the domain of the cookie.\n     */\n    void setDomain(std::string &&domain)\n    {\n        domain_ = std::move(domain);\n    }\n\n    /**\n     * @brief Set the path of the cookie.\n     */\n    void setPath(const std::string &path)\n    {\n        path_ = path;\n    }\n\n    /**\n     * @brief Set the path of the cookie.\n     */\n    void setPath(std::string &&path)\n    {\n        path_ = std::move(path);\n    }\n\n    /**\n     * @brief Set the key of the cookie.\n     */\n    void setKey(const std::string &key)\n    {\n        key_ = key;\n    }\n\n    /**\n     * @brief Set the key of the cookie.\n     */\n    void setKey(std::string &&key)\n    {\n        key_ = std::move(key);\n    }\n\n    /**\n     * @brief Set the value of the cookie.\n     */\n    void setValue(const std::string &value)\n    {\n        value_ = value;\n    }\n\n    /**\n     * @brief Set the value of the cookie.\n     */\n    void setValue(std::string &&value)\n    {\n        value_ = std::move(value);\n    }\n\n    /**\n     * @brief Set the max-age of the cookie.\n     */\n    void setMaxAge(int value)\n    {\n        maxAge_ = value;\n    }\n\n    /**\n     * @brief Set the same site of the cookie.\n     */\n    void setSameSite(SameSite sameSite)\n    {\n        sameSite_ = sameSite;\n    }\n\n    /**\n     *  @brief Set the partitioned status of the cookie\n     */\n    void setPartitioned(bool partitioned)\n    {\n        partitioned_ = partitioned;\n        if (partitioned)\n        {\n            setSecure(true);\n        }\n    }\n\n    /**\n     * @brief Get the string value of the cookie\n     */\n    std::string cookieString() const;\n\n    /**\n     * @brief Get the string value of the cookie\n     */\n    std::string getCookieString() const\n    {\n        return cookieString();\n    }\n\n    /**\n     * @brief Get the expiration date of the cookie\n     */\n    const trantor::Date &expiresDate() const\n    {\n        return expiresDate_;\n    }\n\n    /**\n     * @brief Get the expiration date of the cookie\n     */\n    const trantor::Date &getExpiresDate() const\n    {\n        return expiresDate_;\n    }\n\n    /**\n     * @brief Get the domain of the cookie\n     */\n    const std::string &domain() const\n    {\n        return domain_;\n    }\n\n    /**\n     * @brief Get the domain of the cookie\n     */\n    const std::string &getDomain() const\n    {\n        return domain_;\n    }\n\n    /**\n     * @brief Get the path of the cookie\n     */\n    const std::string &path() const\n    {\n        return path_;\n    }\n\n    /**\n     * @brief Get the path of the cookie\n     */\n    const std::string &getPath() const\n    {\n        return path_;\n    }\n\n    /**\n     * @brief Get the keyword of the cookie\n     */\n    const std::string &key() const\n    {\n        return key_;\n    }\n\n    /**\n     * @brief Get the keyword of the cookie\n     */\n    const std::string &getKey() const\n    {\n        return key_;\n    }\n\n    /**\n     * @brief Get the value of the cookie\n     */\n    const std::string &value() const\n    {\n        return value_;\n    }\n\n    /**\n     * @brief Get the value of the cookie\n     */\n    const std::string &getValue() const\n    {\n        return value_;\n    }\n\n    /**\n     * @brief Check if the cookie is empty\n     *\n     * @return true means the cookie is not empty\n     * @return false means the cookie is empty\n     */\n    operator bool() const\n    {\n        return (!key_.empty()) && (!value_.empty());\n    }\n\n    /**\n     * @brief Check if the cookie is HTTP only\n     *\n     * @return true means the cookie is HTTP only\n     * @return false means the cookie is not HTTP only\n     */\n    bool isHttpOnly() const\n    {\n        return httpOnly_;\n    }\n\n    /**\n     * @brief Check if the cookie is secure.\n     *\n     * @return true means the cookie is secure.\n     * @return false means the cookie is not secure.\n     */\n    bool isSecure() const\n    {\n        return secure_;\n    }\n\n    /**\n     * @brief Check if the cookie is partitioned.\n     *\n     * @return true means the cookie is partitioned.\n     * @return false means the cookie is not partitioned.\n     */\n    bool isPartitioned() const\n    {\n        return partitioned_;\n    }\n\n    /**\n     * @brief Get the max-age of the cookie\n     */\n    std::optional<int> maxAge() const\n    {\n        return maxAge_;\n    }\n\n    /**\n     * @brief Get the max-age of the cookie\n     */\n    std::optional<int> getMaxAge() const\n    {\n        return maxAge_;\n    }\n\n    /**\n     * @brief Get the same site of the cookie\n     */\n    SameSite sameSite() const\n    {\n        return sameSite_;\n    }\n\n    /**\n     * @brief Get the same site of the cookie\n     */\n    SameSite getSameSite() const\n    {\n        return sameSite_;\n    }\n\n    /**\n     * @brief Compare two strings ignoring the their cases\n     *\n     * @param str1 string to check its value\n     * @param str2 string to check against, written in lower case\n     *\n     * @note the function is optimized to check for cookie's samesite value\n     * where we check if the value equals to a specific value we already know in\n     * str2. so the function doesn't apply tolower to the second argument\n     * str2 as it's always in lower case.\n     *\n     * @return true if both strings are equal ignoring case\n     */\n    static bool stricmp(const std::string_view str1,\n                        const std::string_view str2)\n    {\n        auto str1Len{str1.length()};\n        auto str2Len{str2.length()};\n        if (str1Len != str2Len)\n            return false;\n        for (size_t idx{0}; idx < str1Len; ++idx)\n        {\n            auto lowerChar{tolower(str1[idx])};\n\n            if (lowerChar != str2[idx])\n            {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * @brief Converts a string value to its associated enum class SameSite\n     * value\n     */\n    static SameSite convertString2SameSite(std::string_view sameSite)\n    {\n        if (stricmp(sameSite, \"lax\"))\n            return Cookie::SameSite::kLax;\n        if (stricmp(sameSite, \"strict\"))\n            return Cookie::SameSite::kStrict;\n        if (stricmp(sameSite, \"none\"))\n            return Cookie::SameSite::kNone;\n        if (!stricmp(sameSite, \"null\"))\n        {\n            LOG_WARN\n                << \"'\" << sameSite\n                << \"' is not a valid SameSite policy. 'Null', 'Lax', 'Strict' \"\n                   \"or \"\n                   \"'None' are proper values. Return value is SameSite::kNull.\";\n        }\n        return Cookie::SameSite::kNull;\n    }\n\n    /**\n     * @brief Converts an enum class SameSite value to its associated string\n     * value\n     */\n    static std::string_view convertSameSite2String(SameSite sameSite)\n    {\n        switch (sameSite)\n        {\n            case SameSite::kLax:\n                return \"Lax\";\n            case SameSite::kStrict:\n                return \"Strict\";\n            case SameSite::kNone:\n                return \"None\";\n            case SameSite::kNull:\n                return \"Null\";\n            default:\n                return \"UNDEFINED\";\n        }\n    }\n\n  private:\n    trantor::Date expiresDate_{(std::numeric_limits<int64_t>::max)()};\n    bool httpOnly_{true};\n    bool secure_{false};\n    bool partitioned_{false};\n    std::string domain_;\n    std::string path_;\n    std::string key_;\n    std::string value_;\n    std::optional<int> maxAge_;\n    SameSite sameSite_{SameSite::kNull};\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/DrClassMap.h",
    "content": "/**\n *\n *  @file DrClassMap.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <trantor/utils/Logger.h>\n#include <functional>\n#include <memory>\n#include <mutex>\n#include <thread>\n#include <unordered_map>\n#include <vector>\n#include <type_traits>\n#include <cstdlib>\n#ifndef _MSC_VER\n#include <cxxabi.h>\n#endif\n#include <stdio.h>\n\nnamespace drogon\n{\nclass DrObjectBase;\nusing DrAllocFunc = std::function<DrObjectBase *()>;\nusing DrSharedAllocFunc = std::function<std::shared_ptr<DrObjectBase>()>;\n\n/**\n * @brief A map class which can create DrObjects from names.\n */\nclass DROGON_EXPORT DrClassMap\n{\n  public:\n    /**\n     * @brief Register a class into the map\n     *\n     * @param className The name of the class\n     * @param func The function which can create a new instance of the class.\n     */\n    static void registerClass(const std::string &className,\n                              const DrAllocFunc &func,\n                              const DrSharedAllocFunc &sharedFunc = nullptr);\n\n    /**\n     * @brief Create a new instance of the class named by className\n     *\n     * @param className The name of the class\n     * @return DrObjectBase* The pointer to the newly created instance.\n     */\n    static DrObjectBase *newObject(const std::string &className);\n\n    /**\n     * @brief Get the shared_ptr instance of the class named by className\n     */\n    static std::shared_ptr<DrObjectBase> newSharedObject(\n        const std::string &className);\n\n    /**\n     * @brief Get the singleton object of the class named by className\n     *\n     * @param className The name of the class\n     * @return const std::shared_ptr<DrObjectBase>& The smart pointer to the\n     * instance.\n     */\n    static const std::shared_ptr<DrObjectBase> &getSingleInstance(\n        const std::string &className);\n\n    /**\n     * @brief Get the singleton T type object\n     *\n     * @tparam T The type of the class\n     * @return std::shared_ptr<T> The smart pointer to the instance.\n     * @note The T must be a subclass of the DrObjectBase class.\n     */\n    template <typename T>\n    static std::shared_ptr<T> getSingleInstance()\n    {\n        static_assert(std::is_base_of<DrObjectBase, T>::value,\n                      \"T must be a sub-class of DrObjectBase\");\n        static auto const singleton =\n            std::dynamic_pointer_cast<T>(getSingleInstance(T::classTypeName()));\n        assert(singleton);\n        return singleton;\n    }\n\n    /**\n     * @brief Set a singleton object into the map.\n     *\n     * @param ins The smart pointer to the instance.\n     */\n    static void setSingleInstance(const std::shared_ptr<DrObjectBase> &ins);\n\n    /**\n     * @brief Get all names of classes registered in the map.\n     *\n     * @return std::vector<std::string> the vector of class names.\n     */\n    static std::vector<std::string> getAllClassName();\n\n    /**\n     * @brief demangle the type name which is returned by typeid(T).name().\n     *\n     * @param mangled_name The type name which is returned by typeid(T).name().\n     * @return std::string The human readable type name.\n     */\n    static std::string demangle(const char *mangled_name)\n    {\n#ifndef _MSC_VER\n        std::size_t len = 0;\n        int status = 0;\n        std::unique_ptr<char, decltype(&std::free)> ptr(\n            __cxxabiv1::__cxa_demangle(mangled_name, nullptr, &len, &status),\n            &std::free);\n        if (status == 0)\n        {\n            return std::string(ptr.get());\n        }\n        LOG_ERROR << \"Demangle error!\";\n        return \"\";\n#else\n        auto pos = strstr(mangled_name, \" \");\n        if (pos == nullptr)\n            return std::string{mangled_name};\n        else\n            return std::string{pos + 1};\n#endif\n    }\n\n  protected:\n    static std::unordered_map<std::string,\n                              std::pair<DrAllocFunc, DrSharedAllocFunc>> &\n    getMap();\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/DrObject.h",
    "content": "/**\n *\n *  @file DrObject.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <drogon/DrClassMap.h>\n\n#include <string>\n#include <type_traits>\n\n#ifdef _MSC_VER\n#pragma warning(disable : 4250)\n#endif\n\nnamespace drogon\n{\n/**\n * @brief The base class for all drogon reflection classes.\n *\n */\nclass DROGON_EXPORT DrObjectBase\n{\n  public:\n    /**\n     * @brief Get the class name\n     *\n     * @return const std::string& the class name\n     */\n    virtual const std::string &className() const\n    {\n        static const std::string name{\"DrObjectBase\"};\n        return name;\n    }\n\n    /**\n     * @brief Return true if the class name is 'class_name'\n     */\n    virtual bool isClass(const std::string &class_name) const\n    {\n        return (className() == class_name);\n    }\n\n    virtual ~DrObjectBase()\n    {\n    }\n};\n\ntemplate <typename T>\nstruct isAutoCreationClass\n{\n    template <class C>\n    static constexpr auto check(C *) -> std::enable_if_t<\n        std::is_same_v<decltype(C::isAutoCreation), const bool>,\n        bool>\n    {\n        return C::isAutoCreation;\n    }\n\n    template <typename>\n    static constexpr bool check(...)\n    {\n        return false;\n    }\n\n    static constexpr bool value = check<T>(nullptr);\n};\n\n/**\n * a class template to\n * implement the reflection function of creating the class object by class name\n */\ntemplate <typename T>\nclass DrObject : public virtual DrObjectBase\n{\n  public:\n    const std::string &className() const override\n    {\n        return alloc_.className();\n    }\n\n    static const std::string &classTypeName()\n    {\n        return alloc_.className();\n    }\n\n    bool isClass(const std::string &class_name) const override\n    {\n        return (className() == class_name);\n    }\n\n  protected:\n    // protect constructor to make this class only inheritable\n    DrObject() = default;\n    ~DrObject() override = default;\n\n  private:\n    class DrAllocator\n    {\n      public:\n        DrAllocator()\n        {\n            registerClass<T>();\n        }\n\n        const std::string &className() const\n        {\n            static std::string className =\n                DrClassMap::demangle(typeid(T).name());\n            return className;\n        }\n\n        template <typename D>\n        void registerClass()\n        {\n            if constexpr (std::is_default_constructible<D>::value)\n            {\n                DrClassMap::registerClass(\n                    className(),\n                    []() -> DrObjectBase * { return new T; },\n                    []() -> std::shared_ptr<DrObjectBase> {\n                        return std::make_shared<T>();\n                    });\n            }\n            else if constexpr (isAutoCreationClass<D>::value)\n            {\n                static_assert(std::is_default_constructible<D>::value,\n                              \"Class is not default constructable!\");\n            }\n        }\n    };\n\n    // use static val to register allocator function for class T;\n    static DrAllocator alloc_;\n};\n\ntemplate <typename T>\ntypename DrObject<T>::DrAllocator DrObject<T>::alloc_;\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/DrTemplate.h",
    "content": "/**\n *\n *  DrTemplate.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include <drogon/DrTemplateBase.h>\n\nnamespace drogon\n{\ntemplate <typename T>\nclass DrTemplate : public DrObject<T>, public DrTemplateBase\n{\n  protected:\n    DrTemplate()\n    {\n    }\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/DrTemplateBase.h",
    "content": "/**\n *\n *  @file DrTemplateBase.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <drogon/DrObject.h>\n#include <drogon/HttpViewData.h>\n#include <memory>\n#include <string>\n\nnamespace drogon\n{\nusing DrTemplateData = HttpViewData;\n\n/// The templating engine class\n/**\n * This class can generate a text string from the template file and template\n * data.\n * For more details on the template file, see the wiki site (the 'View' section)\n */\nclass DROGON_EXPORT DrTemplateBase : public virtual DrObjectBase\n{\n  public:\n    /// Create an object of the implementation class\n    /**\n     * @param templateName represents the name of the template file. A template\n     * file is a description file with a special format. Its extension is\n     * usually .csp. The user should preprocess the template file with the\n     * drogon_ctl tool to create c++ source files.\n     */\n    static std::shared_ptr<DrTemplateBase> newTemplate(\n        const std::string &templateName);\n\n    /// Generate the text string\n    /**\n     * @param data represents data rendered in the string in a format\n     * according to the template file.\n     */\n    virtual std::string genText(\n        const DrTemplateData &data = DrTemplateData()) = 0;\n\n    virtual ~DrTemplateBase(){};\n    DrTemplateBase(){};\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/HttpAppFramework.h",
    "content": "/**\n *\n *  @file HttpAppFramework.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#ifdef __cpp_impl_coroutine\n#include <drogon/utils/coroutine.h>\n#endif\n#include <drogon/exports.h>\n#include <drogon/utils/HttpConstraint.h>\n#include <drogon/CacheMap.h>\n#include <drogon/DrObject.h>\n#include <drogon/HttpBinder.h>\n#include <drogon/HttpFilter.h>\n#include <drogon/MultiPart.h>\n#include <drogon/NotFound.h>\n#include <drogon/drogon_callbacks.h>\n#include <drogon/utils/Utilities.h>\n#include <drogon/plugins/Plugin.h>\n#include <drogon/HttpRequest.h>\n#include <drogon/HttpResponse.h>\n#include <drogon/orm/DbClient.h>\n#include <drogon/orm/DbConfig.h>\n#include <drogon/nosql/RedisClient.h>\n#include <drogon/Cookie.h>\n#include <trantor/net/Resolver.h>\n#include <trantor/net/EventLoop.h>\n#include <trantor/utils/NonCopyable.h>\n#include <functional>\n#include <memory>\n#include <string>\n#include <type_traits>\n#include <vector>\n#include <chrono>\n\n#if defined(__APPLE__) && defined(__MACH__) && \\\n    (defined(__ENVIRONMENT_IPHONE_OS__) ||     \\\n     defined(__IPHONE_OS_VERSION_MIN_REQUIRED))\n// iOS\n#define TARGET_OS_IOS 1\n#else\n// not iOS\n#define TARGET_OS_IOS 0\n#endif\n\nnamespace drogon\n{\n// the drogon banner\nconst char banner[] =\n    \"     _                             \\n\"\n    \"  __| |_ __ ___   __ _  ___  _ __  \\n\"\n    \" / _` | '__/ _ \\\\ / _` |/ _ \\\\| '_ \\\\ \\n\"\n    \"| (_| | | | (_) | (_| | (_) | | | |\\n\"\n    \" \\\\__,_|_|  \\\\___/ \\\\__, |\\\\___/|_| |_|\\n\"\n    \"                 |___/             \\n\";\n\nDROGON_EXPORT std::string getVersion();\nDROGON_EXPORT std::string getGitCommit();\n\nclass HttpControllerBase;\nclass HttpSimpleControllerBase;\nclass WebSocketControllerBase;\nusing ExceptionHandler =\n    std::function<void(const std::exception &,\n                       const HttpRequestPtr &,\n                       std::function<void(const HttpResponsePtr &)> &&)>;\nusing DefaultHandler =\n    std::function<void(const HttpRequestPtr &,\n                       std::function<void(const HttpResponsePtr &)> &&)>;\nusing HttpHandlerInfo = std::tuple<std::string, HttpMethod, std::string>;\n\n#ifdef __cpp_impl_coroutine\nclass HttpAppFramework;\n\nnamespace internal\n{\nstruct [[nodiscard]] ForwardAwaiter\n    : public CallbackAwaiter<drogon::HttpResponsePtr>\n{\n  public:\n    ForwardAwaiter(drogon::HttpRequestPtr &&req,\n                   std::string &&host,\n                   double timeout,\n                   HttpAppFramework &app)\n        : req_(std::move(req)),\n          host_(std::move(host)),\n          timeout_(timeout),\n          app_(app)\n    {\n    }\n\n    void await_suspend(std::coroutine_handle<> handle) noexcept;\n\n  private:\n    drogon::HttpRequestPtr req_;\n    std::string host_;\n    double timeout_;\n    HttpAppFramework &app_;\n};\n}  // namespace internal\n#endif\nclass DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable\n{\n  public:\n    virtual ~HttpAppFramework() = default;\n    /// Get the instance of HttpAppFramework\n    /**\n     * HttpAppFramework works at singleton mode, so any calling of this\n     * method gets the same instance;\n     * Calling drogon::HttpAppFramework::instance()\n     * can be replaced by a simple interface -- drogon::app()\n     */\n    static HttpAppFramework &instance();\n\n    /// Run the event loop\n    /**\n     * Calling this method starts the IO event loops and the main loop of the\n     * application;\n     * This method can be called in the main thread or any other thread.\n     * This method blocks the current thread until the main event loop exits.\n     */\n    virtual void run() = 0;\n\n    /// Return true if the framework is running\n    virtual bool isRunning() = 0;\n\n    /// Quit the event loop\n    /**\n     * Calling this method results in stopping all network IO in the\n     * framework and interrupting the blocking of the run() method. Usually,\n     * after calling this method, the application exits (when the run() method\n     * is called in the main thread).\n     *\n     * @note\n     * This method can be called in any thread and anywhere.\n     * This method should not be called before calling run().\n     */\n    virtual void quit() = 0;\n\n    /// Get the main event loop of the framework;\n    /**\n     * @note\n     * The event loop is not the network IO loop, but the main event loop\n     * of the framework in which only some timer tasks are running;\n     * User can run some timer tasks or other tasks in this loop;\n     * This method can be call in any thread.\n     */\n    virtual trantor::EventLoop *getLoop() const = 0;\n\n    /// Get an IO loop with id. E.g. 0 <= id < \\#Total thread-loops\n    /**\n     * @note\n     * The event loop is one of the network IO loops. Use the loop\n     * for events/actions rather then the main thread.\n     * REMARKS : Function assumed the number of threads will not exceed 2^32.\n     *           Change to long long for alien computers.\n     */\n    virtual trantor::EventLoop *getIOLoop(size_t id) const = 0;\n\n    /// Set custom 404 page\n    /**\n     * @param resp is the object set to 404 response\n     * After calling this method, the resp object is returned\n     * by the HttpResponse::newNotFoundResponse() method.\n     * @param set404 if true, the status code of the resp will\n     * be set to 404 automatically\n     */\n    virtual HttpAppFramework &setCustom404Page(const HttpResponsePtr &resp,\n                                               bool set404 = true) = 0;\n\n    /// Set custom error handler\n    /**\n     * @param resp_generator is invoked when an error in the framework needs to\n     * be sent to the client to provide a custom layout.\n     */\n    virtual HttpAppFramework &setCustomErrorHandler(\n        std::function<HttpResponsePtr(HttpStatusCode,\n                                      const HttpRequestPtr &req)>\n            &&resp_generator) = 0;\n\n    HttpAppFramework &setCustomErrorHandler(\n        std::function<HttpResponsePtr(HttpStatusCode)> &&resp_generator)\n    {\n        return setCustomErrorHandler(\n            [cb = std::move(resp_generator)](HttpStatusCode code,\n                                             const HttpRequestPtr &) {\n                return cb(code);\n            });\n    }\n\n    /// Get custom error handler\n    /**\n     * @return A const-reference to the error handler set using\n     * setCustomErrorHandler. If none was provided, the default error handler is\n     * returned.\n     */\n    virtual const std::function<HttpResponsePtr(HttpStatusCode,\n                                                const HttpRequestPtr &req)> &\n    getCustomErrorHandler() const = 0;\n\n    /// Get the plugin object registered in the framework\n    /**\n     * @note\n     * This method is usually called after the framework runs.\n     * Calling this method in the initAndStart() method of plugins is also\n     * valid.\n     */\n    template <typename T>\n    T *getPlugin()\n    {\n        static_assert(IsPlugin<T>::value,\n                      \"The Template parameter must be a subclass of \"\n                      \"PluginBase\");\n        assert(isRunning());\n        static auto pluginPtr =\n            dynamic_cast<T *>(getPlugin(T::classTypeName()));\n        return pluginPtr;\n    }\n\n    /// Get the shared_ptr plugin object registered in the framework\n    /**\n     * @note\n     * This method is usually called after the framework runs.\n     * Calling this method in the initAndStart() method of plugins is also\n     * valid.\n     */\n    template <typename T>\n    std::shared_ptr<T> getSharedPlugin()\n    {\n        static_assert(IsPlugin<T>::value,\n                      \"The Template parameter must be a subclass of \"\n                      \"PluginBase\");\n        assert(isRunning());\n        static auto pluginPtr =\n            std::dynamic_pointer_cast<T>(getSharedPlugin(T::classTypeName()));\n        return pluginPtr;\n    }\n\n    /// @brief the plugin object registered in the framework\n    /**\n     * @param name is the class name of the plugin.\n     *\n     * @note\n     * This method is usually called after the framework runs.\n     * Calling this method in the initAndStart() method of plugins is also\n     * valid.\n     */\n    virtual PluginBase *getPlugin(const std::string &name) = 0;\n\n    /**\n     * @brief Get the shared_ptr plugin object registered in the framework\n     *\n     * @note\n     * This method is usually called after the framework runs.\n     * Calling this method in the initAndStart() method of plugins is also\n     * valid.\n     */\n    virtual std::shared_ptr<PluginBase> getSharedPlugin(\n        const std::string &name) = 0;\n\n    /* The following is a series of methods of AOP */\n\n    /// Register a beginning advice\n    /**\n     * @param advice is called immediately after the main event loop runs.\n     */\n    virtual HttpAppFramework &registerBeginningAdvice(\n        const std::function<void()> &advice) = 0;\n\n    /// Register an advice for new connections\n    /**\n     * @param advice is called immediately when a new connection is\n     * established. the first parameter of it is the remote address of the new\n     * connection, the second one is the local address of it.\n     * If the advice returns a false value, drogon closes the connection.\n     * Users can use this advice to implement some security policies.\n     */\n    virtual HttpAppFramework &registerNewConnectionAdvice(\n        const std::function<bool(const trantor::InetAddress &,\n                                 const trantor::InetAddress &)> &advice) = 0;\n\n    /**\n     * @brief Register an advice for new HTTP responses.\n     *\n     * @param advice is called immediately when a new HTTP response is created.\n     * Users can use the callback to modify the response if they want.\n     * @note This advice is called before any subsequent operation on the\n     * response is performed by drogon or applications, so some modification\n     * (e.g. modification on the status code) in this callback may be override\n     * by subsequent operations.\n     * @return HttpAppFramework&\n     */\n    virtual HttpAppFramework &registerHttpResponseCreationAdvice(\n        const std::function<void(const HttpResponsePtr &)> &advice) = 0;\n\n    /// Register a synchronous advice\n    /**\n     * @param advice is called immediately after the request is created. If a\n     * no-empty response is returned by the advice, it is sent to the client and\n     * no handler is invoked.\n     *\n     * @note The following diagram shows the location of the\n     * AOP join points during http request processing.\n     *\n     * @code\n                         +-----------+                             +----------+\n                         |  Request  |                             | Response |\n                         +-----------+                             +----------+\n                               |                                         ^\n                               v                                         |\n               sync join point o----------->[HttpResponsePtr]----------->+\n                               |                                         |\n                               v                                         |\n        Pre-routing join point o----------->[Advice callback]----------->+\n                               |                                         |\n                               v         Invalid path                    |\n                         [Find Handler]---------------->[404]----------->+\n                               |                                         |\n                               v                                         |\n       Post-routing join point o----------->[Advice callback]----------->+\n                               |                                         |\n                               v        Invalid method                   |\n                         [Check Method]---------------->[405]----------->+\n                               |                                         |\n                               v                                         |\n                     [Filters/Middlewares]------>[Filter callback]------>+\n                               |                                         |\n                               v             Y                           |\n                      [Is OPTIONS method?]------------->[200]----------->+\n                               |                                         |\n                               v                                         |\n       Pre-handling join point o----------->[Advice callback]----------->+\n                               |                                         |\n                               v                                         |\n                           [Handler]                                     |\n                               |                                         |\n                               v                                         |\n      Post-handling join point o---------------------------------------->+\n                               |                                         |\n                               v                                         |\n                    [Middlewares post logic]--->[Middleware callback]--->+\n\n      @endcode\n     *\n     */\n    virtual HttpAppFramework &registerSyncAdvice(\n        const std::function<HttpResponsePtr(const HttpRequestPtr &)>\n            &advice) = 0;\n\n    /// Register an advice called before routing\n    /**\n     * @param advice is called after all the synchronous advice return\n     * nullptr and before the request is routed to any handler. The parameters\n     * of the advice are same as those of the doFilter method of the Filter\n     * class.\n     */\n    virtual HttpAppFramework &registerPreRoutingAdvice(\n        const std::function<void(const HttpRequestPtr &,\n                                 AdviceCallback &&,\n                                 AdviceChainCallback &&)> &advice) = 0;\n\n    /// Register an observer called before routing\n    /**\n     * @param advice is called at the same time as the above advice. It can be\n     * thought of as an observer who cannot respond to http requests.\n     * @note This advice has less overhead than the above one. If one does not\n     * intend to intercept the http request, please use this interface.\n     */\n    virtual HttpAppFramework &registerPreRoutingAdvice(\n        const std::function<void(const HttpRequestPtr &)> &advice) = 0;\n\n    /// Register an advice called after routing\n    /**\n     * @param advice is called immediately after the request matches a handler\n     * path and before any filters/middlewares applies. The parameters\n     * of the advice are same as those of the doFilter method of the Filter\n     * class.\n     */\n    virtual HttpAppFramework &registerPostRoutingAdvice(\n        const std::function<void(const HttpRequestPtr &,\n                                 AdviceCallback &&,\n                                 AdviceChainCallback &&)> &advice) = 0;\n\n    /// Register an observer called after routing\n    /**\n     * @param advice is called at the same time as the above advice. It can be\n     * thought of as an observer who cannot respond to http requests.\n     * @note This advice has less overhead than the above one. If one does not\n     * intend to intercept the http request, please use this interface.\n     */\n    virtual HttpAppFramework &registerPostRoutingAdvice(\n        const std::function<void(const HttpRequestPtr &)> &advice) = 0;\n\n    /// Register an advice called before the request is handled\n    /**\n     * @param advice is called immediately after the request is approved by all\n     * filters/middlewares and before it is handled. The parameters of the\n     * advice are same as those of the doFilter method of the Filter class.\n     */\n    virtual HttpAppFramework &registerPreHandlingAdvice(\n        const std::function<void(const HttpRequestPtr &,\n                                 AdviceCallback &&,\n                                 AdviceChainCallback &&)> &advice) = 0;\n\n    /// Register an observer called before the request is handled\n    /**\n     * @param advice is called at the same time as the above advice. It can be\n     * thought of as an observer who cannot respond to http requests. This\n     * advice has less overhead than the above one. If one does not intend to\n     * intercept the http request, please use this interface.\n     */\n    virtual HttpAppFramework &registerPreHandlingAdvice(\n        const std::function<void(const HttpRequestPtr &)> &advice) = 0;\n\n    /// Register an advice called after the request is handled\n    /**\n     * @param advice is called immediately after the request is handled and\n     * a response object is created by handlers.\n     */\n    virtual HttpAppFramework &registerPostHandlingAdvice(\n        const std::function<void(const HttpRequestPtr &,\n                                 const HttpResponsePtr &)> &advice) = 0;\n\n    /// Register an advice called before a response is sent to the client.\n    /**\n     * @note This advice is different from the PostHandlingAdvice, responses to\n     * static resources are also handled here.\n     */\n    virtual HttpAppFramework &registerPreSendingAdvice(\n        const std::function<void(const HttpRequestPtr &,\n                                 const HttpResponsePtr &)> &advice) = 0;\n\n    /// Setup output of logs to files\n    /**\n     * @note\n     * Logs are output to the standard output by default.\n     * Logging is setuped only if output path of logs is defined.\n     * This method is called in run() function, hence use this method only if\n     * you want to setup logging earlier.\n     * @return HttpAppFramework&\n     */\n    virtual HttpAppFramework &setupFileLogger() = 0;\n\n    /* End of AOP methods */\n\n    /// Load the configuration file with json format.\n    /**\n     * @param fileName the configuration file\n     */\n    virtual HttpAppFramework &loadConfigFile(\n        const std::string &fileName) noexcept(false) = 0;\n\n    /// Load the configuration from a Json::Value Object.\n    /**\n     * @param data Json::Value Object containing the configuration.\n     * @note Please refer to the configuration file for the content of the json\n     * object.\n     */\n    virtual HttpAppFramework &loadConfigJson(const Json::Value &data) noexcept(\n        false) = 0;\n\n    /// Load the configuration from a Json::Value Object.\n    /**\n     * @param data rvalue reference to a Json::Value object containing the\n     * configuration.\n     * @note Please refer to the configuration file for the content of the json\n     * object.\n     */\n    virtual HttpAppFramework &loadConfigJson(Json::Value &&data) noexcept(\n        false) = 0;\n\n    /// Register a HttpSimpleController object into the framework.\n    /**\n     * @param pathName When the path of a http request is equal to the\n     * pathName, the asyncHandleHttpRequest() method of the controller is\n     * called.\n     * @param ctrlName is the name of the controller. It includes the namespace\n     * to which the controller belongs.\n     * @param constraints is a vector containing Http methods or middleware\n     names\n     *\n     *   Example:\n     * @code\n       app.registerHttpSimpleController(\"/userinfo\",\"UserInfoCtrl\",{Get,\"LoginFilter\"});\n       @endcode\n     *\n     * @note\n     * Users can perform the same operation through the configuration file or a\n     * macro in the header file.\n     */\n    virtual HttpAppFramework &registerHttpSimpleController(\n        const std::string &pathName,\n        const std::string &ctrlName,\n        const std::vector<internal::HttpConstraint> &constraints = {}) = 0;\n\n    /// Register a handler into the framework.\n    /**\n     * @param pathPattern When the path of a http request matches the\n     * pathPattern, the handler indicated by the function parameter is called.\n     * @param function indicates any type of callable object with a valid\n     * processing interface.\n     * @param constraints is the same as the third parameter in the above\n     * method.\n     *\n     *   Example:\n     * @code\n       app().registerHandler(\"/hello?username={1}\",\n                             [](const HttpRequestPtr& req,\n                                std::function<void (const HttpResponsePtr&)>\n     &&callback, const std::string &name)\n                                {\n                                    Json::Value json;\n                                    json[\"result\"]=\"ok\";\n                                    json[\"message\"]=std::string(\"hello,\")+name;\n                                    auto\n     resp=HttpResponse::newHttpJsonResponse(json); callback(resp);\n                                },\n                             {Get,\"LoginFilter\"});\n     @endcode\n     * @note\n     * As you can see in the above example, this method supports parameters\n     * mapping.\n     */\n    template <typename FUNCTION>\n    HttpAppFramework &registerHandler(\n        const std::string &pathPattern,\n        FUNCTION &&function,\n        const std::vector<internal::HttpConstraint> &constraints = {},\n        const std::string &handlerName = \"\")\n    {\n        LOG_TRACE << \"pathPattern:\" << pathPattern;\n        auto binder = std::make_shared<internal::HttpBinder<FUNCTION>>(\n            std::forward<FUNCTION>(function));\n\n        getLoop()->queueInLoop([binder]() { binder->createHandlerInstance(); });\n\n        std::vector<HttpMethod> validMethods;\n        std::vector<std::string> middlewares;\n        for (auto const &constraint : constraints)\n        {\n            if (constraint.type() == internal::ConstraintType::HttpMiddleware)\n            {\n                middlewares.push_back(constraint.getMiddlewareName());\n            }\n            else if (constraint.type() == internal::ConstraintType::HttpMethod)\n            {\n                validMethods.push_back(constraint.getHttpMethod());\n            }\n            else\n            {\n                LOG_ERROR << \"Invalid controller constraint type\";\n                exit(1);\n            }\n        }\n        registerHttpController(\n            pathPattern, binder, validMethods, middlewares, handlerName);\n        return *this;\n    }\n\n    /**\n     * @brief Register a handler into the framework via a regular expression.\n     *\n     * @param regExp A regular expression string, when the path of a http\n     * request matches the regular expression, the handler indicated by the\n     * function parameter is called.\n     * @note When the match is successful, Each string that matches a\n     * subexpression is sequentially mapped to a handler parameter.\n     * @param function indicates any type of callable object with a valid\n     * processing interface.\n     * @param constraints is the same as the third parameter in the\n     * above method.\n     * @param handlerName a name for the handler.\n     * @return HttpAppFramework&\n     */\n    template <typename FUNCTION>\n    HttpAppFramework &registerHandlerViaRegex(\n        const std::string &regExp,\n        FUNCTION &&function,\n        const std::vector<internal::HttpConstraint> &constraints = {},\n        const std::string &handlerName = \"\")\n    {\n        LOG_TRACE << \"regex:\" << regExp;\n        internal::HttpBinderBasePtr binder;\n\n        binder = std::make_shared<internal::HttpBinder<FUNCTION>>(\n            std::forward<FUNCTION>(function));\n\n        std::vector<HttpMethod> validMethods;\n        std::vector<std::string> middlewares;\n        for (auto const &constraint : constraints)\n        {\n            if (constraint.type() == internal::ConstraintType::HttpMiddleware)\n            {\n                middlewares.push_back(constraint.getMiddlewareName());\n            }\n            else if (constraint.type() == internal::ConstraintType::HttpMethod)\n            {\n                validMethods.push_back(constraint.getHttpMethod());\n            }\n            else\n            {\n                LOG_ERROR << \"Invalid controller constraint type\";\n                exit(1);\n            }\n        }\n        registerHttpControllerViaRegex(\n            regExp, binder, validMethods, middlewares, handlerName);\n        return *this;\n    }\n\n    /// Register a WebSocketController into the framework.\n    /**\n     * The parameters of this method are the same as those in the\n     * registerHttpSimpleController() method.\n     */\n    virtual HttpAppFramework &registerWebSocketController(\n        const std::string &pathName,\n        const std::string &ctrlName,\n        const std::vector<internal::HttpConstraint> &constraints = {}) = 0;\n\n    /// Register a WebSocketController into the framework.\n    /**\n     * The parameters of this method are the same as those in the\n     * registerHttpSimpleController() method but using regular\n     * expression string for path.\n     */\n    virtual HttpAppFramework &registerWebSocketControllerRegex(\n        const std::string &regExp,\n        const std::string &ctrlName,\n        const std::vector<internal::HttpConstraint> &constraints =\n            std::vector<internal::HttpConstraint>{}) = 0;\n\n    /// Register controller objects created and initialized by the user\n    /**\n     * @details Drogon can only automatically create controllers using the\n     * default constructor. Sometimes users want to be able to create\n     * controllers using constructors with parameters. Controllers created by\n     * user in this way should be registered to the framework via this method.\n     * The macro or configuration file is still valid for the path routing\n     * configuration of the controller created by users.\n     *\n     * @note\n     * The declaration of the controller class must be as follows:\n     * @code\n        class ApiTest : public drogon::HttpController<ApiTest, false>\n        {\n            public:\n                ApiTest(const std::string &str);\n            ...\n        };\n       @endcode\n     * The second template parameter must be explicitly set to false to disable\n     * automatic creation.\n     * And then user can create and register it somewhere as follows:\n     * @code\n        auto ctrlPtr=std::make_shared<ApiTest>(\"hello world\");\n        drogon::app().registerController(ctrlPtr);\n       @endcode\n     * This method should be called before calling the app().run() method.\n     */\n    template <typename T>\n    HttpAppFramework &registerController(const std::shared_ptr<T> &ctrlPtr)\n    {\n        static_assert((std::is_base_of<HttpControllerBase, T>::value ||\n                       std::is_base_of<HttpSimpleControllerBase, T>::value ||\n                       std::is_base_of<WebSocketControllerBase, T>::value),\n                      \"Error! Only controller objects can be registered here\");\n        static_assert(!T::isAutoCreation,\n                      \"Controllers created and initialized \"\n                      \"automatically by drogon cannot be \"\n                      \"registered here\");\n        DrClassMap::setSingleInstance(ctrlPtr);\n        T::initPathRouting();\n        return *this;\n    }\n\n    /// Register filter objects created and initialized by the user\n    /**\n     * This method is similar to the above method.\n     */\n    template <typename T>\n    HttpAppFramework &registerFilter(const std::shared_ptr<T> &filterPtr)\n    {\n        static_assert(std::is_base_of<HttpFilterBase, T>::value,\n                      \"Error! Only filter objects can be registered here\");\n        static_assert(!T::isAutoCreation,\n                      \"Filters created and initialized \"\n                      \"automatically by drogon cannot be \"\n                      \"registered here\");\n        DrClassMap::setSingleInstance(filterPtr);\n        return *this;\n    }\n\n    /// Register middleware objects created and initialized by the user\n    /**\n     * This method is similar to the above method.\n     */\n    template <typename T>\n    HttpAppFramework &registerMiddleware(\n        const std::shared_ptr<T> &middlewarePtr)\n    {\n        static_assert(std::is_base_of<HttpMiddlewareBase, T>::value,\n                      \"Error! Only middleware objects can be registered here\");\n        static_assert(!T::isAutoCreation,\n                      \"Middleware created and initialized \"\n                      \"automatically by drogon cannot be \"\n                      \"registered here\");\n        DrClassMap::setSingleInstance(middlewarePtr);\n        return *this;\n    }\n\n    /// Register a default handler into the framework when no handler matches\n    /// the request. If set, it is executed if the static file router does\n    /// not find any file corresponding to the request. Thus it replaces\n    /// the default 404 not found response.\n    /**\n     * @param handler function indicates any type of callable object with\n     * a valid processing interface.\n     */\n    virtual HttpAppFramework &setDefaultHandler(DefaultHandler handler) = 0;\n\n    /// Forward the http request\n    /**\n     * @param req the HTTP request to be forwarded;\n     * @param hostString is the address where the request is forwarded. The\n     * following strings are valid for the parameter:\n     *\n     *  @code\n        https://www.baidu.com\n        http://www.baidu.com\n        https://127.0.0.1:8080/\n        http://127.0.0.1\n        http://[::1]:8080/\n        @endcode\n     *\n     * @param timeout See the timeout parameter of the sendRequest method of the\n     * HttpClient class. this parameter is only valid when the hostString is not\n     * empty.\n     * @param callback is called when the response is created.\n     *\n     * @note\n     * If the hostString parameter is empty, the request is handled by the same\n     * application, so in this condition\n     * one should modify the path of the req parameter before forwarding to\n     * avoid infinite loop processing.\n     *\n     * This method can be used to implement reverse proxy or redirection on the\n     * server side.\n     */\n    virtual void forward(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback,\n        const std::string &hostString = \"\",\n        double timeout = 0) = 0;\n#ifdef __cpp_impl_coroutine\n    /**\n     * @brief Forward the http request, this is the coroutine version of the\n     * above method.\n     */\n    internal::ForwardAwaiter forwardCoro(HttpRequestPtr req,\n                                         std::string hostString = \"\",\n                                         double timeout = 0)\n    {\n        return internal::ForwardAwaiter(std::move(req),\n                                        std::move(hostString),\n                                        timeout,\n                                        *this);\n    }\n#endif\n    /// Get information about the handlers registered to drogon\n    /**\n     * @return\n     * The first item of std::tuple in the return value represents the path\n     * pattern of the handler;\n     * The last item in std::tuple is the description of the handler.\n     */\n    virtual std::vector<HttpHandlerInfo> getHandlersInfo() const = 0;\n\n    /// Get the custom configuration defined by users in the configuration file.\n    virtual const Json::Value &getCustomConfig() const = 0;\n\n    /// Set the number of threads for IO event loops\n    /**\n     * @param threadNum the number of threads\n     * The default value is 1, if the parameter is 0, the number is equal to\n     * the number of CPU cores.\n     *\n     * @note\n     * This number is usually less than or equal to the number of CPU cores.\n     * This number can be configured in the configuration file.\n     */\n    virtual HttpAppFramework &setThreadNum(size_t threadNum) = 0;\n\n    /// Get the number of threads for IO event loops\n    virtual size_t getThreadNum() const = 0;\n\n    /// Set the global cert file and private key file for https\n    /// These options can be configured in the configuration file.\n    virtual HttpAppFramework &setSSLFiles(const std::string &certPath,\n                                          const std::string &keyPath) = 0;\n\n    /// Supplies file style SSL options to `SSL_CONF_cmd`. Valid options are\n    /// available at\n    /// https://www.openssl.org/docs/manmaster/man3/SSL_CONF_cmd.html\n    virtual HttpAppFramework &setSSLConfigCommands(\n        const std::vector<std::pair<std::string, std::string>>\n            &sslConfCmds) = 0;\n\n    /// Reload the global cert file and private key file for https server\n    /// Note: The goal of this method is not to make the framework\n    /// use the new SSL path, but rather to reload the new content\n    /// from the old path while the framework is still running.\n    /// Typically, when our SSL is about to expire,\n    /// we need to reload the SSL. The purpose of this function\n    /// is to use the new SSL certificate without stopping the framework.\n    virtual HttpAppFramework &reloadSSLFiles() = 0;\n\n    /// Add plugins\n    /**\n     * @param configs The plugins array\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual void addPlugins(const Json::Value &configs) = 0;\n\n    /// Add a plugin\n    /**\n     * @param name Name of the plugin\n     * @param dependencies Names of plugins this plugin depends on\n     * @param config Custom config for the plugin\n     */\n    virtual void addPlugin(const std::string &name,\n                           const std::vector<std::string> &dependencies,\n                           const Json::Value &config) = 0;\n\n    /// Add a listener for http or https service\n    /**\n     * @param ip is the ip that the listener listens on.\n     * @param port is the port that the listener listens on.\n     * @param useSSL if the parameter is true, the listener is used for the\n     * https service.\n     * @param certFile\n     * @param keyFile specify the cert file and the private key file for this\n     * listener. If they are empty, the global configuration set by the above\n     * method is used.\n     * @param useOldTLS if true, the TLS1.0/1.1 are enabled for HTTPS\n     * connections.\n     * @param sslConfCmds vector of ssl configuration key/value pairs.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &addListener(\n        const std::string &ip,\n        uint16_t port,\n        bool useSSL = false,\n        const std::string &certFile = \"\",\n        const std::string &keyFile = \"\",\n        bool useOldTLS = false,\n        const std::vector<std::pair<std::string, std::string>> &sslConfCmds =\n            {}) = 0;\n\n    /// Enable sessions supporting.\n    /**\n     * @param timeout The number of seconds which is the timeout of a session\n     * @param sameSite The default value of SameSite attribute\n     * @param cookieKey The key of the session cookie\n     *\n     * @note\n     * Session support is disabled by default.\n     * If there isn't any request from a client for timeout(>0) seconds,\n     * the session of the client is destroyed.\n     * If the timeout parameter is equal to 0, sessions will remain permanently\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &enableSession(\n        const size_t timeout = 0,\n        Cookie::SameSite sameSite = Cookie::SameSite::kNull,\n        const std::string &cookieKey = \"JSESSIONID\",\n        int maxAge = -1,\n        std::function<std::string()> idGeneratorCallback = nullptr) = 0;\n\n    /// A wrapper of the above method.\n    /**\n     *   Example: Users can set the timeout value as follows:\n     * @code\n        app().enableSession(0.2h);\n        app().enableSession(12min);\n       @endcode\n     */\n    inline HttpAppFramework &enableSession(\n        const std::chrono::duration<double> &timeout,\n        Cookie::SameSite sameSite = Cookie::SameSite::kNull,\n        const std::string &cookieKey = \"JSESSIONID\",\n        int maxAge = -1,\n        std::function<std::string()> idGeneratorCallback = nullptr)\n    {\n        return enableSession((size_t)timeout.count(),\n                             sameSite,\n                             cookieKey,\n                             maxAge,\n                             idGeneratorCallback);\n    }\n\n    /// Register an advice called when starting a new session.\n    /**\n     * @param advice is called with the session id.\n     */\n    virtual HttpAppFramework &registerSessionStartAdvice(\n        const AdviceStartSessionCallback &advice) = 0;\n\n    /// Register an advice called when destroying a session.\n    /**\n     * @param advice is called with the session id.\n     */\n    virtual HttpAppFramework &registerSessionDestroyAdvice(\n        const AdviceDestroySessionCallback &advice) = 0;\n\n    /// Disable sessions supporting.\n    /**\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &disableSession() = 0;\n\n    /// Set the root path of HTTP document, default path is ./\n    /**\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setDocumentRoot(const std::string &rootPath) = 0;\n\n    /// Get the document root directory.\n    virtual const std::string &getDocumentRoot() const = 0;\n    /**\n     * @brief Set the Static File Headers\n     *\n     * @param headers Each pair object in the vector presents the field name and\n     * field value of a header in an static file response.\n     */\n    virtual HttpAppFramework &setStaticFileHeaders(\n        const std::vector<std::pair<std::string, std::string>> &headers) = 0;\n\n    /**\n     * @brief Add a location of static files for GET requests.\n     *\n     * @param uriPrefix The URI prefix of the location prefixed with \"/\"\n     * @param defaultContentType The default content type of the static files\n     * without an extension.\n     * @param alias The location in file system, if it is prefixed with \"/\", it\n     * presents an absolute path, otherwise it presents a relative path to the\n     * document_root path.\n     * @param isCaseSensitive\n     * @param allowAll If it is set to false, only static files with a valid\n     * extension can be accessed.\n     * @param isRecursive If it is set to false, files in sub directories can't\n     * be accessed.\n     * @param middlewareNames The list of middlewares which acting on the\n     * location.\n     * @return HttpAppFramework&\n     */\n    virtual HttpAppFramework &addALocation(\n        const std::string &uriPrefix,\n        const std::string &defaultContentType = \"\",\n        const std::string &alias = \"\",\n        bool isCaseSensitive = false,\n        bool allowAll = true,\n        bool isRecursive = true,\n        const std::vector<std::string> &middlewareNames = {}) = 0;\n\n    /// Set the path to store uploaded files.\n    /**\n     * @param uploadPath is the directory where the uploaded files are\n     * stored. if it isn't prefixed with /, ./ or ../, it is relative path\n     * of document_root path, The default value is 'uploads'.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration\n     * file.\n     */\n    virtual HttpAppFramework &setUploadPath(const std::string &uploadPath) = 0;\n\n    /// Get the path to store uploaded files.\n    virtual const std::string &getUploadPath() const = 0;\n\n    /// Set types of files that can be downloaded.\n    /**\n     *   Example:\n     * @code\n       app.setFileTypes({\"html\",\"txt\",\"png\",\"jpg\"});\n       @endcode\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setFileTypes(\n        const std::vector<std::string> &types) = 0;\n\n#if !defined(_WIN32) && !TARGET_OS_IOS\n    /// Enable supporting for dynamic views loading.\n    /**\n     *\n     * @param libPaths is a vector that contains paths to view files.\n     *\n     * @param outputPath is the directory where the output source files locate.\n     * If it is set to an empty string, drogon use libPaths as output paths. If\n     * the path isn't prefixed with /, it is the relative path of the current\n     * working directory.\n     *\n     * @note\n     * It is disabled by default.\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &enableDynamicViewsLoading(\n        const std::vector<std::string> &libPaths,\n        const std::string &outputPath = \"\") = 0;\n#endif\n\n    /// Set the maximum number of all connections.\n    /**\n     * The default value is 100000.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setMaxConnectionNum(size_t maxConnections) = 0;\n\n    /// Set the maximum number of connections per remote IP.\n    /**\n     * The default value is 0 which means no limit.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setMaxConnectionNumPerIP(\n        size_t maxConnectionsPerIP) = 0;\n\n    /// Make the application run as a daemon.\n    /**\n     * Disabled by default.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &enableRunAsDaemon() = 0;\n\n    /// Disable the handling of SIGTERM signal.\n    /**\n     * Enabled by default.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     * When disabled setTermSignalHandler() is useless\n     */\n    virtual HttpAppFramework &disableSigtermHandling() = 0;\n\n    /// Make the application restart after crashing.\n    /**\n     * Disabled by default.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &enableRelaunchOnError() = 0;\n\n    /**\n     * @brief Set the output path of logs.\n     * @param logPath The path to logs - logs to console if empty.\n     * @param logfileBaseName The base name of log files - defaults to \"drogon\"\n     * if empty.\n     * @param logSize indicates the maximum size of a log file.\n     * @param maxFiles max count of log file - 0 = unlimited.\n     * @param useSpdlog Use spdlog for logging (if compiled-in).\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setLogPath(\n        const std::string &logPath,\n        const std::string &logfileBaseName = \"\",\n        size_t logSize = 100000000,\n        size_t maxFiles = 0,\n        bool useSpdlog = false) = 0;\n\n    /**\n     * @brief Set the log level.\n     * @param level is one of TRACE, DEBUG, INFO, WARN. The Default value is\n     * DEBUG.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setLogLevel(trantor::Logger::LogLevel level) = 0;\n\n    /// Set the log time display\n    /**\n     * @param on is true to display local time, false to display UTC time. The\n     * Default value is false.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setLogLocalTime(bool on) = 0;\n\n    /// Enable the sendfile system call in linux.\n    /**\n     * @param sendFile if the parameter is true, sendfile() system-call is used\n     * to send static files to clients; The default value is true.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     * Even though sendfile() is enabled, only files larger than 200k are sent\n     * this way,\n     * because the advantages of sendfile() can only be reflected in sending\n     * large files.\n     */\n    virtual HttpAppFramework &enableSendfile(bool sendFile) = 0;\n\n    /// Enable gzip compression.\n    /**\n     * @param useGzip if the parameter is true, use gzip to compress the\n     * response body's content;\n     * The default value is true.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     * After gzip is enabled, gzip is used under the following conditions:\n     * 1. The content type of response is not a binary type.\n     * 2. The content length is bigger than 1024 bytes.\n     */\n    virtual HttpAppFramework &enableGzip(bool useGzip) = 0;\n\n    /// Return true if gzip is enabled.\n    virtual bool isGzipEnabled() const = 0;\n\n    /// Enable brotli compression.\n    /**\n     * @param useBrotli if the parameter is true, use brotli to compress the\n     * response body's content;\n     * The default value is true.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     * After brotli is enabled, brotli is used under the following conditions:\n     * 1. The content type of response is not a binary type.\n     * 2. The content length is bigger than 1024 bytes.\n     */\n    virtual HttpAppFramework &enableBrotli(bool useBrotli) = 0;\n\n    /// Return true if brotli is enabled.\n    virtual bool isBrotliEnabled() const = 0;\n\n    /// Set the time in which the static file response is cached in memory.\n    /**\n     * @param cacheTime in seconds. 0 means always cached, negative means no\n     * cache\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setStaticFilesCacheTime(int cacheTime) = 0;\n\n    /// Get the time set by the above method.\n    virtual int staticFilesCacheTime() const = 0;\n\n    /// Set the lifetime of the connection without read or write\n    /**\n     * @param timeout in seconds. 60 by default. Setting the timeout to 0 means\n     * that drogon does not close idle connections.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setIdleConnectionTimeout(size_t timeout) = 0;\n\n    /// A wrapper of the above method.\n    /**\n     *   Example:\n     * Users can set the timeout value as follows:\n     * @code\n         app().setIdleConnectionTimeout(0.5h);\n         app().setIdleConnectionTimeout(30min);\n       @endcode\n     */\n    inline HttpAppFramework &setIdleConnectionTimeout(\n        const std::chrono::duration<double> &timeout)\n    {\n        return setIdleConnectionTimeout((size_t)timeout.count());\n    }\n\n    /// Set the 'server' header field in each response sent by drogon.\n    /**\n     * @param server empty string by default with which the 'server' header\n     * field is set to \"Server: drogon/version string\\r\\n\"\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setServerHeaderField(\n        const std::string &server) = 0;\n\n    /// Control if the 'Server' header is added to each HTTP response.\n    /**\n     * @note\n     * These operations can be performed by options in the configuration file.\n     * The headers are sent to clients by default.\n     */\n    virtual HttpAppFramework &enableServerHeader(bool flag) = 0;\n\n    /// Control if the 'Date' header is added to each HTTP response.\n    /**\n     * @note\n     * These operations can be performed by options in the configuration file.\n     * The headers are sent to clients by default.\n     */\n    virtual HttpAppFramework &enableDateHeader(bool flag) = 0;\n\n    /// Set the maximum number of requests that can be served through one\n    /// keep-alive connection.\n    /**\n     * After the maximum number of requests are made, the connection is closed.\n     * The default value is 0 which means no limit.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setKeepaliveRequestsNumber(\n        const size_t number) = 0;\n\n    /// Set the maximum number of unhandled requests that can be cached in\n    /// pipelining buffer.\n    /**\n     * The default value of 0 means no limit.\n     * After the maximum number of requests cached in pipelining buffer are\n     * made, the connection is closed.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setPipeliningRequestsNumber(\n        const size_t number) = 0;\n\n    /// Set the gzip_static option.\n    /**\n     * If it is set to true, when the client requests a static file, drogon\n     * first finds the compressed file with the extension \".gz\" in the same path\n     * and send the compressed file to the client. The default value is true.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setGzipStatic(bool useGzipStatic) = 0;\n\n    /// Set the br_static option.\n    /**\n     * If it is set to true, when the client requests a static file, drogon\n     * first finds the compressed file with the extension \".br\" in the same path\n     * and send the compressed file to the client. The default value is true.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setBrStatic(bool useGzipStatic) = 0;\n\n    /// Set the max body size of the requests received by drogon.\n    /**\n     * The default value is 1M.\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setClientMaxBodySize(size_t maxSize) = 0;\n\n    /// Set the maximum body size in memory of HTTP requests received by drogon.\n    /**\n     * The default value is \"64K\" bytes. If the body size of a HTTP request\n     * exceeds this limit, the body is stored to a temporary file for\n     * processing.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setClientMaxMemoryBodySize(size_t maxSize) = 0;\n\n    /// Set the max size of messages sent by WebSocket client.\n    /**\n     * The default value is 128K.\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setClientMaxWebSocketMessageSize(\n        size_t maxSize) = 0;\n\n    // Set the HTML file of the home page, the default value is \"index.html\"\n    /**\n     * If there isn't any handler registered to the path \"/\", the home page file\n     * in the \"document_root\"\n     * is send to clients as a response to the request for \"/\".\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setHomePage(const std::string &homePageFile) = 0;\n\n    /**\n     * @brief Set the TERM Signal Handler. This method provides a way to users\n     * for exiting program gracefully. When the TERM signal is received after\n     * app().run() is called, the handler is invoked. Drogon uses a default\n     * signal handler for the TERM signal, which calls the 'app().quit()' method\n     * when the TERM signal is received.\n     *\n     * @param handler\n     * @return HttpAppFramework&\n     */\n    virtual HttpAppFramework &setTermSignalHandler(\n        const std::function<void()> &handler) = 0;\n\n    /**\n     * @brief Set the INT Signal Handler. This method provides a way to users\n     * for exiting program gracefully. When the INT signal is received after\n     * app().run() is called, the handler is invoked. Drogon uses a default\n     * signal handler for the INT signal, which calls the 'app().quit()' method\n     * when the INT signal is received.\n     *\n     * @param handler\n     * @return HttpAppFramework&\n     */\n    virtual HttpAppFramework &setIntSignalHandler(\n        const std::function<void()> &handler) = 0;\n\n    /// Get homepage, default is \"index.html\"\n    /**\n     * @note\n     * This method must be called after the framework has been run.\n     */\n    virtual const std::string &getHomePage() const = 0;\n\n    /// Set to enable implicit pages, enabled by default\n    /**\n     * @brief Implicit pages are used when the server detects if the user\n     * requested a directory. By default, it will try to append index.html to\n     * the path, see setImplicitPage() if you want to customize this\n     * (http://localhost/a-directory resolves to\n     * http://localhost/a-directory/index.html by default).\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setImplicitPageEnable(bool useImplicitPage) = 0;\n\n    /// Return true if implicit pages are enabled\n    /**\n     * @note\n     * This method must be called after the framework has been run.\n     */\n    virtual bool isImplicitPageEnabled() const = 0;\n\n    /// Set the HTML file that a directory would resolve to by default, default\n    /// is \"index.html\"\n    /**\n     * @brief Set the page which would the server load in if it detects that\n     * the user requested a directory\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setImplicitPage(\n        const std::string &implicitPageFile) = 0;\n\n    /// Get the implicit HTML page\n    /**\n     * @note\n     * This method must be called after the framework has been run.\n     */\n    virtual const std::string &getImplicitPage() const = 0;\n\n    /// Get a database client by name\n    /**\n     * @note\n     * This method must be called after the framework has been run.\n     */\n    virtual orm::DbClientPtr getDbClient(\n        const std::string &name = \"default\") = 0;\n\n    /// Get a 'fast' database client by name\n    /**\n     * @note\n     * This method must be called after the framework has been run.\n     */\n    virtual orm::DbClientPtr getFastDbClient(\n        const std::string &name = \"default\") = 0;\n\n    /**\n     * @brief Check if all database clients in the framework are available\n     * (connect to the database successfully).\n     */\n    virtual bool areAllDbClientsAvailable() const noexcept = 0;\n\n    /// Get a redis client by name\n    /**\n     * @note\n     * This method must be called after the framework has been run.\n     */\n    virtual nosql::RedisClientPtr getRedisClient(\n        const std::string &name = \"default\") = 0;\n\n    /// Get a 'fast' redis client by name\n    /**\n     * @note\n     * This method must be called after the framework has been run.\n     */\n    virtual nosql::RedisClientPtr getFastRedisClient(\n        const std::string &name = \"default\") = 0;\n\n    /**\n     * @brief Set the maximum stack depth of the json parser when reading a json\n     * string, the default value is 1000.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &setJsonParserStackLimit(\n        size_t limit) noexcept = 0;\n\n    /**\n     * @brief Get the maximum stack depth of the json parser when reading a json\n     * string.\n     */\n    virtual size_t getJsonParserStackLimit() const noexcept = 0;\n    /**\n     * @brief This method is to enable or disable the unicode escaping (\\\\u) in\n     * the json string of HTTP responses or requests. it works (disable\n     * successfully) when the version of JsonCpp >= 1.9.3, the unicode escaping\n     * is enabled by default.\n     */\n    virtual HttpAppFramework &setUnicodeEscapingInJson(\n        bool enable) noexcept = 0;\n\n    /**\n     * @brief Check if the unicode escaping is used in the json string of HTTP\n     * requests and responses.\n     */\n    virtual bool isUnicodeEscapingUsedInJson() const noexcept = 0;\n\n    /**\n     * @brief Set the float precision in Json string of HTTP requests or\n     * responses with json content.\n     *\n     * @param precision The maximum digits length.\n     * @param precisionType Must be \"significant\" or \"decimal\", defaults to\n     * \"significant\" that means setting max number of significant digits in\n     * string, \"decimal\" means setting max number of digits after \".\" in string\n     * @return HttpAppFramework&\n     */\n    virtual HttpAppFramework &setFloatPrecisionInJson(\n        unsigned int precision,\n        const std::string &precisionType = \"significant\") noexcept = 0;\n    /**\n     * @brief Get the float precision set by the above method.\n     *\n     * @return std::pair<size_t, std::string>\n     */\n    virtual const std::pair<unsigned int, std::string> &\n    getFloatPrecisionInJson() const noexcept = 0;\n    /// Create a database client\n    /**\n     * @param dbType The database type is one of\n     * \"postgresql\",\"mysql\",\"sqlite3\".\n     * @param host IP or host name.\n     * @param port The port on which the database server is listening.\n     * @param databaseName Database name\n     * @param userName User name\n     * @param password Password for the database server\n     * @param connectionNum The number of connections to the database server.\n     * It's valid only if @p isFast is false.\n     * @param filename The file name of sqlite3 database file.\n     * @param name The client name.\n     * @param isFast Indicates if the client is a fast database client.\n     * @param characterSet The character set of the database server.\n     * @param timeout The timeout in seconds for executing SQL queries. zero or\n     * negative value means no timeout.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    [[deprecated(\"Use addDbClient() instead\")]] virtual HttpAppFramework &\n    createDbClient(const std::string &dbType,\n                   const std::string &host,\n                   unsigned short port,\n                   const std::string &databaseName,\n                   const std::string &userName,\n                   const std::string &password,\n                   size_t connectionNum = 1,\n                   const std::string &filename = \"\",\n                   const std::string &name = \"default\",\n                   bool isFast = false,\n                   const std::string &characterSet = \"\",\n                   double timeout = -1.0,\n                   bool autoBatch = false) = 0;\n\n    virtual HttpAppFramework &addDbClient(const orm::DbConfig &config) = 0;\n\n    /// Create a redis client\n    /**\n     * @param ip IP of redis server.\n     * @param port The port on which the redis server is listening.\n     * @param name The client name.\n     * @param username Username for redis server\n     * @param password Password for the redis server\n     * @param connectionNum The number of connections to the redis server.\n     * @param isFast Indicates if the client is a fast database client.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual HttpAppFramework &createRedisClient(\n        const std::string &ip,\n        unsigned short port,\n        const std::string &name = \"default\",\n        const std::string &password = \"\",\n        size_t connectionNum = 1,\n        bool isFast = false,\n        double timeout = -1.0,\n        unsigned int db = 0,\n        const std::string &username = \"\") = 0;\n\n    /// Get the DNS resolver\n    /**\n     * @note\n     * When the c-ares library is installed in the system, it runs with the best\n     * performance.\n     */\n    virtual const std::shared_ptr<trantor::Resolver> &getResolver() const = 0;\n\n    /// Return true is drogon supports SSL(https)\n    virtual bool supportSSL() const = 0;\n\n    /**\n     * @brief Get the Current Thread Index whose range is [0, the total number\n     * of IO threads]\n     *\n     * @return size_t If the current thread is the main EventLoop thread (in\n     * which the app().run() is called), the number of the IO threads is\n     * returned. If the current thread is a network IO thread, the index of it\n     * in the range [0, the number of IO threads) is returned. otherwise the\n     * maximum value of type size_t is returned.\n     *\n     * @note Basically this method is used for storing thread-related various in\n     * an array and users can use indexes returned by this method to access\n     * them. This is much faster than using a map. If the array is properly\n     * initialized at the beginning, users can access it without locks.\n     */\n    virtual size_t getCurrentThreadIndex() const = 0;\n\n    /**\n     * @brief Get the addresses of listeners.\n     *\n     * @return std::vector<trantor::InetAddress>\n     * @note This method should be called after calling the app().run(). One\n     * could run this method in an AOP join point (such as the BeginningAdvice).\n     */\n    virtual std::vector<trantor::InetAddress> getListeners() const = 0;\n\n    /**\n     * @brief Enable ReusePort mode or not. If the mode is enabled, one can run\n     * multiple processes listening to the same port at the same time. If this\n     * method is not called, the feature is disabled.\n     *\n     * @note\n     * This operation can be performed by an option in the configuration file.\n     */\n    virtual void enableReusePort(bool enable = true) = 0;\n\n    /**\n     * @brief Return if the ReusePort mode is enabled.\n     */\n    virtual bool reusePort() const = 0;\n\n    /**\n     * @brief handler will be called upon an exception escapes a request handler\n     */\n    virtual HttpAppFramework &setExceptionHandler(ExceptionHandler handler) = 0;\n\n    /**\n     * @brief returns the excaption handler\n     */\n    virtual const ExceptionHandler &getExceptionHandler() const = 0;\n\n    /**\n     * @brief Adds a new custom extension to MIME type mapping\n     */\n    virtual HttpAppFramework &registerCustomExtensionMime(\n        const std::string &ext,\n        const std::string &mime) = 0;\n\n    virtual HttpAppFramework &enableCompressedRequest(bool enable = true) = 0;\n    virtual bool isCompressedRequestEnabled() const = 0;\n    /*\n     * @brief get the number of active connections.\n     */\n    virtual int64_t getConnectionCount() const = 0;\n\n    /**\n     * @brief Set the before listen setsockopt callback.\n     *\n     * @param cb This callback will be called before the listen\n     */\n    virtual HttpAppFramework &setBeforeListenSockOptCallback(\n        std::function<void(int)> cb) = 0;\n\n    /**\n     * @brief Set the after accept setsockopt callback.\n     *\n     * @param cb This callback will be called after accept\n     */\n    virtual HttpAppFramework &setAfterAcceptSockOptCallback(\n        std::function<void(int)> cb) = 0;\n\n    /**\n     * @brief Set the client disconnect or connect callback.\n     *\n     * @param cb This callback will be called, when the client disconnect or\n     * connect\n     */\n    virtual HttpAppFramework &setConnectionCallback(\n        std::function<void(const trantor::TcpConnectionPtr &)> cb) = 0;\n\n    virtual HttpAppFramework &enableRequestStream(bool enable = true) = 0;\n    virtual bool isRequestStreamEnabled() const = 0;\n\n  private:\n    virtual void registerHttpController(\n        const std::string &pathPattern,\n        const internal::HttpBinderBasePtr &binder,\n        const std::vector<HttpMethod> &validMethods = {},\n        const std::vector<std::string> &middlewareNames = {},\n        const std::string &handlerName = \"\") = 0;\n    virtual void registerHttpControllerViaRegex(\n        const std::string &regExp,\n        const internal::HttpBinderBasePtr &binder,\n        const std::vector<HttpMethod> &validMethods,\n        const std::vector<std::string> &middlewareNames,\n        const std::string &handlerName) = 0;\n};\n\n/// A wrapper of the instance() method\ninline HttpAppFramework &app()\n{\n    return HttpAppFramework::instance();\n}\n#ifdef __cpp_impl_coroutine\nnamespace internal\n{\ninline void ForwardAwaiter::await_suspend(\n    std::coroutine_handle<> handle) noexcept\n{\n    app_.forward(\n        req_,\n        [this, handle](const drogon::HttpResponsePtr &resp) {\n            setValue(resp);\n            handle.resume();\n        },\n        host_,\n        timeout_);\n}\n}  // namespace internal\n#endif\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/HttpBinder.h",
    "content": "/**\n *\n *  @file HttpBinder.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n/// The classes in the file are internal tool classes. Do not include this\n/// file directly and use any of these classes directly.\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <drogon/DrClassMap.h>\n#include <drogon/DrObject.h>\n#include <drogon/utils/FunctionTraits.h>\n#include <drogon/utils/Utilities.h>\n#include <drogon/HttpRequest.h>\n#include <deque>\n#include <memory>\n#include <sstream>\n#include <string>\n\nnamespace drogon\n{\nnamespace internal\n{\n// we only accept value type or const lreference type or right reference type as\n// the handle method parameters type\ntemplate <typename T>\nstruct BinderArgTypeTraits\n{\n    static const bool isValid = true;\n};\n\ntemplate <typename T>\nstruct BinderArgTypeTraits<T *>\n{\n    static const bool isValid = false;\n};\n\ntemplate <typename T>\nstruct BinderArgTypeTraits<T &>\n{\n    static const bool isValid = false;\n};\n\ntemplate <typename T>\nstruct BinderArgTypeTraits<T &&>\n{\n    static const bool isValid = true;\n};\n\ntemplate <typename T>\nstruct BinderArgTypeTraits<const T &&>\n{\n    static const bool isValid = false;\n};\n\ntemplate <typename T>\nstruct BinderArgTypeTraits<const T &>\n{\n    static const bool isValid = true;\n};\n\ntemplate <typename T>\nT getHandlerArgumentValue(std::string &&p)\n{\n    if constexpr (internal::CanConstructFromString<T>::value)\n    {\n        return T(std::move(p));\n    }\n    else if constexpr (internal::CanConvertFromStringStream<T>::value)\n    {\n        T value{T()};\n        if (!p.empty())\n        {\n            std::stringstream ss(std::move(p));\n            ss >> value;\n        }\n        return value;\n    }\n    else if constexpr (internal::CanConvertFromString<T>::value)\n    {\n        T value;\n        value = p;\n        return value;\n    }\n    else\n    {\n        LOG_ERROR << \"Can't convert string to type \" << typeid(T).name();\n        return T();\n    }\n}\n\ntemplate <>\ninline std::string getHandlerArgumentValue<std::string>(std::string &&p)\n{\n    return std::move(p);\n}\n\ntemplate <>\ninline int getHandlerArgumentValue<int>(std::string &&p)\n{\n    return std::stoi(p);\n}\n\ntemplate <>\ninline long getHandlerArgumentValue<long>(std::string &&p)\n{\n    return std::stol(p);\n}\n\ntemplate <>\ninline long long getHandlerArgumentValue<long long>(std::string &&p)\n{\n    return std::stoll(p);\n}\n\ntemplate <>\ninline unsigned long getHandlerArgumentValue<unsigned long>(std::string &&p)\n{\n    return std::stoul(p);\n}\n\ntemplate <>\ninline unsigned long long getHandlerArgumentValue<unsigned long long>(\n    std::string &&p)\n{\n    return std::stoull(p);\n}\n\ntemplate <>\ninline float getHandlerArgumentValue<float>(std::string &&p)\n{\n    return std::stof(p);\n}\n\ntemplate <>\ninline double getHandlerArgumentValue<double>(std::string &&p)\n{\n    return std::stod(p);\n}\n\ntemplate <>\ninline long double getHandlerArgumentValue<long double>(std::string &&p)\n{\n    return std::stold(p);\n}\n\nclass HttpBinderBase\n{\n  public:\n    virtual void handleHttpRequest(\n        std::deque<std::string> &pathArguments,\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) = 0;\n    virtual size_t paramCount() = 0;\n    virtual const std::string &handlerName() const = 0;\n    virtual bool isStreamHandler() = 0;\n\n    virtual ~HttpBinderBase()\n    {\n    }\n};\n\ntemplate <typename T>\nT &getControllerObj()\n{\n    // Initialization of function-local statics is guaranteed to occur only once\n    // even when\n    // called from multiple threads, and may be more efficient than the\n    // equivalent code using std::call_once.\n    static T obj;\n    return obj;\n}\n\nDROGON_EXPORT void handleException(\n    const std::exception &,\n    const HttpRequestPtr &,\n    std::function<void(const HttpResponsePtr &)> &&);\n\nusing HttpBinderBasePtr = std::shared_ptr<HttpBinderBase>;\n\ntemplate <typename FUNCTION>\nclass HttpBinder : public HttpBinderBase\n{\n  public:\n    using traits = FunctionTraits<FUNCTION>;\n    using FunctionType = FUNCTION;\n\n    void handleHttpRequest(\n        std::deque<std::string> &pathArguments,\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) override\n    {\n        if (!pathArguments.empty())\n        {\n            std::vector<std::string> args;\n            args.reserve(pathArguments.size());\n            for (auto &arg : pathArguments)\n            {\n                args.emplace_back(arg);\n            }\n            req->setRoutingParameters(std::move(args));\n        }\n        run(pathArguments, req, std::move(callback));\n    }\n\n    size_t paramCount() override\n    {\n        return traits::arity;\n    }\n\n    bool isStreamHandler() override\n    {\n        return traits::isStreamHandler;\n    }\n\n    HttpBinder(FUNCTION &&func) : func_(std::forward<FUNCTION>(func))\n    {\n        static_assert(traits::isHTTPFunction,\n                      \"Your API handler function interface is wrong!\");\n        handlerName_ = DrClassMap::demangle(typeid(FUNCTION).name());\n    }\n\n    void test()\n    {\n        std::cout << \"argument_count=\" << argument_count << \" \"\n                  << traits::isHTTPFunction << std::endl;\n    }\n\n    const std::string &handlerName() const override\n    {\n        return handlerName_;\n    }\n\n    template <bool isClassFunction = traits::isClassFunction,\n              bool isDrObjectClass = traits::isDrObjectClass>\n    void createHandlerInstance()\n    {\n        if constexpr (isClassFunction)\n        {\n            if constexpr (isDrObjectClass)\n            {\n                auto objPtr = DrClassMap::getSingleInstance<\n                    typename traits::class_type>();\n                LOG_TRACE << \"create handler class object: \" << objPtr.get();\n            }\n            else\n            {\n                auto &obj = getControllerObj<typename traits::class_type>();\n                LOG_TRACE << \"create handler class object: \" << &obj;\n            }\n        }\n    }\n\n  private:\n    FUNCTION func_;\n    template <std::size_t Index>\n    using nth_argument_type = typename traits::template argument<Index>;\n\n    static const size_t argument_count = traits::arity;\n    std::string handlerName_;\n\n    template <typename... Values,\n              std::size_t Boundary = argument_count,\n              bool isStreamHandler = traits::isStreamHandler,\n              bool isCoroutine = traits::isCoroutine>\n    void run(std::deque<std::string> &pathArguments,\n             const HttpRequestPtr &req,\n             std::function<void(const HttpResponsePtr &)> &&callback,\n             Values &&...values)\n    {\n        if constexpr (sizeof...(Values) < Boundary)\n        {  // Call this function recursively until parameter's count equals to\n           // the count of target function parameters\n            static_assert(\n                BinderArgTypeTraits<\n                    nth_argument_type<sizeof...(Values)>>::isValid,\n                \"your handler argument type must be value type or const left \"\n                \"reference type or right reference type\");\n            using ValueType = std::remove_cv_t<\n                std::remove_reference_t<nth_argument_type<sizeof...(Values)>>>;\n            if (!pathArguments.empty())\n            {\n                std::string v{std::move(pathArguments.front())};\n                pathArguments.pop_front();\n                try\n                {\n                    if (!v.empty())\n                    {\n                        auto value =\n                            getHandlerArgumentValue<ValueType>(std::move(v));\n                        run(pathArguments,\n                            req,\n                            std::move(callback),\n                            std::forward<Values>(values)...,\n                            std::move(value));\n                        return;\n                    }\n                }\n                catch (const std::exception &e)\n                {\n                    handleException(e, req, std::move(callback));\n                    return;\n                }\n            }\n            else\n            {\n                try\n                {\n                    auto value = req->as<ValueType>();\n                    run(pathArguments,\n                        req,\n                        std::move(callback),\n                        std::forward<Values>(values)...,\n                        std::move(value));\n                    return;\n                }\n                catch (const std::exception &e)\n                {\n                    handleException(e, req, std::move(callback));\n                    return;\n                }\n                catch (...)\n                {\n                    LOG_ERROR << \"Exception not derived from std::exception\";\n                    return;\n                }\n            }\n\n            run(pathArguments,\n                req,\n                std::move(callback),\n                std::forward<Values>(values)...,\n                ValueType());\n        }\n        else if constexpr (sizeof...(Values) == Boundary)\n        {\n            if constexpr (!isCoroutine)\n            {\n                try\n                {\n                    // Explicit copy because `callFunction` moves it\n                    auto cb = callback;\n                    if constexpr (isStreamHandler)\n                    {\n                        callFunction(req,\n                                     createRequestStream(req),\n                                     cb,\n                                     std::move(values)...);\n                    }\n                    else\n                    {\n                        callFunction(req, cb, std::move(values)...);\n                    }\n                }\n                catch (const std::exception &except)\n                {\n                    handleException(except, req, std::move(callback));\n                }\n                catch (...)\n                {\n                    LOG_ERROR << \"Exception not derived from std::exception\";\n                    return;\n                }\n            }\n#ifdef __cpp_impl_coroutine\n            else\n            {\n                static_assert(!isStreamHandler);\n                [this](HttpRequestPtr req,\n                       std::function<void(const HttpResponsePtr &)> callback,\n                       Values &&...values) -> AsyncTask {\n                    try\n                    {\n                        if constexpr (std::is_same_v<\n                                          AsyncTask,\n                                          typename traits::return_type>)\n                        {\n                            // Explicit copy because `callFunction` moves it\n                            auto cb = callback;\n                            callFunction(req, cb, std::move(values)...);\n                        }\n                        else if constexpr (std::is_same_v<\n                                               Task<>,\n                                               typename traits::return_type>)\n                        {\n                            // Explicit copy because `callFunction` moves it\n                            auto cb = callback;\n                            co_await callFunction(req,\n                                                  cb,\n                                                  std::move(values)...);\n                        }\n                        else if constexpr (std::is_same_v<\n                                               Task<HttpResponsePtr>,\n                                               typename traits::return_type>)\n                        {\n                            auto resp =\n                                co_await callFunction(req,\n                                                      std::move(values)...);\n                            callback(std::move(resp));\n                        }\n                    }\n                    catch (const std::exception &except)\n                    {\n                        handleException(except, req, std::move(callback));\n                    }\n                    catch (...)\n                    {\n                        LOG_ERROR\n                            << \"Exception not derived from std::exception\";\n                    }\n                    co_return;\n                }(req, std::move(callback), std::move(values)...);\n            }\n#endif\n        }\n    }\n\n    template <typename... Values,\n              bool isClassFunction = traits::isClassFunction,\n              bool isDrObjectClass = traits::isDrObjectClass,\n              bool isNormal = std::is_same_v<typename traits::first_param_type,\n                                             HttpRequestPtr>>\n    typename traits::return_type callFunction(const HttpRequestPtr &req,\n                                              Values &&...values)\n    {\n        if constexpr (isNormal)\n        {\n            if constexpr (isClassFunction)\n            {\n                if constexpr (!isDrObjectClass)\n                {\n                    static auto &obj =\n                        getControllerObj<typename traits::class_type>();\n                    return (obj.*func_)(req, std::move(values)...);\n                }\n                else\n                {\n                    static auto objPtr = DrClassMap::getSingleInstance<\n                        typename traits::class_type>();\n                    return (*objPtr.*func_)(req, std::move(values)...);\n                }\n            }\n            else\n            {\n                return func_(req, std::move(values)...);\n            }\n        }\n        else\n        {\n            if constexpr (isClassFunction)\n            {\n                if constexpr (!isDrObjectClass)\n                {\n                    static auto &obj =\n                        getControllerObj<typename traits::class_type>();\n                    return (obj.*func_)((*req), std::move(values)...);\n                }\n                else\n                {\n                    static auto objPtr = DrClassMap::getSingleInstance<\n                        typename traits::class_type>();\n                    return (*objPtr.*func_)((*req), std::move(values)...);\n                }\n            }\n            else\n            {\n                return func_((*req), std::move(values)...);\n            }\n        }\n    }\n};\n\n}  // namespace internal\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/HttpClient.h",
    "content": "/**\n *\n *  @file HttpClient.h\n *\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by the MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include <drogon/exports.h>\n#include <drogon/HttpTypes.h>\n#include <drogon/drogon_callbacks.h>\n#include <drogon/HttpResponse.h>\n#include <drogon/HttpRequest.h>\n#include <trantor/utils/NonCopyable.h>\n#include <trantor/net/EventLoop.h>\n#include <cstddef>\n#include <functional>\n#include <memory>\n#include <future>\n#include \"drogon/HttpBinder.h\"\n\n#ifdef __cpp_impl_coroutine\n#include <drogon/utils/coroutine.h>\n#endif\n\nnamespace drogon\n{\nclass HttpClient;\nusing HttpClientPtr = std::shared_ptr<HttpClient>;\n#ifdef __cpp_impl_coroutine\nnamespace internal\n{\nstruct HttpRespAwaiter : public CallbackAwaiter<HttpResponsePtr>\n{\n    HttpRespAwaiter(HttpClient *client, HttpRequestPtr req, double timeout)\n        : client_(client), req_(std::move(req)), timeout_(timeout)\n    {\n    }\n\n    void await_suspend(std::coroutine_handle<> handle);\n\n  private:\n    HttpClient *client_;\n    HttpRequestPtr req_;\n    double timeout_;\n};\n\n}  // namespace internal\n#endif\n\n/// Asynchronous http client\n/**\n * HttpClient implementation object uses the HttpAppFramework's event loop by\n * default, so you should call app().run() to make the client work.\n * Each HttpClient object establishes a persistent connection with the server.\n * If the connection is broken, the client attempts to reconnect\n * when calling the sendRequest method.\n *\n * Using the static method newHttpClient(...) to get shared_ptr of the object\n * implementing the class, the shared_ptr is retained in the framework until all\n * response callbacks are invoked without fear of accidental deconstruction.\n *\n */\nclass DROGON_EXPORT HttpClient : public trantor::NonCopyable\n{\n  public:\n    /**\n     * @brief Send a request asynchronously to the server\n     *\n     * @param req The request sent to the server.\n     * @param callback The callback is called when the response is received from\n     * the server.\n     * @param timeout In seconds. If the response is not received within the\n     * timeout, the callback is called with `ReqResult::Timeout` and an empty\n     * response. The zero value by default disables the timeout.\n     *\n     * @note\n     * The request object is altered(some headers are added to it) before it is\n     * sent, so calling this method with a same request object in different\n     * thread is dangerous.\n     * Please be careful when using timeout on an non-idempotent request.\n     */\n    virtual void sendRequest(const HttpRequestPtr &req,\n                             const HttpReqCallback &callback,\n                             double timeout = 0) = 0;\n\n    /**\n     * @brief Send a request asynchronously to the server\n     *\n     * @param req The request sent to the server.\n     * @param callback The callback is called when the response is received from\n     * the server.\n     * @param timeout In seconds. If the response is not received within\n     * the timeout, the callback is called with `ReqResult::Timeout` and an\n     * empty response. The zero value by default disables the timeout.\n     *\n     * @note\n     * The request object is altered(some headers are added to it) before it is\n     * sent, so calling this method with a same request object in different\n     * thread is dangerous.\n     * Please be careful when using timeout on an non-idempotent request.\n     */\n    virtual void sendRequest(const HttpRequestPtr &req,\n                             HttpReqCallback &&callback,\n                             double timeout = 0) = 0;\n\n    /**\n     * @brief Send a request synchronously to the server and return the\n     * response.\n     *\n     * @param req\n     * @param timeout In seconds. If the response is not received within the\n     * timeout, the `ReqResult::Timeout` and an empty response is returned. The\n     * zero value by default disables the timeout.\n     *\n     * @return std::pair<ReqResult, HttpResponsePtr>\n     * @note Never call this function in the event loop thread of the\n     * client (partially in the callback function of the asynchronous\n     * sendRequest method), otherwise the thread will be blocked forever.\n     * Please be careful when using timeout on an non-idempotent request.\n     */\n    std::pair<ReqResult, HttpResponsePtr> sendRequest(const HttpRequestPtr &req,\n                                                      double timeout = 0)\n    {\n        assert(!getLoop()->isInLoopThread() &&\n               \"Deadlock detected! Calling a sync API from the same loop as \"\n               \"the HTTP client processes on will deadlock the event loop\");\n        std::promise<std::pair<ReqResult, HttpResponsePtr>> prom;\n        auto f = prom.get_future();\n        sendRequest(\n            req,\n            [&prom](ReqResult r, const HttpResponsePtr &resp) {\n                prom.set_value({r, resp});\n            },\n            timeout);\n        return f.get();\n    }\n\n#ifdef __cpp_impl_coroutine\n    /**\n     * @brief Send a request via coroutines to the server and return an\n     * awaiter what could be `co_await`-ed to retrieve the response\n     * (HttpResponsePtr)\n     *\n     * @param req\n     * @param timeout In seconds. If the response is not received within the\n     * timeout, A `drogon::HttpException` with `ReqResult::Timeout` is thrown.\n     * The zero value by default disables the timeout.\n     *\n     * @return internal::HttpRespAwaiter. Await on it to get the response\n     */\n    internal::HttpRespAwaiter sendRequestCoro(HttpRequestPtr req,\n                                              double timeout = 0)\n    {\n        return internal::HttpRespAwaiter(this, std::move(req), timeout);\n    }\n#endif\n\n    /// Set socket options(before connecting)\n    /**\n     * @brief Set the callback which is called before connecting to the\n     * server. The callback is used to set socket options on the socket fd.\n     *\n     * @code\n       auto client = HttpClient::newHttpClient(\"http://www.baidu.com\");\n       client->setSockOptCallback([](int fd) {});\n       auto req = HttpRequest::newHttpRequest();\n       client->sendRequest(req, [](ReqResult result, const HttpResponsePtr&\n       response) {});\n       @endcode\n     */\n    virtual void setSockOptCallback(std::function<void(int)> cb) = 0;\n\n    /**\n     * @brief Return the number of unsent http requests in the current http\n     * client cache buffer\n     */\n    virtual std::size_t requestsBufferSize() = 0;\n\n    /// Set the pipelining depth, which is the number of requests that are not\n    /// responding.\n    /**\n     * If this method is not called, the default depth value is 0 which means\n     * the pipelining is disabled. For details about pipelining, see\n     * rfc2616-8.1.2.2\n     */\n    virtual void setPipeliningDepth(size_t depth) = 0;\n\n    /// Enable cookies for the client\n    /**\n     * @param flag if the parameter is true, all requests sent by the client\n     * carry the cookies set by the server side. Cookies are disabled by\n     * default.\n     */\n    virtual void enableCookies(bool flag = true) = 0;\n\n    /// Add a cookie to the client\n    /**\n     * @note\n     * These methods are independent of the enableCookies() method. Whether the\n     * enableCookies() is called with true or false, the cookies added by these\n     * methods will be sent to the server.\n     */\n    virtual void addCookie(const std::string &key,\n                           const std::string &value) = 0;\n\n    /// Add a cookie to the client\n    /**\n     * @note\n     * These methods are independent of the enableCookies() method. Whether the\n     * enableCookies() is called with true or false, the cookies added by these\n     * methods will be sent to the server.\n     */\n    virtual void addCookie(const Cookie &cookie) = 0;\n\n    /**\n     * @brief Set the user_agent header, the default value is 'DrogonClient' if\n     * this method is not used.\n     *\n     * @param userAgent The user_agent value, if it is empty, the user_agent\n     * header is not sent to the server.\n     */\n    virtual void setUserAgent(const std::string &userAgent) = 0;\n\n    /**\n     * @brief Create a new HTTP client which use ip and port to connect to\n     * server\n     *\n     * @param ip The ip address of the HTTP server\n     * @param port The port of the HTTP server\n     * @param useSSL if the parameter is set to true, the client connects to the\n     * server using HTTPS.\n     * @param loop If the loop parameter is set to nullptr, the client uses the\n     * HttpAppFramework's event loop, otherwise it runs in the loop identified\n     * by the parameter.\n     * @param useOldTLS If the parameter is set to true, the TLS1.0/1.1 are\n     * enabled for HTTPS.\n     * @param validateCert If the parameter is set to true, the client validates\n     * the server certificate when SSL handshaking.\n     * @return HttpClientPtr The smart pointer to the new client object.\n     * @note: The ip parameter support for both ipv4 and ipv6 address\n     */\n    static HttpClientPtr newHttpClient(const std::string &ip,\n                                       uint16_t port,\n                                       bool useSSL = false,\n                                       trantor::EventLoop *loop = nullptr,\n                                       bool useOldTLS = false,\n                                       bool validateCert = true);\n\n    /// Get the event loop of the client;\n    virtual trantor::EventLoop *getLoop() = 0;\n\n    /// Get the number of bytes sent or received\n    virtual size_t bytesSent() const = 0;\n    virtual size_t bytesReceived() const = 0;\n\n    virtual std::string host() const = 0;\n\n    std::string getHost() const\n    {\n        return host();\n    }\n\n    virtual uint16_t port() const = 0;\n\n    uint16_t getPort() const\n    {\n        return port();\n    }\n\n    virtual bool secure() const = 0;\n\n    bool onDefaultPort() const\n    {\n        if (secure())\n            return port() == 443;\n        return port() == 80;\n    }\n\n    /**\n     * @brief Set the client certificate used by the HTTP connection\n     *\n     * @param cert Path to the certificate\n     * @param key Path to the certificate's private key\n     * @note this method has no effect if the HTTP client is communicating via\n     * unencrypted HTTP\n     */\n    virtual void setCertPath(const std::string &cert,\n                             const std::string &key) = 0;\n\n    /**\n     * @brief Supplies command style options for `SSL_CONF_cmd`\n     *\n     * @param sslConfCmds options for SSL_CONF_cmd\n     * @note this method has no effect if the HTTP client is communicating via\n     * unencrypted HTTP\n     * @code\n       addSSLConfigs({{\"-dhparam\", \"/path/to/dhparam\"}, {\"-strict\", \"\"}});\n     * @endcode\n     */\n    virtual void addSSLConfigs(\n        const std::vector<std::pair<std::string, std::string>>\n            &sslConfCmds) = 0;\n\n    /// Create a Http client using the hostString to connect to server\n    /**\n     *\n     * @param hostString this parameter must be prefixed by 'http://' or\n     * 'https://'.\n     *\n     * Examples for hostString:\n     * @code\n       https://www.baidu.com\n       http://www.baidu.com\n       https://127.0.0.1:8080/\n       http://127.0.0.1\n       http://[::1]:8080/   //IPv6 address must be enclosed in [], rfc2732\n       @endcode\n     *\n     * @param loop If the loop parameter is set to nullptr, the client uses the\n     * HttpAppFramework's event loop, otherwise it runs in the loop identified\n     * by the parameter.\n     *\n     * @param useOldTLS If the parameter is set to true, the TLS1.0/1.1 are\n     * enabled for HTTPS.\n     * @note\n     *\n     * @param validateCert If the parameter is set to true, the client validates\n     * the server certificate when SSL handshaking.\n     *\n     * @note Don't add path and parameters in hostString, the request path and\n     * parameters should be set in HttpRequestPtr when calling the sendRequest()\n     * method.\n     *\n     */\n    static HttpClientPtr newHttpClient(const std::string &hostString,\n                                       trantor::EventLoop *loop = nullptr,\n                                       bool useOldTLS = false,\n                                       bool validateCert = true);\n\n    virtual ~HttpClient()\n    {\n    }\n\n  protected:\n    HttpClient() = default;\n};\n\n#ifdef __cpp_impl_coroutine\n\nclass HttpException : public std::exception\n{\n  public:\n    HttpException() = delete;\n\n    explicit HttpException(ReqResult res)\n        : resultCode_(res), message_(to_string_view(res))\n    {\n    }\n\n    const char *what() const noexcept override\n    {\n        return message_.data();\n    }\n\n    ReqResult code() const\n    {\n        return resultCode_;\n    }\n\n  private:\n    ReqResult resultCode_;\n    std::string_view message_;\n};\n\ninline void internal::HttpRespAwaiter::await_suspend(\n    std::coroutine_handle<> handle)\n{\n    assert(client_ != nullptr);\n    assert(req_ != nullptr);\n    client_->sendRequest(\n        req_,\n        [handle, this](ReqResult result, const HttpResponsePtr &resp) {\n            if (result == ReqResult::Ok)\n                setValue(resp);\n            else\n                setException(std::make_exception_ptr(HttpException(result)));\n            handle.resume();\n        },\n        timeout_);\n}\n#endif\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/HttpController.h",
    "content": "/**\n *\n *  HttpController.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include <drogon/utils/HttpConstraint.h>\n#include <drogon/HttpAppFramework.h>\n#include <iostream>\n#include <string>\n#include <trantor/utils/Logger.h>\n#include <vector>\n\n/// For more details on the class, see the wiki site (the 'HttpController'\n/// section)\n\n#define METHOD_LIST_BEGIN         \\\n    static void initPathRouting() \\\n    {\n#define METHOD_ADD(method, pattern, ...) \\\n    registerMethod(&method, pattern, {__VA_ARGS__}, true, #method)\n#define ADD_METHOD_TO(method, path_pattern, ...) \\\n    registerMethod(&method, path_pattern, {__VA_ARGS__}, false, #method)\n#define ADD_METHOD_VIA_REGEX(method, regex, ...) \\\n    registerMethodViaRegex(&method, regex, {__VA_ARGS__}, #method)\n#define METHOD_LIST_END \\\n    return;             \\\n    }\n\nnamespace drogon\n{\n/**\n * @brief The base class for HTTP controllers.\n *\n */\nclass HttpControllerBase\n{\n};\n\n/**\n * @brief The reflection base class template for HTTP controllers\n *\n * @tparam T the type of the implementation class\n * @tparam AutoCreation The flag for automatically creating, user can set this\n * flag to false for classes that have nondefault constructors.\n */\ntemplate <typename T, bool AutoCreation = true>\nclass HttpController : public DrObject<T>, public HttpControllerBase\n{\n  public:\n    static constexpr bool isAutoCreation = AutoCreation;\n\n  protected:\n    template <typename FUNCTION>\n    static void registerMethod(\n        FUNCTION &&function,\n        const std::string &pattern,\n        const std::vector<internal::HttpConstraint> &constraints = {},\n        bool classNameInPath = true,\n        const std::string &handlerName = \"\")\n    {\n        if (classNameInPath)\n        {\n            std::string path = \"/\";\n            path.append(HttpController<T, AutoCreation>::classTypeName());\n            LOG_TRACE << \"classname:\"\n                      << HttpController<T, AutoCreation>::classTypeName();\n\n            // transform(path.begin(), path.end(), path.begin(), [](unsigned\n            // char c){ return tolower(c); });\n            std::string::size_type pos;\n            while ((pos = path.find(\"::\")) != std::string::npos)\n            {\n                path.replace(pos, 2, \"/\");\n            }\n            if (pattern.empty() || pattern[0] == '/')\n                app().registerHandler(path + pattern,\n                                      std::forward<FUNCTION>(function),\n                                      constraints,\n                                      handlerName);\n            else\n                app().registerHandler(path + \"/\" + pattern,\n                                      std::forward<FUNCTION>(function),\n                                      constraints,\n                                      handlerName);\n        }\n        else\n        {\n            std::string path = pattern;\n            if (path.empty() || path[0] != '/')\n            {\n                path = \"/\" + path;\n            }\n            app().registerHandler(path,\n                                  std::forward<FUNCTION>(function),\n                                  constraints,\n                                  handlerName);\n        }\n    }\n\n    template <typename FUNCTION>\n    static void registerMethodViaRegex(\n        FUNCTION &&function,\n        const std::string &regExp,\n        const std::vector<internal::HttpConstraint> &constraints =\n            std::vector<internal::HttpConstraint>{},\n        const std::string &handlerName = \"\")\n    {\n        app().registerHandlerViaRegex(regExp,\n                                      std::forward<FUNCTION>(function),\n                                      constraints,\n                                      handlerName);\n    }\n\n  private:\n    class methodRegistrator\n    {\n      public:\n        methodRegistrator()\n        {\n            if (AutoCreation)\n                T::initPathRouting();\n        }\n    };\n\n    // use static value to register controller method in framework before\n    // main();\n    static methodRegistrator registrator_;\n\n    virtual void *touch()\n    {\n        return &registrator_;\n    }\n};\n\ntemplate <typename T, bool AutoCreation>\ntypename HttpController<T, AutoCreation>::methodRegistrator\n    HttpController<T, AutoCreation>::registrator_;\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/HttpFilter.h",
    "content": "/**\n *\n *  @file HttpFilter.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include <drogon/drogon_callbacks.h>\n#include <drogon/HttpRequest.h>\n#include <drogon/HttpResponse.h>\n#include <drogon/HttpMiddleware.h>\n#include <memory>\n\n#ifdef __cpp_impl_coroutine\n#include <drogon/utils/coroutine.h>\n#endif\n\nnamespace drogon\n{\n/**\n * @brief The abstract base class for filters\n * For more details on the class, see the wiki site (the 'Filter' section)\n */\nclass DROGON_EXPORT HttpFilterBase : public virtual DrObjectBase,\n                                     public HttpMiddlewareBase\n{\n  public:\n    /// This virtual function should be overridden in subclasses.\n    /**\n     * This method is an asynchronous interface, user should return the result\n     * via 'FilterCallback' or 'FilterChainCallback'.\n     * @param req is the request object processed by the filter\n     * @param fcb if this is called, the response object is send to the client\n     * by the callback, and doFilter methods of next filters and the handler\n     * registered on the path are not called anymore.\n     * @param fccb if this callback is called, the next filter's doFilter method\n     * or the handler registered on the path is called.\n     */\n    virtual void doFilter(const HttpRequestPtr &req,\n                          FilterCallback &&fcb,\n                          FilterChainCallback &&fccb) = 0;\n    ~HttpFilterBase() override = default;\n\n  private:\n    void invoke(const HttpRequestPtr &req,\n                MiddlewareNextCallback &&nextCb,\n                MiddlewareCallback &&mcb) final\n    {\n        auto mcbPtr = std::make_shared<MiddlewareCallback>(std::move(mcb));\n        doFilter(\n            req,\n            [mcbPtr](const HttpResponsePtr &resp) {\n                (*mcbPtr)(resp);\n            },  // fcb, intercept the response\n            [nextCb = std::move(nextCb), mcbPtr]() mutable {\n                nextCb([mcbPtr = std::move(mcbPtr)](\n                           const HttpResponsePtr &resp) { (*mcbPtr)(resp); });\n            }  // fccb, call the next middleware\n        );\n    }\n};\n\n/**\n * @brief The reflection base class template for filters\n *\n * @tparam T The type of the implementation class\n * @tparam AutoCreation The flag for automatically creating, user can set this\n * flag to false for classes that have non-default constructors.\n */\ntemplate <typename T, bool AutoCreation = true>\nclass HttpFilter : public DrObject<T>, public HttpFilterBase\n{\n  public:\n    static constexpr bool isAutoCreation{AutoCreation};\n    ~HttpFilter() override = default;\n};\n\n#ifdef __cpp_impl_coroutine\ntemplate <typename T, bool AutoCreation = true>\nclass HttpCoroFilter : public DrObject<T>, public HttpFilterBase\n{\n  public:\n    static constexpr bool isAutoCreation{AutoCreation};\n    ~HttpCoroFilter() override = default;\n\n    void doFilter(const HttpRequestPtr &req,\n                  FilterCallback &&fcb,\n                  FilterChainCallback &&fccb) final\n    {\n        drogon::async_run([this,\n                           req,\n                           fcb = std::move(fcb),\n                           fccb = std::move(fccb)]() mutable -> drogon::Task<> {\n            HttpResponsePtr resp;\n            try\n            {\n                resp = co_await doFilter(req);\n            }\n            catch (const std::exception &ex)\n            {\n                internal::handleException(ex, req, std::move(fcb));\n                co_return;\n            }\n            catch (...)\n            {\n                LOG_ERROR << \"Exception not derived from std::exception\";\n                co_return;\n            }\n\n            if (resp)\n            {\n                fcb(resp);\n            }\n            else\n            {\n                fccb();\n            }\n        });\n    }\n\n    virtual Task<HttpResponsePtr> doFilter(const HttpRequestPtr &req) = 0;\n};\n#endif\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/HttpMiddleware.h",
    "content": "/**\n *\n *  @file HttpMiddleware.h\n *  @author Nitromelon\n *\n *  Copyright 2024, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include <drogon/drogon_callbacks.h>\n#include <drogon/HttpRequest.h>\n#include <drogon/HttpResponse.h>\n#include <memory>\n\n#ifdef __cpp_impl_coroutine\n#include <drogon/utils/coroutine.h>\n#endif\n\nnamespace drogon\n{\n/**\n * @brief The abstract base class for middleware\n */\nclass DROGON_EXPORT HttpMiddlewareBase : public virtual DrObjectBase\n{\n  public:\n    /**\n     * This virtual function should be overridden in subclasses.\n     *\n     * Example:\n     * @code\n     * void invoke(const HttpRequestPtr &req,\n     *             MiddlewareNextCallback &&nextCb,\n     *             MiddlewareCallback &&mcb) override\n     *  {\n     *     if (req->path() == \"/some/path\") {\n     *         // intercept directly\n     *         mcb(HttpResponse::newNotFoundResponse(req));\n     *         return;\n     *     }\n     *     // Do something before calling the next middleware\n     *     nextCb([mcb = std::move(mcb)](const HttpResponsePtr &resp) {\n     *         // Do something after the next middleware returns\n     *         mcb(resp);\n     *     });\n     * }\n     * @endcode\n     *\n     */\n    virtual void invoke(const HttpRequestPtr &req,\n                        MiddlewareNextCallback &&nextCb,\n                        MiddlewareCallback &&mcb) = 0;\n    ~HttpMiddlewareBase() override = default;\n};\n\n/**\n * @brief The reflection base class template for middlewares\n *\n * @tparam T The type of the implementation class\n * @tparam AutoCreation The flag for automatically creating, user can set this\n * flag to false for classes that have non-default constructors.\n */\ntemplate <typename T, bool AutoCreation = true>\nclass HttpMiddleware : public DrObject<T>, public HttpMiddlewareBase\n{\n  public:\n    static constexpr bool isAutoCreation{AutoCreation};\n    ~HttpMiddleware() override = default;\n};\n\nnamespace internal\n{\nDROGON_EXPORT void handleException(\n    const std::exception &,\n    const HttpRequestPtr &,\n    std::function<void(const HttpResponsePtr &)> &&);\n}\n\n#ifdef __cpp_impl_coroutine\n\nstruct [[nodiscard]] MiddlewareNextAwaiter\n    : public CallbackAwaiter<HttpResponsePtr>\n{\n  public:\n    MiddlewareNextAwaiter(MiddlewareNextCallback &&nextCb)\n        : nextCb_(std::move(nextCb))\n    {\n    }\n\n    void await_suspend(std::coroutine_handle<> handle) noexcept\n    {\n        nextCb_([this, handle](const HttpResponsePtr &resp) {\n            setValue(resp);\n            handle.resume();\n        });\n    }\n\n  private:\n    MiddlewareNextCallback nextCb_;\n};\n\ntemplate <typename T, bool AutoCreation = true>\nclass HttpCoroMiddleware : public DrObject<T>, public HttpMiddlewareBase\n{\n  public:\n    static constexpr bool isAutoCreation{AutoCreation};\n    ~HttpCoroMiddleware() override = default;\n\n    void invoke(const HttpRequestPtr &req,\n                MiddlewareNextCallback &&nextCb,\n                MiddlewareCallback &&mcb) final\n    {\n        drogon::async_run([this,\n                           req,\n                           nextCb = std::move(nextCb),\n                           mcb = std::move(mcb)]() mutable -> drogon::Task<> {\n            HttpResponsePtr resp;\n            try\n            {\n                resp = co_await invoke(req, {std::move(nextCb)});\n            }\n            catch (const std::exception &ex)\n            {\n                internal::handleException(ex, req, std::move(mcb));\n                co_return;\n            }\n            catch (...)\n            {\n                LOG_ERROR << \"Exception not derived from std::exception\";\n                co_return;\n            }\n\n            mcb(resp);\n        });\n    }\n\n    virtual Task<HttpResponsePtr> invoke(const HttpRequestPtr &req,\n                                         MiddlewareNextAwaiter &&next) = 0;\n};\n\n#endif\n\n/**\n * @brief Simple middleware that tags OPTIONS requests\n * @details It adds the attribute \"drogon.customCORShandling\" to the request, so\n * that HttpServer does not handle CORS for them internally.\n *\n * This allows custom CORS handling via the path handlers.\n * For example to restrict the origins, headers allowed, specify a max age to\n * avoid OPTIONS on every request, etc.\n *\n * Just register it:\n * 1. globally via\n *   app().registerMiddleware(std::make_shared<drogon::HttpOptionsMiddleware>())\n * 2. on every path handlers that need non-default handling, with\n *    ADD_METHOD_TO(..., drogon::Options, \"drogon::HttpOptionsMiddleware\")\n */\ntemplate <class Derived, bool AutoCreation = true>\nclass HttpOptionsMiddlewareImpl\n    : public drogon::HttpMiddleware<Derived, AutoCreation>\n{\n  public:\n    void invoke(const HttpRequestPtr &req,\n                MiddlewareNextCallback &&nextCb,\n                MiddlewareCallback &&mcb) override\n    {\n        // Tag OPTIONS\n        if (req->method() == drogon::HttpMethod::Options)\n            req->attributes()->insert(\"drogon.customCORShandling\", true);\n        // continue with next middleware (no post-processing here)\n        nextCb(std::move(mcb));\n    }\n};\n\nclass HttpOptionsMiddlewareAuto\n    : public HttpOptionsMiddlewareImpl<HttpOptionsMiddlewareAuto, true>\n{\n};\n\nclass HttpOptionsMiddleware\n    : public HttpOptionsMiddlewareImpl<HttpOptionsMiddleware, false>\n{\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/HttpRequest.h",
    "content": "/**\n *\n *  @file HttpRequest.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <drogon/utils/Utilities.h>\n#include <drogon/DrClassMap.h>\n#include <drogon/HttpTypes.h>\n#include <drogon/Session.h>\n#include <drogon/Attribute.h>\n#include <drogon/UploadFile.h>\n#include <json/json.h>\n#include <trantor/net/InetAddress.h>\n#include <trantor/net/Certificate.h>\n#include <trantor/utils/Date.h>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <optional>\n#include <string_view>\n#include <trantor/net/TcpConnection.h>\n\nnamespace drogon\n{\nclass HttpRequest;\nusing HttpRequestPtr = std::shared_ptr<HttpRequest>;\n\n/**\n * @brief This template is used to convert a request object to a custom\n * type object. Users must specialize the template for a particular type.\n */\ntemplate <typename T>\nT fromRequest(const HttpRequest &)\n{\n    LOG_ERROR << \"You must specialize the fromRequest template for the type of \"\n              << DrClassMap::demangle(typeid(T).name());\n    exit(1);\n}\n\n/**\n * @brief This template is used to create a request object from a custom\n * type object by calling the newCustomHttpRequest(). Users must specialize\n * the template for a particular type.\n */\ntemplate <typename T>\nHttpRequestPtr toRequest(T &&)\n{\n    LOG_ERROR << \"You must specialize the toRequest template for the type of \"\n              << DrClassMap::demangle(typeid(T).name());\n    exit(1);\n}\n\ntemplate <>\nHttpRequestPtr toRequest<const Json::Value &>(const Json::Value &pJson);\ntemplate <>\nHttpRequestPtr toRequest(Json::Value &&pJson);\n\ntemplate <>\ninline HttpRequestPtr toRequest<Json::Value &>(Json::Value &pJson)\n{\n    return toRequest((const Json::Value &)pJson);\n}\n\ntemplate <>\nstd::shared_ptr<Json::Value> fromRequest(const HttpRequest &req);\n\n/// Abstract class for webapp developer to get or set the Http request;\nclass DROGON_EXPORT HttpRequest\n{\n  public:\n    /**\n     * @brief This template enables implicit type conversion. For using this\n     * template, user must specialize the fromRequest template. For example a\n     * shared_ptr<Json::Value> specialization version is available above, so\n     * we can use the following code to get a json object:\n     * @code\n       std::shared_ptr<Json::Value> jsonPtr = *requestPtr;\n       @endcode\n     * With this template, user can use their favorite JSON library instead of\n     * the default jsoncpp library or convert the request to an object of any\n     * custom type.\n     */\n    template <typename T>\n    operator T() const\n    {\n        return fromRequest<T>(*this);\n    }\n\n    /**\n     * @brief This template enables explicit type conversion, see the above\n     * template.\n     */\n    template <typename T>\n    T as() const\n    {\n        return fromRequest<T>(*this);\n    }\n\n    /// Return the method string of the request, such as GET, POST, etc.\n    virtual const char *methodString() const = 0;\n\n    const char *getMethodString() const\n    {\n        return methodString();\n    }\n\n    /// Return the enum type method of the request.\n    virtual HttpMethod method() const = 0;\n\n    HttpMethod getMethod() const\n    {\n        return method();\n    }\n\n    /**\n     * @brief Check if the method is or was HttpMethod::Head\n     * @details Allows to know that an incoming request is a HEAD request, since\n     *          drogon sets the method to HttpMethod::Get before calling the\n     *          controller\n     * @return true if method() returns HttpMethod::Head, or HttpMethod::Get but\n     *              was previously HttpMethod::Head\n     */\n    virtual bool isHead() const = 0;\n\n    /// Get the header string identified by the key parameter.\n    /**\n     * @note\n     * If there is no the header, a empty string is returned.\n     * The key is case insensitive\n     */\n    virtual const std::string &getHeader(std::string key) const = 0;\n\n    /**\n     * @brief Set the header string identified by the field parameter\n     *\n     * @param field The field parameter is transformed to lower case before\n     * storing.\n     * @param value The value of the header.\n     */\n    virtual void addHeader(std::string field, const std::string &value) = 0;\n    virtual void addHeader(std::string field, std::string &&value) = 0;\n\n    /**\n     * @brief  Remove the header identified by the key parameter.\n     *\n     * @param key The key is case insensitive\n     */\n    virtual void removeHeader(std::string key) = 0;\n\n    /// Get the cookie string identified by the field parameter\n    virtual const std::string &getCookie(const std::string &field) const = 0;\n\n    /// Get all headers of the request\n    virtual const SafeStringMap<std::string> &headers() const = 0;\n\n    /// Get all headers of the request\n    const SafeStringMap<std::string> &getHeaders() const\n    {\n        return headers();\n    }\n\n    /// Get all cookies of the request\n    virtual const SafeStringMap<std::string> &cookies() const = 0;\n\n    /// Get all cookies of the request\n    const SafeStringMap<std::string> &getCookies() const\n    {\n        return cookies();\n    }\n\n    /**\n     * @brief Return content length parsed from the Content-Length header\n     * If no Content-Length header, return null.\n     */\n    virtual size_t realContentLength() const = 0;\n\n    size_t getRealContentLength() const\n    {\n        return realContentLength();\n    }\n\n    /// Get the query string of the request.\n    /**\n     * The query string is the substring after the '?' in the URL string.\n     */\n    virtual const std::string &query() const = 0;\n\n    /// Get the query string of the request.\n    const std::string &getQuery() const\n    {\n        return query();\n    }\n\n    /// Get the content string of the request, which is the body part of the\n    /// request.\n    std::string_view body() const\n    {\n        return std::string_view(bodyData(), bodyLength());\n    }\n\n    /// Get the content string of the request, which is the body part of the\n    /// request.\n    std::string_view getBody() const\n    {\n        return body();\n    }\n\n    virtual const char *bodyData() const = 0;\n    virtual size_t bodyLength() const = 0;\n\n    /// Set the content string of the request.\n    virtual void setBody(const std::string &body) = 0;\n\n    /// Set the content string of the request.\n    virtual void setBody(std::string &&body) = 0;\n\n    /// Get the path of the request.\n    virtual const std::string &path() const = 0;\n\n    /// Get the original path of the request.(before url-decoding)\n    virtual const std::string &getOriginalPath() const = 0;\n\n    /// Get the path of the request.\n    const std::string &getPath() const\n    {\n        return path();\n    }\n\n    /// Get the matched path pattern after routing\n    std::string_view getMatchedPathPattern() const\n    {\n        return matchedPathPattern();\n    }\n\n    /// Get the matched path pattern after routing\n    std::string_view matchedPathPattern() const\n    {\n        return std::string_view(matchedPathPatternData(),\n                                matchedPathPatternLength());\n    }\n\n    /// Get the matched path pattern after routing (including matched parameters\n    /// in the query string)\n    virtual const std::vector<std::string> &getRoutingParameters() const = 0;\n\n    /// This method usually is called by the framework.\n    virtual void setRoutingParameters(std::vector<std::string> &&params) = 0;\n\n    virtual const char *matchedPathPatternData() const = 0;\n    virtual size_t matchedPathPatternLength() const = 0;\n\n    /// Return the string of http version of request, such as HTTP/1.0,\n    /// HTTP/1.1, etc.\n    virtual const char *versionString() const = 0;\n\n    const char *getVersionString() const\n    {\n        return versionString();\n    }\n\n    /// Return the enum type version of the request.\n    /**\n     * kHttp10 means Http version is 1.0\n     * kHttp11 means Http version is 1.1\n     */\n    virtual Version version() const = 0;\n\n    /// Return the enum type version of the request.\n    Version getVersion() const\n    {\n        return version();\n    }\n\n    /// Get the session to which the request belongs.\n    virtual const SessionPtr &session() const = 0;\n\n    /// Get the session to which the request belongs.\n    const SessionPtr &getSession() const\n    {\n        return session();\n    }\n\n    /// Get the attributes store, users can add/get any type of data to/from\n    /// this store\n    virtual const AttributesPtr &attributes() const = 0;\n\n    /// Get the attributes store, users can add/get any type of data to/from\n    /// this store\n    const AttributesPtr &getAttributes() const\n    {\n        return attributes();\n    }\n\n    /// Get parameters of the request.\n    virtual const SafeStringMap<std::string> &parameters() const = 0;\n\n    /// Get parameters of the request.\n    const SafeStringMap<std::string> &getParameters() const\n    {\n        return parameters();\n    }\n\n    /// Get a parameter identified by the @param key\n    virtual const std::string &getParameter(const std::string &key) const = 0;\n\n    /**\n     * @brief Get the optional parameter identified by the @p key. if the\n     * parameter doesn't exist, or the original parameter can't be converted to\n     * a T type object, an empty optional object is returned.\n     *\n     * @tparam T\n     * @param key\n     * @return optional<T>\n     */\n    template <typename T>\n    std::optional<T> getOptionalParameter(const std::string &key)\n    {\n        auto &params = getParameters();\n        auto it = params.find(key);\n        if (it != params.end())\n        {\n            try\n            {\n                return std::optional<T>(\n                    drogon::utils::fromString<T>(it->second));\n            }\n            catch (const std::exception &e)\n            {\n                LOG_ERROR << e.what();\n                return std::optional<T>{};\n            }\n        }\n        else\n        {\n            return std::optional<T>{};\n        }\n    }\n\n    /// Return the remote IP address and port\n    virtual const trantor::InetAddress &peerAddr() const = 0;\n\n    const trantor::InetAddress &getPeerAddr() const\n    {\n        return peerAddr();\n    }\n\n    /// Return the local IP address and port\n    virtual const trantor::InetAddress &localAddr() const = 0;\n\n    const trantor::InetAddress &getLocalAddr() const\n    {\n        return localAddr();\n    }\n\n    /// Return the creation timestamp set by the framework.\n    virtual const trantor::Date &creationDate() const = 0;\n\n    const trantor::Date &getCreationDate() const\n    {\n        return creationDate();\n    }\n\n    // Return the peer certificate (if any)\n    virtual const trantor::CertificatePtr &peerCertificate() const = 0;\n\n    const trantor::CertificatePtr &getPeerCertificate() const\n    {\n        return peerCertificate();\n    }\n\n    /// Get the Json object of the request\n    /**\n     * The content type of the request must be 'application/json',\n     * otherwise the method returns an empty shared_ptr object.\n     */\n    virtual const std::shared_ptr<Json::Value> &jsonObject() const = 0;\n\n    /// Get the Json object of the request\n    const std::shared_ptr<Json::Value> &getJsonObject() const\n    {\n        return jsonObject();\n    }\n\n    /**\n     * @brief Get the error message of parsing the JSON body received from peer.\n     * This method usually is called after getting a empty shared_ptr object\n     * by the getJsonObject() method.\n     *\n     * @return const std::string& The error message. An empty string is returned\n     * when no error occurs.\n     */\n    virtual const std::string &getJsonError() const = 0;\n\n    /// Get the content type\n    virtual ContentType contentType() const = 0;\n\n    ContentType getContentType() const\n    {\n        return contentType();\n    }\n\n    /// Set the Http method\n    virtual void setMethod(const HttpMethod method) = 0;\n\n    /// Set the path of the request\n    virtual void setPath(const std::string &path) = 0;\n    virtual void setPath(std::string &&path) = 0;\n\n    /**\n     * @brief The default behavior is to encode the value of setPath\n     * using urlEncode. Setting the path encode to false avoid the\n     * value of path will be changed by the library\n     *\n     * @param bool true --> the path will be url encoded\n     *             false --> using value of path as it is set\n     */\n    virtual void setPathEncode(bool) = 0;\n\n    /// Set the parameter of the request\n    virtual void setParameter(const std::string &key,\n                              const std::string &value) = 0;\n\n    /// Set or get the content type\n    virtual void setContentTypeCode(const ContentType type) = 0;\n\n    /// Set the content-type string, The string may contain the header name and\n    /// CRLF. Or just the MIME type\n    //\n    /// For example, \"content-type: text/plain\\r\\n\" or \"text/plain\"\n    void setContentTypeString(const std::string_view &typeString)\n    {\n        setContentTypeString(typeString.data(), typeString.size());\n    }\n\n    /// Set the request content-type string, The string\n    /// must contain the header name and CRLF.\n    /// For example, \"content-type: text/plain\\r\\n\"\n    virtual void setCustomContentTypeString(const std::string &type) = 0;\n\n    /// Add a cookie\n    virtual void addCookie(std::string key, std::string value) = 0;\n\n    /**\n     * @brief Set the request object to the pass-through mode or not. It's not\n     * by default when a new request object is created.\n     * In pass-through mode, no additional headers (including user-agent,\n     * connection, etc.) are added to the request. This mode is useful for some\n     * applications such as a proxy.\n     *\n     * @param flag\n     */\n    virtual void setPassThrough(bool flag) = 0;\n\n    /// The following methods are a series of factory methods that help users\n    /// create request objects.\n\n    /// Create a normal request with http method Get and version Http1.1.\n    static HttpRequestPtr newHttpRequest();\n\n    /// Create a http request with:\n    /// Method: Get\n    /// Version: Http1.1\n    /// Content type: application/json, the @param data is serialized into the\n    /// content of the request.\n    static HttpRequestPtr newHttpJsonRequest(const Json::Value &data);\n\n    /// Create a http request with:\n    /// Method: Post\n    /// Version: Http1.1\n    /// Content type: application/x-www-form-urlencoded\n    static HttpRequestPtr newHttpFormPostRequest();\n\n    /// Create a http file upload request with:\n    /// Method: Post\n    /// Version: Http1.1\n    /// Content type: multipart/form-data\n    /// The @param files represents pload files which are transferred to the\n    /// server via the multipart/form-data format\n    static HttpRequestPtr newFileUploadRequest(\n        const std::vector<UploadFile> &files);\n\n    /**\n     * @brief Create a custom HTTP request object. For using this template,\n     * users must specialize the toRequest template.\n     */\n    template <typename T>\n    static HttpRequestPtr newCustomHttpRequest(T &&obj)\n    {\n        return toRequest(std::forward<T>(obj));\n    }\n\n    virtual bool isOnSecureConnection() const noexcept = 0;\n    virtual void setContentTypeString(const char *typeString,\n                                      size_t typeStringLength) = 0;\n\n    virtual bool connected() const noexcept = 0;\n\n    virtual const std::weak_ptr<trantor::TcpConnection> &getConnectionPtr()\n        const noexcept = 0;\n\n    virtual ~HttpRequest()\n    {\n    }\n};\n\ntemplate <>\ninline HttpRequestPtr toRequest<const Json::Value &>(const Json::Value &pJson)\n{\n    return HttpRequest::newHttpJsonRequest(pJson);\n}\n\ntemplate <>\ninline HttpRequestPtr toRequest(Json::Value &&pJson)\n{\n    return HttpRequest::newHttpJsonRequest(std::move(pJson));\n}\n\ntemplate <>\ninline std::shared_ptr<Json::Value> fromRequest(const HttpRequest &req)\n{\n    return req.getJsonObject();\n}\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/HttpResponse.h",
    "content": "/**\n *  @file HttpResponse.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <trantor/net/Certificate.h>\n#include <trantor/net/callbacks.h>\n#include <trantor/net/AsyncStream.h>\n#include <drogon/DrClassMap.h>\n#include <drogon/Cookie.h>\n#include <drogon/HttpRequest.h>\n#include <drogon/HttpTypes.h>\n#include <drogon/HttpViewData.h>\n#include <drogon/utils/Utilities.h>\n#include <json/json.h>\n#include <memory>\n#include <string>\n#include <string_view>\n\nnamespace drogon\n{\n/// Abstract class for webapp developer to get or set the Http response;\nclass HttpResponse;\nusing HttpResponsePtr = std::shared_ptr<HttpResponse>;\n\n/**\n * @brief This template is used to convert a response object to a custom\n * type object. Users must specialize the template for a particular type.\n */\ntemplate <typename T>\nT fromResponse(const HttpResponse &)\n{\n    LOG_ERROR\n        << \"You must specialize the fromResponse template for the type of \"\n        << DrClassMap::demangle(typeid(T).name());\n    exit(1);\n}\n\n/**\n * @brief This template is used to create a response object from a custom\n * type object by calling the newCustomHttpResponse(). Users must specialize\n * the template for a particular type.\n */\ntemplate <typename T>\nHttpResponsePtr toResponse(T &&)\n{\n    LOG_ERROR << \"You must specialize the toResponse template for the type of \"\n              << DrClassMap::demangle(typeid(T).name());\n    exit(1);\n}\n\ntemplate <>\nHttpResponsePtr toResponse<const Json::Value &>(const Json::Value &pJson);\ntemplate <>\nHttpResponsePtr toResponse(Json::Value &&pJson);\n\ntemplate <>\ninline HttpResponsePtr toResponse<Json::Value &>(Json::Value &pJson)\n{\n    return toResponse((const Json::Value &)pJson);\n}\n\nclass DROGON_EXPORT ResponseStream\n{\n  public:\n    explicit ResponseStream(trantor::AsyncStreamPtr asyncStream)\n        : asyncStream_(std::move(asyncStream))\n    {\n    }\n\n    ~ResponseStream()\n    {\n        close();\n    }\n\n    bool send(const std::string &data)\n    {\n        if (!asyncStream_)\n        {\n            return false;\n        }\n        std::ostringstream oss;\n        oss << std::hex << data.length() << \"\\r\\n\";\n        oss << data << \"\\r\\n\";\n        return asyncStream_->send(oss.str());\n    }\n\n    void close()\n    {\n        if (asyncStream_)\n        {\n            static std::string closeStream{\"0\\r\\n\\r\\n\"};\n            asyncStream_->send(closeStream);\n            asyncStream_->close();\n            asyncStream_.reset();\n        }\n    }\n\n  private:\n    trantor::AsyncStreamPtr asyncStream_;\n};\n\nusing ResponseStreamPtr = std::unique_ptr<ResponseStream>;\n\nclass DROGON_EXPORT HttpResponse\n{\n  public:\n    /**\n     * @brief This template enables automatic type conversion. For using this\n     * template, user must specialize the fromResponse template. For example a\n     * shared_ptr<Json::Value> specialization version is available above, so\n     * we can use the following code to get a json object:\n     * @code\n     *  std::shared_ptr<Json::Value> jsonPtr = *responsePtr;\n     *  @endcode\n     * With this template, user can use their favorite JSON library instead of\n     * the default jsoncpp library or convert the response to an object of any\n     * custom type.\n     */\n    template <typename T>\n    operator T() const\n    {\n        return fromResponse<T>(*this);\n    }\n\n    /**\n     * @brief This template enables explicit type conversion, see the above\n     * template.\n     */\n    template <typename T>\n    T as() const\n    {\n        return fromResponse<T>(*this);\n    }\n\n    /// Get the status code such as 200, 404\n    virtual HttpStatusCode statusCode() const = 0;\n\n    HttpStatusCode getStatusCode() const\n    {\n        return statusCode();\n    }\n\n    /// Set the status code of the response.\n    virtual void setStatusCode(HttpStatusCode code) = 0;\n\n    void setCustomStatusCode(int code,\n                             std::string_view message = std::string_view{})\n    {\n        setCustomStatusCode(code, message.data(), message.length());\n    }\n\n    /// Set whether the response should be compress.\n    virtual void setAllowCompression(bool allow) = 0;\n\n    /// Get whether the response allow compression.\n    virtual bool allowCompression() const = 0;\n\n    /// Get the creation timestamp of the response.\n    virtual const trantor::Date &creationDate() const = 0;\n\n    const trantor::Date &getCreationDate() const\n    {\n        return creationDate();\n    }\n\n    /// Set the http version, http1.0 or http1.1\n    virtual void setVersion(const Version v) = 0;\n\n    /// Set if close the connection after the request is sent.\n    /**\n     * @param on if the parameter is false, the connection keeps alive on the\n     * condition that the client request has a 'keep-alive' head, otherwise it\n     * is closed immediately after sending the last byte of the response. It's\n     * false by default when the response is created.\n     */\n    virtual void setCloseConnection(bool on) = 0;\n\n    /// Get the status set by the setCloseConnection() method.\n    virtual bool ifCloseConnection() const = 0;\n\n    /// Set the response content type, such as text/html, text/plain, image/png\n    /// and so on. If the content type\n    /// is a text type, the character set is utf8.\n    virtual void setContentTypeCode(ContentType type) = 0;\n\n    /// Set the content-type string, The string may contain the header name and\n    /// CRLF. Or just the MIME type For example, \"content-type: text/plain\\r\\n\"\n    /// or \"text/plain\"\n    void setContentTypeString(const std::string_view &typeString)\n    {\n        setContentTypeString(typeString.data(), typeString.size());\n    }\n\n    /// Set the response content type and the content-type string, The string\n    /// may contain the header name and CRLF. Or just the MIME type\n    /// For example, \"content-type: text/plain\\r\\n\" or \"text/plain\"\n    void setContentTypeCodeAndCustomString(ContentType type,\n                                           const std::string_view &typeString)\n    {\n        setContentTypeCodeAndCustomString(type,\n                                          typeString.data(),\n                                          typeString.length());\n    }\n\n    template <int N>\n    void setContentTypeCodeAndCustomString(ContentType type,\n                                           const char (&typeString)[N])\n    {\n        assert(N > 0);\n        setContentTypeCodeAndCustomString(type, typeString, N - 1);\n    }\n\n    /// Set the response content type and the character set.\n    /// virtual void setContentTypeCodeAndCharacterSet(ContentType type, const\n    /// std::string &charSet = \"utf-8\") = 0;\n\n    /// Get the response content type.\n    virtual ContentType contentType() const = 0;\n\n    ContentType getContentType() const\n    {\n        return contentType();\n    }\n\n    /// Get the header string identified by the key parameter.\n    /**\n     * @note\n     * If there is no the header, a empty string is returned.\n     * The key is case insensitive\n     */\n    virtual const std::string &getHeader(std::string key) const = 0;\n\n    /**\n     * @brief  Remove the header identified by the key parameter.\n     *\n     * @param key The key is case insensitive\n     */\n    virtual void removeHeader(std::string key) = 0;\n\n    /// Get all headers of the response\n    virtual const SafeStringMap<std::string> &headers() const = 0;\n\n    /// Get all headers of the response\n    const SafeStringMap<std::string> &getHeaders() const\n    {\n        return headers();\n    }\n\n    /**\n     * @brief Set the header string identified by the field parameter\n     *\n     * @param field The field parameter is transformed to lower case before\n     * storing.\n     * @param value The value of the header.\n     */\n    virtual void addHeader(std::string field, const std::string &value) = 0;\n    virtual void addHeader(std::string field, std::string &&value) = 0;\n\n    /// Add a cookie\n    virtual void addCookie(const std::string &key,\n                           const std::string &value) = 0;\n\n    /// Add a cookie\n    virtual void addCookie(const Cookie &cookie) = 0;\n    virtual void addCookie(Cookie &&cookie) = 0;\n\n    /// Get the cookie identified by the key parameter.\n    /// If there is no the cookie, the empty cookie is returned.\n    virtual const Cookie &getCookie(const std::string &key) const = 0;\n\n    /// Get all cookies.\n    virtual const SafeStringMap<Cookie> &cookies() const = 0;\n\n    /// Get all cookies.\n    const SafeStringMap<Cookie> &getCookies() const\n    {\n        return cookies();\n    }\n\n    /// Remove the cookie identified by the key parameter.\n    virtual void removeCookie(const std::string &key) = 0;\n\n    /// Set the response body(content).\n    /**\n     * @note The body must match the content type\n     */\n    virtual void setBody(const std::string &body) = 0;\n\n    /// Set the response body(content).\n    virtual void setBody(std::string &&body) = 0;\n\n    /// Set the response body(content).\n    template <int N>\n    void setBody(const char (&body)[N])\n    {\n        assert(strnlen(body, N) == N - 1);\n        setBody(body, N - 1);\n    }\n\n    /// Get the response body.\n    std::string_view body() const\n    {\n        return std::string_view{getBodyData(), getBodyLength()};\n    }\n\n    /// Get the response body.\n    std::string_view getBody() const\n    {\n        return body();\n    }\n\n    /// Return the string of http version of request, such as HTTP/1.0,\n    /// HTTP/1.1, etc.\n    virtual const char *versionString() const = 0;\n\n    const char *getVersionString() const\n    {\n        return versionString();\n    }\n\n    /// Return the enum type version of the response.\n    /**\n     * kHttp10 means Http version is 1.0\n     * kHttp11 means Http version is 1.1\n     */\n    virtual Version version() const = 0;\n\n    /// Return the enum type version of the response.\n    Version getVersion() const\n    {\n        return version();\n    }\n\n    /// Reset the response object to its initial state\n    virtual void clear() = 0;\n\n    /// Set the expiration time of the response cache in memory.\n    /// in seconds, 0 means always cache, negative means not cache, default is\n    /// -1.\n    virtual void setExpiredTime(ssize_t expiredTime) = 0;\n\n    /// Get the expiration time of the response.\n    virtual ssize_t expiredTime() const = 0;\n\n    ssize_t getExpiredTime() const\n    {\n        return expiredTime();\n    }\n\n    /// Get the json object from the server response.\n    /// If the response is not in json format, then a empty shared_ptr is\n    /// returned.\n    virtual const std::shared_ptr<Json::Value> &jsonObject() const = 0;\n\n    const std::shared_ptr<Json::Value> &getJsonObject() const\n    {\n        return jsonObject();\n    }\n\n    /**\n     * @brief Get the error message of parsing the JSON body received from peer.\n     * This method usually is called after getting a empty shared_ptr object\n     * by the getJsonObject() method.\n     *\n     * @return const std::string& The error message. An empty string is returned\n     * when no error occurs.\n     */\n    virtual const std::string &getJsonError() const = 0;\n\n    /**\n     * @brief Set the response object to the pass-through mode or not. It's not\n     * by default when a new response object is created.\n     * In pass-through mode, no additional headers (including server, date,\n     * content-type and content-length, etc.) are added to the response. This\n     * mode is useful for some applications such as a proxy.\n     *\n     * @param flag\n     */\n    virtual void setPassThrough(bool flag) = 0;\n\n    /**\n     * @brief Get the certificate of the peer, if any.\n     * @return The certificate of the peer. nullptr is none.\n     */\n    virtual const trantor::CertificatePtr &peerCertificate() const = 0;\n\n    const trantor::CertificatePtr &getPeerCertificate() const\n    {\n        return peerCertificate();\n    }\n\n    /* The following methods are a series of factory methods that help users\n     * create response objects. */\n\n    /// Create a normal response with a status code of 200ok and a content type\n    /// of text/html.\n    static HttpResponsePtr newHttpResponse();\n    /// Create a response with a status code and a content type\n    static HttpResponsePtr newHttpResponse(HttpStatusCode code,\n                                           ContentType type);\n    /// Create a response which returns a 404 page.\n    static HttpResponsePtr newNotFoundResponse(\n        const HttpRequestPtr &req = HttpRequestPtr());\n    /// Create a response which returns a json object. Its content-type is set\n    /// to application/json.\n    static HttpResponsePtr newHttpJsonResponse(const Json::Value &data);\n    /// Create a response which returns a json object. Its content-type is set\n    /// to application/json.\n    static HttpResponsePtr newHttpJsonResponse(Json::Value &&data);\n    /// Create a response that returns a page rendered by a view named\n    /// viewName.\n    /**\n     * @param viewName The name of the view\n     * @param data is the data displayed on the page.\n     * @note For more details, see the wiki pages, the \"View\" section.\n     */\n    static HttpResponsePtr newHttpViewResponse(\n        const std::string &viewName,\n        const HttpViewData &data = HttpViewData(),\n        const HttpRequestPtr &req = HttpRequestPtr());\n\n    /// Create a response that returns a redirection page, redirecting to\n    /// another page located in the location parameter.\n    /**\n     * @param location The location to redirect\n     * @param status The HTTP status code, k302Found by default. Users could set\n     * it to one of the 301, 302, 303, 307, ...\n     */\n    static HttpResponsePtr newRedirectionResponse(\n        const std::string &location,\n        HttpStatusCode status = k302Found);\n\n    /// Create a response that returns a file to the client.\n    /**\n     * @param fullPath is the full path to the file.\n     * @param attachmentFileName if the parameter is not empty, the browser\n     * does not open the file, but saves it as an attachment.\n     * @param type the content type code. If the parameter is CT_NONE, the\n     * content type is set by drogon based on the file extension and typeString.\n     * Set it to CT_CUSTOM when no drogon internal content type matches.\n     * @param typeString the MIME string of the content type.\n     */\n    static HttpResponsePtr newFileResponse(\n        const std::string &fullPath,\n        const std::string &attachmentFileName = \"\",\n        ContentType type = CT_NONE,\n        const std::string &typeString = \"\",\n        const HttpRequestPtr &req = HttpRequestPtr());\n\n    /// Create a response that returns part of a file to the client.\n    /**\n     * @brief If offset and length can not be satisfied, statusCode will be set\n     * to k416RequestedRangeNotSatisfiable, and nothing else will be modified.\n     *\n     * @param fullPath is the full path to the file.\n     * @param offset is the offset to begin sending, in bytes.\n     * @param length is the total length to send, in bytes. In particular,\n     * length = 0 means send all content from offset till end of file.\n     * @param setContentRange whether set 'Content-Range' header automatically.\n     * @param attachmentFileName if the parameter is not empty, the browser\n     * does not open the file, but saves it as an attachment.\n     * @param type the content type code. If the parameter is CT_NONE, the\n     * content type is set by drogon based on the file extension and typeString.\n     * Set it to CT_CUSTOM when no drogon internal content type matches.\n     * @param typeString the MIME string of the content type.\n     */\n    static HttpResponsePtr newFileResponse(\n        const std::string &fullPath,\n        size_t offset,\n        size_t length,\n        bool setContentRange = true,\n        const std::string &attachmentFileName = \"\",\n        ContentType type = CT_NONE,\n        const std::string &typeString = \"\",\n        const HttpRequestPtr &req = HttpRequestPtr());\n\n    /// Create a response that returns a file to the client from buffer in\n    /// memory/stack\n    /**\n     * @param pBuffer is a uint 8 bit flat buffer for object/files in memory\n     * @param bufferLength is the length of the expected buffer\n     * @param attachmentFileName if the parameter is not empty, the browser\n     * does not open the file, but saves it as an attachment.\n     * @param type the content type code. If the parameter is CT_NONE, the\n     * content type is set by drogon based on the file extension and typeString.\n     * Set it to CT_CUSTOM when no drogon internal content type matches.\n     * @param typeString the MIME string of the content type.\n     */\n    static HttpResponsePtr newFileResponse(\n        const unsigned char *pBuffer,\n        size_t bufferLength,\n        const std::string &attachmentFileName = \"\",\n        ContentType type = CT_NONE,\n        const std::string &typeString = \"\");\n\n    /// Create a response that returns a file to the client from a callback\n    /// function\n    /**\n     * @note if the Connection is keep-alive and the Content-Length header is\n     * not set, the stream data is sent with Transfer-Encoding: chunked.\n     * @param callback function to retrieve the stream data (stream ends when a\n     *                 zero size is returned) the callback will be called with\n     *                 nullptr when the send is finished/interrupted so that it\n     *                 cleans up its internals.\n     * @param attachmentFileName if the parameter is not empty, the browser\n     *                           does not open the file, but saves it as an\n     *                           attachment.\n     * @param type the content type code. If the parameter is CT_NONE, the\n     *             content type is set by drogon based on the file extension and\n     *             typeString. Set it to CT_CUSTOM when no drogon internal\n     *             content type matches.\n     * @param typeString the MIME string of the content type.\n     */\n    static HttpResponsePtr newStreamResponse(\n        const std::function<std::size_t(char *, std::size_t)> &callback,\n        const std::string &attachmentFileName = \"\",\n        ContentType type = CT_NONE,\n        const std::string &typeString = \"\",\n        const HttpRequestPtr &req = HttpRequestPtr());\n\n    /// Create a response that allows sending asynchronous data from a callback\n    /// function\n    /**\n     * @note Async streams are always sent with Transfer-Encoding: chunked.\n     * @param callback function that receives the asynchronous HTTP stream. You\n     *                 may call the stream->send() method to transmit new data.\n     *                 The send method will return true as long as the stream is\n     *                 still open. Once you have finished sending data, or the\n     *                 stream->send() function returned false, you should call\n     *                 stream->close() to gracefully close the chunked transfer.\n     * @param disableKickoffTimeout set this to true to disable trantors default\n     *                              kickoff timeout. This is useful if you need\n     *                              long running asynchronous streams.\n     */\n    static HttpResponsePtr newAsyncStreamResponse(\n        const std::function<void(ResponseStreamPtr)> &callback,\n        bool disableKickoffTimeout = false);\n\n    /**\n     * @brief Create a custom HTTP response object. For using this template,\n     * users must specialize the toResponse template.\n     */\n    template <typename T>\n    static HttpResponsePtr newCustomHttpResponse(T &&obj)\n    {\n        return toResponse(std::forward<T>(obj));\n    }\n\n    /**\n     * @brief If the response is a file response (i.e. created by\n     * newFileResponse) returns the path on the filesystem. Otherwise a\n     * empty string.\n     */\n    virtual const std::string &sendfileName() const = 0;\n\n    /**\n     * @brief Returns the range of the file response as a pair of size_t\n     * (offset, length). Length of 0 means the entire file is sent. Behavior of\n     * this function is undefined if the response is not a file response\n     */\n    using SendfileRange = std::pair<size_t, size_t>;  // { offset, length }\n    virtual const SendfileRange &sendfileRange() const = 0;\n\n    /**\n     * @brief If the response is a stream response (i.e. created by\n     * newStreamResponse) returns the callback function. Otherwise a\n     * null function.\n     */\n    virtual const std::function<std::size_t(char *, std::size_t)> &\n    streamCallback() const = 0;\n\n    /**\n     * @brief If the response is a async stream response (i.e. created by\n     * asyncStreamCallback) returns the stream ptr.\n     */\n    virtual const std::function<void(ResponseStreamPtr)> &asyncStreamCallback()\n        const = 0;\n\n    /**\n     * @brief Returns the content type associated with the response\n     */\n    virtual std::string contentTypeString() const = 0;\n\n    virtual ~HttpResponse()\n    {\n    }\n\n  private:\n    virtual void setBody(const char *body, size_t len) = 0;\n    virtual const char *getBodyData() const = 0;\n    virtual size_t getBodyLength() const = 0;\n    virtual void setContentTypeCodeAndCustomString(ContentType type,\n                                                   const char *typeString,\n                                                   size_t typeStringLength) = 0;\n    virtual void setContentTypeString(const char *typeString,\n                                      size_t typeStringLength) = 0;\n    virtual void setCustomStatusCode(int code,\n                                     const char *message,\n                                     size_t messageLength) = 0;\n};\n\ntemplate <>\ninline HttpResponsePtr toResponse<const Json::Value &>(const Json::Value &pJson)\n{\n    return HttpResponse::newHttpJsonResponse(pJson);\n}\n\ntemplate <>\ninline HttpResponsePtr toResponse(Json::Value &&pJson)\n{\n    return HttpResponse::newHttpJsonResponse(std::move(pJson));\n}\n\ntemplate <>\ninline std::shared_ptr<Json::Value> fromResponse(const HttpResponse &resp)\n{\n    return resp.getJsonObject();\n}\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/HttpSimpleController.h",
    "content": "/**\n *\n *  HttpSimpleController.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include <drogon/utils/HttpConstraint.h>\n#include <drogon/HttpAppFramework.h>\n#include <trantor/utils/Logger.h>\n#include <iostream>\n#include <string>\n#include <vector>\n#define PATH_LIST_BEGIN           \\\n    static void initPathRouting() \\\n    {\n#define PATH_ADD(path, ...) registerSelf__(path, {__VA_ARGS__})\n#define PATH_LIST_END }\n\nnamespace drogon\n{\n/**\n * @brief The abstract base class for HTTP simple controllers.\n *\n */\nclass HttpSimpleControllerBase : public virtual DrObjectBase\n{\n  public:\n    /**\n     * @brief The function is called when a HTTP request is routed to the\n     * controller.\n     *\n     * @param req The HTTP request.\n     * @param callback The callback via which a response is returned.\n     */\n    virtual void asyncHandleHttpRequest(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) = 0;\n\n    virtual ~HttpSimpleControllerBase()\n    {\n    }\n};\n\n/**\n * @brief The reflection base class template for HTTP simple controllers\n *\n * @tparam T The type of the implementation class\n * @tparam AutoCreation The flag for automatically creating, user can set this\n * flag to false for classes that have nondefault constructors.\n */\ntemplate <typename T, bool AutoCreation = true>\nclass HttpSimpleController : public DrObject<T>, public HttpSimpleControllerBase\n{\n  public:\n    static const bool isAutoCreation = AutoCreation;\n\n    virtual ~HttpSimpleController()\n    {\n    }\n\n  protected:\n    HttpSimpleController()\n    {\n    }\n\n    static void registerSelf__(\n        const std::string &path,\n        const std::vector<internal::HttpConstraint> &constraints)\n    {\n        LOG_TRACE << \"register simple controller(\"\n                  << HttpSimpleController<T, AutoCreation>::classTypeName()\n                  << \") on path:\" << path;\n        app().registerHttpSimpleController(\n            path,\n            HttpSimpleController<T, AutoCreation>::classTypeName(),\n            constraints);\n    }\n\n  private:\n    class pathRegistrator\n    {\n      public:\n        pathRegistrator()\n        {\n            if (AutoCreation)\n            {\n                T::initPathRouting();\n            }\n        }\n    };\n\n    friend pathRegistrator;\n    static pathRegistrator registrator_;\n\n    virtual void *touch()\n    {\n        return &registrator_;\n    }\n};\n\ntemplate <typename T, bool AutoCreation>\ntypename HttpSimpleController<T, AutoCreation>::pathRegistrator\n    HttpSimpleController<T, AutoCreation>::registrator_;\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/HttpTypes.h",
    "content": "/**\n *  @file HttpTypes.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <atomic>\n#include <thread>\n#include <iostream>\n#include <string_view>\n#include <trantor/utils/LogStream.h>\n#include <drogon/utils/Utilities.h>\n\nnamespace drogon\n{\nenum HttpStatusCode\n{\n    kUnknown = 0,\n    k100Continue = 100,\n    k101SwitchingProtocols = 101,\n    k102Processing = 102,\n    k103EarlyHints = 103,\n    k200OK = 200,\n    k201Created = 201,\n    k202Accepted = 202,\n    k203NonAuthoritativeInformation = 203,\n    k204NoContent = 204,\n    k205ResetContent = 205,\n    k206PartialContent = 206,\n    k207MultiStatus = 207,\n    k208AlreadyReported = 208,\n    k226IMUsed = 226,\n    k300MultipleChoices = 300,\n    k301MovedPermanently = 301,\n    k302Found = 302,\n    k303SeeOther = 303,\n    k304NotModified = 304,\n    k305UseProxy = 305,\n    k306Unused = 306,\n    k307TemporaryRedirect = 307,\n    k308PermanentRedirect = 308,\n    k400BadRequest = 400,\n    k401Unauthorized = 401,\n    k402PaymentRequired = 402,\n    k403Forbidden = 403,\n    k404NotFound = 404,\n    k405MethodNotAllowed = 405,\n    k406NotAcceptable = 406,\n    k407ProxyAuthenticationRequired = 407,\n    k408RequestTimeout = 408,\n    k409Conflict = 409,\n    k410Gone = 410,\n    k411LengthRequired = 411,\n    k412PreconditionFailed = 412,\n    k413RequestEntityTooLarge = 413,\n    k414RequestURITooLarge = 414,\n    k415UnsupportedMediaType = 415,\n    k416RequestedRangeNotSatisfiable = 416,\n    k417ExpectationFailed = 417,\n    k418ImATeapot = 418,\n    k421MisdirectedRequest = 421,\n    k422UnprocessableEntity = 422,\n    k423Locked = 423,\n    k424FailedDependency = 424,\n    k425TooEarly = 425,\n    k426UpgradeRequired = 426,\n    k428PreconditionRequired = 428,\n    k429TooManyRequests = 429,\n    k431RequestHeaderFieldsTooLarge = 431,\n    k451UnavailableForLegalReasons = 451,\n    k500InternalServerError = 500,\n    k501NotImplemented = 501,\n    k502BadGateway = 502,\n    k503ServiceUnavailable = 503,\n    k504GatewayTimeout = 504,\n    k505HTTPVersionNotSupported = 505,\n    k506VariantAlsoNegotiates = 506,\n    k507InsufficientStorage = 507,\n    k508LoopDetected = 508,\n    k510NotExtended = 510,\n    k511NetworkAuthenticationRequired = 511\n};\n\nenum class Version\n{\n    kUnknown = 0,\n    kHttp10,\n    kHttp11\n};\n\nenum ContentType\n{\n    CT_NONE = 0,\n    CT_APPLICATION_JSON,\n    CT_TEXT_PLAIN,\n    CT_TEXT_HTML,\n    CT_APPLICATION_X_FORM,\n    CT_APPLICATION_X_JAVASCRIPT [[deprecated(\"use CT_TEXT_JAVASCRIPT\")]],\n    CT_TEXT_JAVASCRIPT,\n    CT_TEXT_CSS,\n    CT_TEXT_CSV,\n    CT_TEXT_XML,         // suggests human readable xml\n    CT_APPLICATION_XML,  // suggest machine-to-machine xml\n    CT_TEXT_XSL,\n    CT_APPLICATION_WASM,\n    CT_APPLICATION_OCTET_STREAM,\n    CT_APPLICATION_FONT_WOFF,\n    CT_APPLICATION_FONT_WOFF2,\n    CT_APPLICATION_GZIP,\n    CT_APPLICATION_JAVA_ARCHIVE,\n    CT_APPLICATION_PDF,\n    CT_APPLICATION_MSWORD,\n    CT_APPLICATION_MSWORDX,\n    CT_APPLICATION_VND_MS_FONTOBJ,\n    CT_APPLICATION_VND_RAR,\n    CT_APPLICATION_XHTML,\n    CT_APPLICATION_X_7Z,\n    CT_APPLICATION_X_BZIP,\n    CT_APPLICATION_X_BZIP2,\n    CT_APPLICATION_X_HTTPD_PHP,\n    CT_APPLICATION_X_FONT_TRUETYPE,\n    CT_APPLICATION_X_FONT_OPENTYPE,\n    CT_APPLICATION_X_TAR,\n    CT_APPLICATION_X_TGZ,\n    CT_APPLICATION_X_XZ,\n    CT_APPLICATION_ZIP,\n    CT_AUDIO_AAC,\n    CT_AUDIO_AC3,\n    CT_AUDIO_AIFF,\n    CT_AUDIO_FLAC,\n    CT_AUDIO_MATROSKA,\n    CT_AUDIO_MPEG,\n    CT_AUDIO_MPEG4,\n    CT_AUDIO_OGG,\n    CT_AUDIO_WAVE,\n    CT_AUDIO_WEBM,\n    CT_AUDIO_X_APE,\n    CT_AUDIO_X_MS_WMA,\n    CT_AUDIO_X_TTA,\n    CT_AUDIO_X_WAVPACK,\n    CT_IMAGE_APNG,\n    CT_IMAGE_AVIF,\n    CT_IMAGE_BMP,\n    CT_IMAGE_GIF,\n    CT_IMAGE_ICNS,\n    CT_IMAGE_JPG,\n    CT_IMAGE_JP2,\n    CT_IMAGE_PNG,\n    CT_IMAGE_SVG_XML,\n    CT_IMAGE_TIFF,\n    CT_IMAGE_WEBP,\n    CT_IMAGE_X_MNG,\n    CT_IMAGE_X_TGA,\n    CT_IMAGE_XICON,\n    CT_VIDEO_APG,\n    CT_VIDEO_AV1,\n    CT_VIDEO_QUICKTIME,\n    CT_VIDEO_MATROSKA,\n    CT_VIDEO_MP4,\n    CT_VIDEO_MPEG,\n    CT_VIDEO_MPEG2TS,\n    CT_VIDEO_OGG,\n    CT_VIDEO_WEBM,\n    CT_VIDEO_X_M4V,\n    CT_VIDEO_X_MSVIDEO,\n    CT_MULTIPART_FORM_DATA,\n    CT_CUSTOM\n};\n\nenum FileType\n{\n    FT_UNKNOWN = 0,\n    FT_CUSTOM,\n    FT_DOCUMENT,\n    FT_ARCHIVE,\n    FT_AUDIO,\n    FT_MEDIA,\n    FT_IMAGE\n};\n\nenum HttpMethod\n{\n    Get = 0,\n    Post,\n    Head,\n    Put,\n    Delete,\n    Options,\n    Patch,\n    Invalid\n};\n\nenum class ReqResult\n{\n    Ok = 0,\n    BadResponse,\n    NetworkFailure,\n    BadServerAddress,\n    Timeout,\n    HandshakeError,\n    InvalidCertificate,\n    EncryptionFailure,\n};\n\nenum class WebSocketMessageType\n{\n    Text = 0,\n    Binary,\n    Ping,\n    Pong,\n    Close,\n    Unknown\n};\n\ninline std::string_view to_string_view(drogon::ReqResult result)\n{\n    switch (result)\n    {\n        case ReqResult::Ok:\n            return \"OK\";\n        case ReqResult::BadResponse:\n            return \"Bad response from server\";\n        case ReqResult::NetworkFailure:\n            return \"Network failure\";\n        case ReqResult::BadServerAddress:\n            return \"Bad server address\";\n        case ReqResult::Timeout:\n            return \"Timeout\";\n        case ReqResult::HandshakeError:\n            return \"Handshake error\";\n        case ReqResult::InvalidCertificate:\n            return \"Invalid certificate\";\n        case ReqResult::EncryptionFailure:\n            return \"Unrecoverable encryption failure\";\n        default:\n            return \"Unknown error\";\n    }\n}\n\ninline std::string to_string(drogon::ReqResult result)\n{\n    auto sv = to_string_view(result);\n    return std::string(sv.data(), sv.size());\n}\n\ninline std::ostream &operator<<(std::ostream &out, drogon::ReqResult result)\n{\n    return out << to_string_view(result);\n}\n\ninline trantor::LogStream &operator<<(trantor::LogStream &out,\n                                      drogon::ReqResult result)\n{\n    return out << to_string_view(result);\n}\n\ninline std::string_view to_string_view(drogon::HttpMethod method)\n{\n    switch (method)\n    {\n        case drogon::HttpMethod::Get:\n            return \"GET\";\n        case drogon::HttpMethod::Post:\n            return \"POST\";\n        case drogon::HttpMethod::Head:\n            return \"HEAD\";\n        case drogon::HttpMethod::Put:\n            return \"PUT\";\n        case drogon::HttpMethod::Delete:\n            return \"DELETE\";\n        case drogon::HttpMethod::Options:\n            return \"OPTIONS\";\n        case drogon::HttpMethod::Patch:\n            return \"PATCH\";\n        default:\n            return \"INVALID\";\n    }\n}\n\ninline std::string to_string(drogon::HttpMethod method)\n{\n    auto sv = to_string_view(method);\n    return std::string(sv.data(), sv.size());\n}\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/HttpViewData.h",
    "content": "/**\n *\n *  @file HttpViewData.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <trantor/utils/Logger.h>\n#include <trantor/utils/MsgBuffer.h>\n#include <sstream>\n#include <string>\n#include <unordered_map>\n#include <stdarg.h>\n#include <stdio.h>\n#include <type_traits>\n#include <any>\n#include <string_view>\n\nnamespace drogon\n{\n/// This class represents the data set displayed in views.\nclass DROGON_EXPORT HttpViewData\n{\n  public:\n    /// The function template is used to get an item in the data set by the key\n    /// parameter.\n    template <typename T>\n    const T &get(const std::string &key) const\n    {\n        static const T nullVal = T();\n        auto it = viewData_.find(key);\n        if (it != viewData_.end())\n        {\n            if (typeid(T) == it->second.type())\n            {\n                return *(std::any_cast<T>(&(it->second)));\n            }\n            else\n            {\n                LOG_ERROR << \"Bad type\";\n            }\n        }\n        return nullVal;\n    }\n\n    /// Insert an item identified by the key parameter into the data set;\n    void insert(const std::string &key, std::any &&obj)\n    {\n        viewData_[key] = std::move(obj);\n    }\n\n    void insert(const std::string &key, const std::any &obj)\n    {\n        viewData_[key] = obj;\n    }\n\n    /// Insert an item identified by the key parameter into the data set; The\n    /// item is converted to a string.\n    template <typename T>\n    void insertAsString(const std::string &key, T &&val)\n    {\n        std::stringstream ss;\n        ss << val;\n        viewData_[key] = ss.str();\n    }\n\n    /// Insert a formatted string identified by the key parameter.\n    void insertFormattedString(const std::string &key, const char *format, ...)\n    {\n        std::string strBuffer;\n        strBuffer.resize(128);\n        va_list ap, backup_ap;\n        va_start(ap, format);\n        va_copy(backup_ap, ap);\n        auto result = vsnprintf((char *)strBuffer.data(),\n                                strBuffer.size(),\n                                format,\n                                backup_ap);\n        va_end(backup_ap);\n        if ((result >= 0) &&\n            (static_cast<std::string::size_type>(result) < strBuffer.size()))\n        {\n            strBuffer.resize(static_cast<std::string::size_type>(result));\n        }\n        else\n        {\n            while (true)\n            {\n                if (result < 0)\n                {\n                    // Older snprintf() behavior. Just try doubling the buffer\n                    // size\n                    strBuffer.resize(strBuffer.size() * 2);\n                }\n                else\n                {\n                    strBuffer.resize(result + 1);\n                }\n\n                va_copy(backup_ap, ap);\n                result = vsnprintf((char *)strBuffer.data(),\n                                   strBuffer.size(),\n                                   format,\n                                   backup_ap);\n                va_end(backup_ap);\n\n                if ((result >= 0) &&\n                    ((std::string::size_type)result < strBuffer.size()))\n                {\n                    strBuffer.resize(result);\n                    break;\n                }\n            }\n        }\n        va_end(ap);\n        viewData_[key] = std::move(strBuffer);\n    }\n\n    /// Get the 'any' object by the key parameter.\n    std::any &operator[](const std::string &key) const\n    {\n        return viewData_[key];\n    }\n\n    /// Translate some special characters to HTML format\n    /**\n     * such as:\n     * @code\n       \" --> &quot;\n       & --> &amp;\n       < --> &lt;\n       > --> &gt;\n       @endcode\n     */\n    static std::string htmlTranslate(const char *str, size_t length);\n\n    static std::string htmlTranslate(const std::string_view &str)\n    {\n        return htmlTranslate(str.data(), str.length());\n    }\n\n    static bool needTranslation(const std::string_view &str)\n    {\n        for (auto const &c : str)\n        {\n            switch (c)\n            {\n                case '\"':\n                case '&':\n                case '<':\n                case '>':\n                    return true;\n                default:\n                    continue;\n            }\n        }\n        return false;\n    }\n\n  protected:\n    using ViewDataMap = std::unordered_map<std::string, std::any>;\n    mutable ViewDataMap viewData_;\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/IOThreadStorage.h",
    "content": "/**\n *\n *  @file IOThreadStorage.h\n *  @author Daniel Mensinger\n *\n *  Copyright 2019, Daniel Mensinger.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/HttpAppFramework.h>\n#include <trantor/utils/NonCopyable.h>\n#include <memory>\n#include <vector>\n#include <limits>\n#include <functional>\n\nnamespace drogon\n{\n/**\n * @brief Utility class for thread storage handling\n *\n * Thread storage allows the efficient handling of reusable data without thread\n * synchronisation. For instance, such a thread storage would be useful to store\n * database connections.\n *\n * Example usage:\n *\n * @code\n * struct MyThreadData {\n *     int threadLocal = 42;\n *     std::string something = \"foo\";\n * };\n *\n * class MyController : public HttpController<MyController> {\n *   public:\n *      METHOD_LIST_BEGIN\n *      ADD_METHOD_TO(MyController::endpoint, \"/some/path\", Get);\n *      METHOD_LIST_END\n *\n *      void login(const HttpRequestPtr &req,\n *                 std::function<void (const HttpResponsePtr &)> &&callback) {\n *          assert(storage_->threadLocal == 42);\n *\n *          // handle the request\n *      }\n *\n *    private:\n *      IOThreadStorage<MyThreadData> storage_;\n * };\n * @endcode\n */\ntemplate <typename C>\nclass IOThreadStorage : public trantor::NonCopyable\n{\n  public:\n    using ValueType = C;\n    using InitCallback = std::function<void(ValueType &, size_t)>;\n\n    template <typename... Args>\n    IOThreadStorage(Args &&...args)\n    {\n        static_assert(std::is_constructible<C, Args &&...>::value,\n                      \"Unable to construct storage with given signature\");\n        size_t numThreads = app().getThreadNum();\n        assert(numThreads > 0 &&\n               numThreads != (std::numeric_limits<size_t>::max)());\n        // set the size to numThreads+1 to enable access to this in the main\n        // thread.\n        storage_.reserve(numThreads + 1);\n\n        for (size_t i = 0; i <= numThreads; ++i)\n        {\n            storage_.emplace_back(std::forward<Args>(args)...);\n        }\n    }\n\n    void init(const InitCallback &initCB)\n    {\n        for (size_t i = 0; i < storage_.size(); ++i)\n        {\n            initCB(storage_[i], i);\n        }\n    }\n\n    /**\n     * @brief Get the thread storage associate with the current thread\n     *\n     * This function may only be called in a request handler\n     */\n    inline ValueType &getThreadData()\n    {\n        size_t idx = app().getCurrentThreadIndex();\n        assert(idx < storage_.size());\n        return storage_[idx];\n    }\n\n    inline const ValueType &getThreadData() const\n    {\n        size_t idx = app().getCurrentThreadIndex();\n        assert(idx < storage_.size());\n        return storage_[idx];\n    }\n\n    /**\n     * @brief Sets the thread data for the current thread\n     *\n     * This function may only be called in a request handler\n     */\n    inline void setThreadData(const ValueType &newData)\n    {\n        size_t idx = app().getCurrentThreadIndex();\n        assert(idx < storage_.size());\n        storage_[idx] = newData;\n    }\n\n    inline void setThreadData(ValueType &&newData)\n    {\n        size_t idx = app().getCurrentThreadIndex();\n        assert(idx < storage_.size());\n        storage_[idx] = std::move(newData);\n    }\n\n    inline ValueType *operator->()\n    {\n        size_t idx = app().getCurrentThreadIndex();\n        assert(idx < storage_.size());\n        return &storage_[idx];\n    }\n\n    inline ValueType &operator*()\n    {\n        return getThreadData();\n    }\n\n    inline const ValueType *operator->() const\n    {\n        size_t idx = app().getCurrentThreadIndex();\n        assert(idx < storage_.size());\n        return &storage_[idx];\n    }\n\n    inline const ValueType &operator*() const\n    {\n        return getThreadData();\n    }\n\n  private:\n    std::vector<ValueType> storage_;\n};\n\ninline trantor::EventLoop *getIOThreadStorageLoop(size_t index) noexcept(false)\n{\n    if (index > drogon::app().getThreadNum())\n    {\n        throw std::out_of_range(\"Event loop index is out of range\");\n    }\n    if (index == drogon::app().getThreadNum())\n        return drogon::app().getLoop();\n    return drogon::app().getIOLoop(index);\n}\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/IntranetIpFilter.h",
    "content": "/**\n *\n *  @file IntranetIpFilter.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <drogon/HttpFilter.h>\n\nnamespace drogon\n{\n/**\n * @brief A filter that prohibit access from external networks\n */\nclass DROGON_EXPORT IntranetIpFilter : public HttpFilter<IntranetIpFilter>\n{\n  public:\n    IntranetIpFilter()\n    {\n    }\n\n    void doFilter(const HttpRequestPtr &req,\n                  FilterCallback &&fcb,\n                  FilterChainCallback &&fccb) override;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/LocalHostFilter.h",
    "content": "/**\n *\n *  @file LocalHostFilter.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <drogon/HttpFilter.h>\n\nnamespace drogon\n{\n/**\n * @brief A filter that prohibit access from other hosts.\n */\nclass DROGON_EXPORT LocalHostFilter : public HttpFilter<LocalHostFilter>\n{\n  public:\n    LocalHostFilter()\n    {\n    }\n\n    void doFilter(const HttpRequestPtr &req,\n                  FilterCallback &&fcb,\n                  FilterChainCallback &&fccb) override;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/MultiPart.h",
    "content": "/**\n *\n *  @file MultiPart.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"drogon/utils/Utilities.h\"\n#include <drogon/exports.h>\n#include <drogon/HttpRequest.h>\n#include <unordered_map>\n#include <string>\n#include <vector>\n#include <memory>\n#include <string_view>\n\nnamespace drogon\n{\nclass HttpFileImpl;\n\n/**\n * @brief This class represents a uploaded file by a HTTP request.\n *\n */\nclass DROGON_EXPORT HttpFile\n{\n  public:\n    explicit HttpFile(std::shared_ptr<HttpFileImpl> &&implPtr) noexcept;\n    /// Return the file name;\n    const std::string &getFileName() const noexcept;\n\n    /// Return the file extension;\n    /// Note: After the HttpFile object is destroyed, do not use this\n    /// std::string_view object.\n    std::string_view getFileExtension() const noexcept;\n\n    /// Return the name of the item in multiple parts.\n    const std::string &getItemName() const noexcept;\n\n    /// Return the type of file.\n    FileType getFileType() const noexcept;\n\n    /// Set the file name, usually called by the MultiPartParser parser.\n    void setFileName(const std::string &fileName) noexcept;\n\n    /// Set the contents of the file, usually called by the MultiPartParser\n    /// parser.\n    void setFile(const char *data, size_t length) noexcept;\n\n    /// Save the file to the file system.\n    /**\n     * The folder saving the file is app().getUploadPath().\n     * The full path is app().getUploadPath()+\"/\"+this->getFileName()\n     */\n    int save() const noexcept;\n\n    /// Save the file to @p path\n    /**\n     * @param path if the parameter is prefixed with \"/\", \"./\" or \"../\", or is\n     * \".\" or \"..\", the full path is path+\"/\"+this->getFileName(),\n     * otherwise the file is saved as\n     * app().getUploadPath()+\"/\"+path+\"/\"+this->getFileName()\n     */\n    int save(const std::string &path) const noexcept;\n\n    /// Save the file to file system with a new name\n    /**\n     * @param fileName if the parameter isn't prefixed with \"/\", \"./\" or \"../\",\n     * the full path is app().getUploadPath()+\"/\"+filename, otherwise the file\n     * is saved as the filename\n     */\n    int saveAs(const std::string &fileName) const noexcept;\n\n    /**\n     * @brief return the content of the file.\n     *\n     * @return std::string_view\n     */\n    std::string_view fileContent() const noexcept\n    {\n        return std::string_view{fileData(), fileLength()};\n    }\n\n    /// Return the file length.\n    size_t fileLength() const noexcept;\n\n    /// Return the content-type of the file.\n    drogon::ContentType getContentType() const noexcept;\n    /**\n     * @brief return the pointer of the file data.\n     *\n     * @return const char*\n     * @note This function just returns the beginning of the file data in\n     * memory. Users mustn't assume that there is an \\0 character at the end of\n     * the file data even if the type of the file is text. One should get the\n     * length of the file by the fileLength() method, or use the fileContent()\n     * method.\n     */\n    const char *fileData() const noexcept;\n\n    /// Return the md5 string of the file\n    std::string getMd5() const noexcept;\n\n    /// Return the content transfer encoding of the file.\n    const std::string &getContentTransferEncoding() const noexcept;\n\n  private:\n    std::shared_ptr<HttpFileImpl> implPtr_;\n};\n\n/// A parser class which help the user to get the files and the parameters in\n/// the multipart format request.\nclass DROGON_EXPORT MultiPartParser\n{\n  public:\n    MultiPartParser(){};\n    MultiPartParser(const MultiPartParser &other) = default;  // Copyable\n    MultiPartParser(MultiPartParser &&other) = default;       // Movable\n    ~MultiPartParser(){};\n    /// Get files, This method should be called after calling the parse()\n    /// method.\n    const std::vector<HttpFile> &getFiles() const;\n\n    /// Get files in a map, the keys of the map are item names of the files.\n    std::unordered_map<std::string, HttpFile> getFilesMap() const;\n\n    /// Get parameters, This method should be called after calling the parse ()\n    /// method.\n    const SafeStringMap<std::string> &getParameters() const;\n\n    /// Get the value of an optional parameter\n    /// This method should be called after calling the parse() method.\n    template <typename T>\n    std::optional<T> getOptionalParameter(const std::string &key)\n    {\n        auto &params = getParameters();\n        auto it = params.find(key);\n        if (it != params.end())\n        {\n            try\n            {\n                return std::optional<T>(utils::fromString<T>(it->second));\n            }\n            catch (const std::exception &e)\n            {\n                LOG_ERROR << e.what();\n                return std::optional<T>{};\n            }\n        }\n        else\n        {\n            return std::optional<T>{};\n        }\n    }\n\n    /// Get the value of a parameter\n    /// This method should be called after calling the parse() method.\n    /// Note: returns a default T object if the parameter is missing\n    template <typename T>\n    T getParameter(const std::string &key)\n    {\n        return getOptionalParameter<T>(key).value_or(T{});\n    }\n\n    /// Parse the http request stream to get files and parameters.\n    int parse(const HttpRequestPtr &req);\n\n  protected:\n    std::vector<HttpFile> files_;\n    SafeStringMap<std::string> parameters_;\n    int parse(const HttpRequestPtr &req,\n              const char *boundaryData,\n              size_t boundaryLen);\n    int parseEntity(const HttpRequestPtr &req,\n                    const char *begin,\n                    const char *end);\n};\n\n/// In order to be compatible with old interfaces\nusing FileUpload = MultiPartParser;\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/NotFound.h",
    "content": "// this file is generated by program automatically,don't modify it!\n\n/**\n *\n *  @file NotFound.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include <drogon/exports.h>\n#include <drogon/DrTemplate.h>\n\nnamespace drogon\n{\n/**\n * @brief This class is used by the drogon to generate the 404 page. Users don't\n * use this class directly.\n */\nclass DROGON_EXPORT NotFound final : public drogon::DrTemplate<NotFound>\n{\n  public:\n    NotFound()\n    {\n    }\n\n    std::string genText(const drogon::HttpViewData &) override;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/PubSubService.h",
    "content": "/**\n *\n *  @file PubSubService.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <trantor/utils/NonCopyable.h>\n#include <functional>\n#include <mutex>\n#include <shared_mutex>\n#include <string>\n#include <memory>\n#include <unordered_map>\n\nnamespace drogon\n{\nusing SubscriberID = uint64_t;\n\n/**\n * @brief This class template presents an unnamed topic.\n *\n * @tparam MessageType\n */\ntemplate <typename MessageType>\nclass Topic : public trantor::NonCopyable\n{\n  public:\n    using MessageHandler = std::function<void(const MessageType &)>;\n#if __cplusplus >= 201703L | defined _WIN32\n    using SharedMutex = std::shared_mutex;\n#else\n    using SharedMutex = std::shared_timed_mutex;\n#endif\n    /**\n     * @brief Publish a message, every subscriber in the topic will receive the\n     * message.\n     *\n     * @param message\n     */\n    void publish(const MessageType &message) const\n    {\n        std::shared_lock<SharedMutex> lock(mutex_);\n        for (auto &pair : handlersMap_)\n        {\n            pair.second(message);\n        }\n    }\n\n    /**\n     * @brief Subscribe to the topic.\n     *\n     * @param handler is invoked when a message arrives.\n     * @return SubscriberID\n     */\n    SubscriberID subscribe(const MessageHandler &handler)\n    {\n        std::unique_lock<SharedMutex> lock(mutex_);\n        handlersMap_[++id_] = handler;\n        return id_;\n    }\n\n    /**\n     * @brief Subscribe to the topic.\n     *\n     * @param handler is invoked when a message arrives.\n     * @return SubscriberID\n     */\n    SubscriberID subscribe(MessageHandler &&handler)\n    {\n        std::unique_lock<SharedMutex> lock(mutex_);\n        handlersMap_[++id_] = std::move(handler);\n        return id_;\n    }\n\n    /**\n     * @brief Unsubscribe from the topic.\n     */\n    void unsubscribe(SubscriberID id)\n    {\n        std::unique_lock<SharedMutex> lock(mutex_);\n        handlersMap_.erase(id);\n    }\n\n    /**\n     * @brief Check if the topic is empty.\n     *\n     * @return true means there are no subscribers.\n     * @return false means there are subscribers in the topic.\n     */\n    bool empty() const\n    {\n        std::shared_lock<SharedMutex> lock(mutex_);\n        return handlersMap_.empty();\n    }\n\n    /**\n     * @brief Remove all subscribers from the topic.\n     *\n     */\n    void clear()\n    {\n        std::unique_lock<SharedMutex> lock(mutex_);\n        handlersMap_.clear();\n    }\n\n  private:\n    std::unordered_map<SubscriberID, MessageHandler> handlersMap_;\n    mutable SharedMutex mutex_;\n    SubscriberID id_{0};\n};\n\n/**\n * @brief This class template implements a publish-subscribe pattern with\n * multiple named topics.\n *\n * @tparam MessageType The message type.\n */\ntemplate <typename MessageType>\nclass PubSubService : public trantor::NonCopyable\n{\n  public:\n    using MessageHandler =\n        std::function<void(const std::string &, const MessageType &)>;\n#if __cplusplus >= 201703L | defined _WIN32\n    using SharedMutex = std::shared_mutex;\n#else\n    using SharedMutex = std::shared_timed_mutex;\n#endif\n\n    /**\n     * @brief Publish a message to a topic. The message will be broadcasted to\n     * every subscriber.\n     */\n    void publish(const std::string &topicName, const MessageType &message) const\n    {\n        std::shared_ptr<Topic<MessageType>> topicPtr;\n        {\n            std::shared_lock<SharedMutex> lock(mutex_);\n            auto iter = topicMap_.find(topicName);\n            if (iter != topicMap_.end())\n            {\n                topicPtr = iter->second;\n            }\n            else\n            {\n                return;\n            }\n        }\n        topicPtr->publish(message);\n    }\n\n    /**\n     * @brief Subscribe to a topic. When a message is published to the topic,\n     * the handler is invoked by passing the topic and message as parameters.\n     */\n    SubscriberID subscribe(const std::string &topicName,\n                           const MessageHandler &handler)\n    {\n        auto topicHandler = [topicName, handler](const MessageType &message) {\n            handler(topicName, message);\n        };\n        return subscribeToTopic(topicName, std::move(topicHandler));\n    }\n\n    /**\n     * @brief Subscribe to a topic. When a message is published to the topic,\n     * the handler is invoked by passing the topic and message as parameters.\n     * @param topicName Topic name.\n     * @param handler The message handler.\n     * @return The subscriber ID.\n     */\n    SubscriberID subscribe(const std::string &topicName,\n                           MessageHandler &&handler)\n    {\n        auto topicHandler = [topicName, handler = std::move(handler)](\n                                const MessageType &message) {\n            handler(topicName, message);\n        };\n        return subscribeToTopic(topicName, std::move(topicHandler));\n    }\n\n    /**\n     * @brief Unsubscribe from a topic.\n     *\n     * @param topicName Topic name.\n     * @param id The subscriber ID returned from the subscribe method.\n     */\n    void unsubscribe(const std::string &topicName, SubscriberID id)\n    {\n        {\n            std::shared_lock<SharedMutex> lock(mutex_);\n            auto iter = topicMap_.find(topicName);\n            if (iter == topicMap_.end())\n            {\n                return;\n            }\n            iter->second->unsubscribe(id);\n            if (!iter->second->empty())\n                return;\n        }\n        std::unique_lock<SharedMutex> lock(mutex_);\n        auto iter = topicMap_.find(topicName);\n        if (iter == topicMap_.end())\n        {\n            return;\n        }\n        if (iter->second->empty())\n            topicMap_.erase(iter);\n    }\n\n    /**\n     * @brief return the number of topics.\n     */\n    size_t size() const\n    {\n        std::shared_lock<SharedMutex> lock(mutex_);\n        return topicMap_.size();\n    }\n\n    /**\n     * @brief remove all topics.\n     */\n    void clear()\n    {\n        std::unique_lock<SharedMutex> lock(mutex_);\n        topicMap_.clear();\n    }\n\n    /**\n     * @brief Remove a topic\n     *\n     */\n    void removeTopic(const std::string &topicName)\n    {\n        std::unique_lock<SharedMutex> lock(mutex_);\n        topicMap_.erase(topicName);\n    }\n\n    /**\n     * @brief Check if a topic is empty.\n     *\n     * @param topicName The topic name.\n     * @return true means there are no subscribers.\n     * @return false means there are subscribers in the topic.\n     */\n    bool isTopicEmpty(const std::string &topicName) const\n    {\n        std::shared_ptr<Topic<MessageType>> topicPtr;\n        {\n            std::shared_lock<SharedMutex> lock(mutex_);\n            auto iter = topicMap_.find(topicName);\n            if (iter != topicMap_.end())\n            {\n                topicPtr = iter->second;\n            }\n            else\n            {\n                return true;\n            }\n        }\n        return topicPtr->empty();\n    }\n\n  private:\n    std::unordered_map<std::string, std::shared_ptr<Topic<MessageType>>>\n        topicMap_;\n    mutable SharedMutex mutex_;\n    SubscriberID subID_ = 0;\n\n    SubscriberID subscribeToTopic(\n        const std::string &topicName,\n        typename Topic<MessageType>::MessageHandler &&handler)\n    {\n        {\n            std::shared_lock<SharedMutex> lock(mutex_);\n            auto iter = topicMap_.find(topicName);\n            if (iter != topicMap_.end())\n            {\n                return iter->second->subscribe(std::move(handler));\n            }\n        }\n        std::unique_lock<SharedMutex> lock(mutex_);\n        auto iter = topicMap_.find(topicName);\n        if (iter != topicMap_.end())\n        {\n            return iter->second->subscribe(std::move(handler));\n        }\n        auto topicPtr = std::make_shared<Topic<MessageType>>();\n        auto id = topicPtr->subscribe(std::move(handler));\n        topicMap_[topicName] = std::move(topicPtr);\n        return id;\n    }\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/RateLimiter.h",
    "content": "#pragma once\n#include <drogon/exports.h>\n#include <memory>\n#include <chrono>\n#include <mutex>\n#include <string>\n\nnamespace drogon\n{\nenum class DROGON_EXPORT RateLimiterType\n{\n    kFixedWindow,\n    kSlidingWindow,\n    kTokenBucket\n};\n\ninline RateLimiterType stringToRateLimiterType(const std::string &type)\n{\n    if (type == \"fixedWindow\" || type == \"fixed_window\")\n        return RateLimiterType::kFixedWindow;\n    else if (type == \"slidingWindow\" || type == \"sliding_window\")\n        return RateLimiterType::kSlidingWindow;\n    return RateLimiterType::kTokenBucket;\n}\nclass DROGON_EXPORT RateLimiter;\nusing RateLimiterPtr = std::shared_ptr<RateLimiter>;\n\n/**\n * @brief This class is used to limit the number of requests per second\n *\n * */\nclass DROGON_EXPORT RateLimiter\n{\n  public:\n    /**\n     * @brief Create a rate limiter\n     * @param type The type of the rate limiter\n     * @param capacity The maximum number of requests in the time unit.\n     * @param timeUnit The time unit of the rate limiter.\n     * @return A rate limiter pointer\n     */\n    static RateLimiterPtr newRateLimiter(\n        RateLimiterType type,\n        size_t capacity,\n        std::chrono::duration<double> timeUnit = std::chrono::seconds(60));\n    /**\n     * @brief Check if a request is allowed\n     *\n     * @return true The request is allowed\n     * @return false The request is not allowed\n     */\n    virtual bool isAllowed() = 0;\n    virtual ~RateLimiter() noexcept = default;\n};\n\nclass DROGON_EXPORT SafeRateLimiter : public RateLimiter\n{\n  public:\n    SafeRateLimiter(RateLimiterPtr limiter) : limiter_(limiter)\n    {\n    }\n\n    bool isAllowed() override\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        return limiter_->isAllowed();\n    }\n\n    ~SafeRateLimiter() noexcept override = default;\n\n  private:\n    RateLimiterPtr limiter_;\n    std::mutex mutex_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/RequestStream.h",
    "content": "/**\n *\n *  @file RequestStream.h\n *  @author Nitromelon\n *\n *  Copyright 2024, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n#include <drogon/exports.h>\n#include <string>\n#include <functional>\n#include <memory>\n#include <exception>\n\nnamespace drogon\n{\nclass HttpRequest;\nusing HttpRequestPtr = std::shared_ptr<HttpRequest>;\n\nclass RequestStreamReader;\nusing RequestStreamReaderPtr = std::shared_ptr<RequestStreamReader>;\n\nstruct MultipartHeader\n{\n    std::string name;\n    std::string filename;\n    std::string contentType;\n};\n\nclass DROGON_EXPORT RequestStream\n{\n  public:\n    virtual ~RequestStream() = default;\n    virtual void setStreamReader(RequestStreamReaderPtr reader) = 0;\n};\n\nusing RequestStreamPtr = std::shared_ptr<RequestStream>;\n\nnamespace internal\n{\nDROGON_EXPORT RequestStreamPtr createRequestStream(const HttpRequestPtr &req);\n}\n\nenum class StreamErrorCode\n{\n    kNone = 0,\n    kBadRequest,\n    kConnectionBroken\n};\n\nclass StreamError final : public std::exception\n{\n  public:\n    const char *what() const noexcept override\n    {\n        return message_.data();\n    }\n\n    StreamErrorCode code() const\n    {\n        return code_;\n    }\n\n    StreamError(StreamErrorCode code, const std::string &message)\n        : message_(message), code_(code)\n    {\n    }\n\n    StreamError(StreamErrorCode code, std::string &&message)\n        : message_(std::move(message)), code_(code)\n    {\n    }\n\n    StreamError() = delete;\n\n  private:\n    std::string message_;\n    StreamErrorCode code_;\n};\n\n/**\n * An interface for stream request reading.\n * User should create an implementation class, or use built-in handlers\n */\nclass DROGON_EXPORT RequestStreamReader\n{\n  public:\n    virtual ~RequestStreamReader() = default;\n    virtual void onStreamData(const char *, size_t) = 0;\n    virtual void onStreamFinish(std::exception_ptr) = 0;\n\n    using StreamDataCallback = std::function<void(const char *, size_t)>;\n    using StreamFinishCallback = std::function<void(std::exception_ptr)>;\n\n    // Create a handler with default implementation\n    static RequestStreamReaderPtr newReader(StreamDataCallback dataCb,\n                                            StreamFinishCallback finishCb);\n\n    // A handler that drops all data\n    static RequestStreamReaderPtr newNullReader();\n\n    using MultipartHeaderCallback = std::function<void(MultipartHeader header)>;\n\n    static RequestStreamReaderPtr newMultipartReader(\n        const HttpRequestPtr &req,\n        MultipartHeaderCallback headerCb,\n        StreamDataCallback dataCb,\n        StreamFinishCallback finishCb);\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/Session.h",
    "content": "/**\n *\n *  @file Session.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <trantor/utils/Logger.h>\n#include <map>\n#include <memory>\n#include <mutex>\n#include <thread>\n#include <optional>\n#include <any>\n\nnamespace drogon\n{\n/**\n * @brief This class represents a session stored in the framework.\n * One can get or set any type of data to a session object.\n */\nclass Session\n{\n  public:\n    using SessionMap = std::map<std::string, std::any>;\n\n    /**\n     * @brief Get the data identified by the key parameter.\n     * @note if the data is not found, a default value is returned.\n     * For example:\n     * @code\n       auto userName = sessionPtr->get<std::string>(\"user name\");\n       @endcode\n     */\n    template <typename T>\n    T get(const std::string &key) const\n    {\n        {\n            std::lock_guard<std::mutex> lck(mutex_);\n            auto it = sessionMap_.find(key);\n            if (it != sessionMap_.end())\n            {\n                if (typeid(T) == it->second.type())\n                {\n                    return *(std::any_cast<T>(&(it->second)));\n                }\n                else\n                {\n                    LOG_ERROR << \"Bad type\";\n                }\n            }\n        }\n        return T();\n    }\n\n    /**\n     * @brief Get the data identified by the key parameter and return an\n     * optional object that wraps the data.\n     *\n     * @tparam T\n     * @param key\n     * @return optional<T>\n     */\n    template <typename T>\n    std::optional<T> getOptional(const std::string &key) const\n    {\n        {\n            std::lock_guard<std::mutex> lck(mutex_);\n            auto it = sessionMap_.find(key);\n            if (it != sessionMap_.end())\n            {\n                if (typeid(T) == it->second.type())\n                {\n                    return *(std::any_cast<T>(&(it->second)));\n                }\n                else\n                {\n                    LOG_ERROR << \"Bad type\";\n                }\n            }\n        }\n        return std::nullopt;\n    }\n\n    /**\n     * @brief Modify or visit the data identified by the key parameter.\n     *\n     * @tparam T the type of the data.\n     * @param key\n     * @param handler A callable that can modify or visit the data. The\n     * signature of the handler should be equivalent to 'void(T&)' or\n     * 'void(const T&)'\n     *\n     * @note This function is multiple-thread safe. if the data identified by\n     * the key doesn't exist, a new one is created and passed to the handler.\n     * The changing of the data is protected by the mutex of the session.\n     */\n    template <typename T, typename Callable>\n    void modify(const std::string &key, Callable &&handler)\n    {\n        std::lock_guard<std::mutex> lck(mutex_);\n        auto it = sessionMap_.find(key);\n        if (it != sessionMap_.end())\n        {\n            if (typeid(T) == it->second.type())\n            {\n                handler(*(std::any_cast<T>(&(it->second))));\n            }\n            else\n            {\n                LOG_ERROR << \"Bad type\";\n            }\n        }\n        else\n        {\n            auto item = T();\n            handler(item);\n            sessionMap_.insert(std::make_pair(key, std::any(std::move(item))));\n        }\n    }\n\n    /**\n     * @brief Modify or visit the session data.\n     *\n     * @tparam Callable: The signature of the callable should be equivalent to\n     * `void (Session::SessionMap &)` or `void (const Session::SessionMap &)`\n     * @param handler A callable that can modify the sessionMap_ inside the\n     * session.\n     * @note This function is multiple-thread safe.\n     */\n    template <typename Callable>\n    void modify(Callable &&handler)\n    {\n        std::lock_guard<std::mutex> lck(mutex_);\n        handler(sessionMap_);\n    }\n\n    /**\n     * @brief Insert a key-value pair\n     * @note here the any object can be created implicitly. for example\n     * @code\n       sessionPtr->insert(\"user name\", userNameString);\n       @endcode\n     * @note If the key already exists, the element is not inserted.\n     */\n    void insert(const std::string &key, const std::any &obj)\n    {\n        std::lock_guard<std::mutex> lck(mutex_);\n        sessionMap_.insert(std::make_pair(key, obj));\n    }\n\n    /**\n     * @brief Insert a key-value pair\n     * @note here the any object can be created implicitly. for example\n     * @code\n       sessionPtr->insert(\"user name\", userNameString);\n       @endcode\n     * @note If the key already exists, the element is not inserted.\n     */\n    void insert(const std::string &key, std::any &&obj)\n    {\n        std::lock_guard<std::mutex> lck(mutex_);\n        sessionMap_.insert(std::make_pair(key, std::move(obj)));\n    }\n\n    /**\n     * @brief Erase the data identified by the given key.\n     */\n    void erase(const std::string &key)\n    {\n        std::lock_guard<std::mutex> lck(mutex_);\n        sessionMap_.erase(key);\n    }\n\n    /**\n     * @brief Return true if the data identified by the key exists.\n     */\n    bool find(const std::string &key)\n    {\n        std::lock_guard<std::mutex> lck(mutex_);\n        if (sessionMap_.find(key) == sessionMap_.end())\n        {\n            return false;\n        }\n        return true;\n    }\n\n    /**\n     * @brief Clear all data in the session.\n     */\n    void clear()\n    {\n        std::lock_guard<std::mutex> lck(mutex_);\n        sessionMap_.clear();\n    }\n\n    /**\n     * @brief Get the session ID of the current session.\n     */\n    std::string sessionId() const\n    {\n        std::lock_guard<std::mutex> lck(mutex_);\n        return sessionId_;\n    }\n\n    /**\n     * @brief Let the framework create a new session ID for this session and set\n     * it to the client.\n     * @note This method does not change the session ID now.\n     */\n    void changeSessionIdToClient()\n    {\n        needToChange_ = true;\n        needToSet_ = true;\n    }\n\n    Session() = delete;\n\n  private:\n    SessionMap sessionMap_;\n    mutable std::mutex mutex_;\n    std::string sessionId_;\n    bool needToSet_{false};\n    bool needToChange_{false};\n    friend class SessionManager;\n    friend class HttpAppFrameworkImpl;\n\n    /**\n     * @brief Constructor, usually called by the framework\n     */\n    Session(const std::string &id, bool needToSet)\n        : sessionId_(id), needToSet_(needToSet)\n    {\n    }\n\n    /**\n     * @brief Change the state of the session, usually called by the framework\n     */\n    void hasSet()\n    {\n        needToSet_ = false;\n    }\n\n    /**\n     * @brief If the session ID needs to be changed.\n     *\n     */\n    bool needToChangeSessionId() const\n    {\n        return needToChange_;\n    }\n\n    /**\n     * @brief If the session ID needs to be set to the client through cookie,\n     * return true\n     */\n    bool needSetToClient() const\n    {\n        return needToSet_;\n    }\n\n    void setSessionId(const std::string &id)\n    {\n        std::lock_guard<std::mutex> lck(mutex_);\n        sessionId_ = id;\n        needToChange_ = false;\n    }\n};\n\nusing SessionPtr = std::shared_ptr<Session>;\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/UploadFile.h",
    "content": "/**\n *\n *  UploadFile.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <string>\n\nnamespace drogon\n{\n/**\n * This class represents an upload file which will be transferred to the server\n * via the multipart/form-data format\n */\nclass UploadFile\n{\n  public:\n    /// Constructor\n    /**\n     * @param filePath The file location on local host, including file name.\n     * @param fileName The file name provided to the server. If it is empty by\n     * default, the file name in the @p filePath is provided to the server.\n     * @param itemName The item name on the browser form.\n     * @param contentType The Mime content type for the part\n     */\n    explicit UploadFile(const std::string &filePath,\n                        const std::string &fileName = \"\",\n                        const std::string &itemName = \"file\",\n                        ContentType contentType = CT_NONE)\n        : path_(filePath), itemName_(itemName), contentType_(contentType)\n    {\n        if (!fileName.empty())\n        {\n            fileName_ = fileName;\n        }\n        else\n        {\n            auto pos = filePath.rfind('/');\n            if (pos != std::string::npos)\n            {\n                fileName_ = filePath.substr(pos + 1);\n            }\n            else\n            {\n                fileName_ = filePath;\n            }\n        }\n    }\n\n    const std::string &path() const\n    {\n        return path_;\n    }\n\n    const std::string &fileName() const\n    {\n        return fileName_;\n    }\n\n    const std::string &itemName() const\n    {\n        return itemName_;\n    }\n\n    ContentType contentType() const\n    {\n        return contentType_;\n    }\n\n  private:\n    std::string path_;\n    std::string fileName_;\n    std::string itemName_;\n    ContentType contentType_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/WebSocketClient.h",
    "content": "/**\n *\n *  @file WebSocketClient.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/HttpRequest.h>\n#include <drogon/HttpResponse.h>\n#include <drogon/WebSocketConnection.h>\n#include <drogon/HttpTypes.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/utils/coroutine.h>\n#endif\n#include <functional>\n#include <memory>\n#include <string>\n#include <trantor/net/EventLoop.h>\n\nnamespace drogon\n{\nclass WebSocketClient;\nusing WebSocketClientPtr = std::shared_ptr<WebSocketClient>;\nusing WebSocketRequestCallback = std::function<\n    void(ReqResult, const HttpResponsePtr &, const WebSocketClientPtr &)>;\n\n#ifdef __cpp_impl_coroutine\nnamespace internal\n{\nstruct [[nodiscard]] WebSocketConnectionAwaiter\n    : public CallbackAwaiter<HttpResponsePtr>\n{\n    WebSocketConnectionAwaiter(WebSocketClient *client, HttpRequestPtr req)\n        : client_(client), req_(std::move(req))\n    {\n    }\n\n    void await_suspend(std::coroutine_handle<> handle);\n\n  private:\n    WebSocketClient *client_;\n    HttpRequestPtr req_;\n};\n\n}  // namespace internal\n#endif\n\n/**\n * @brief WebSocket client abstract class\n *\n */\nclass DROGON_EXPORT WebSocketClient\n{\n  public:\n    /// Get the WebSocket connection that is typically used to send messages.\n    virtual WebSocketConnectionPtr getConnection() = 0;\n\n    /**\n     * @brief Set messages handler. When a message is received from the server,\n     * the callback is called.\n     *\n     * @param callback The function to call when a message is received.\n     */\n    virtual void setMessageHandler(\n        const std::function<void(std::string &&message,\n                                 const WebSocketClientPtr &,\n                                 const WebSocketMessageType &)> &callback) = 0;\n\n    /// Set the connection closing handler. When the connection is established\n    /// or closed, the @p callback is called with a bool parameter.\n    /**\n     * @brief Set the connection closing handler. When the websocket connection\n     * is closed, the  callback is called\n     *\n     * @param callback The function to call when the connection is closed.\n     */\n    virtual void setConnectionClosedHandler(\n        const std::function<void(const WebSocketClientPtr &)> &callback) = 0;\n\n    /// Connect to the server.\n    virtual void connectToServer(const HttpRequestPtr &request,\n                                 const WebSocketRequestCallback &callback) = 0;\n\n    /**\n     * @brief Set the client certificate used by the HTTP connection\n     *\n     * @param cert Path to the certificate\n     * @param key Path to the certificate's private key\n     * @note this method has no effect if the HTTP client is communicating via\n     * unencrypted HTTP\n     */\n    virtual void setCertPath(const std::string &cert,\n                             const std::string &key) = 0;\n\n    /**\n     * @brief Supplies command style options for `SSL_CONF_cmd`\n     *\n     * @param sslConfCmds options for SSL_CONF_cmd\n     * @note this method has no effect if the HTTP client is communicating via\n     * unencrypted HTTP\n     * @code\n       addSSLConfigs({{\"-dhparam\", \"/path/to/dhparam\"}, {\"-strict\", \"\"}});\n     * @endcode\n     */\n    virtual void addSSLConfigs(\n        const std::vector<std::pair<std::string, std::string>>\n            &sslConfCmds) = 0;\n\n#ifdef __cpp_impl_coroutine\n    /**\n     * @brief Set messages handler. When a message is received from the server,\n     * the callback is called.\n     *\n     * @param callback The function to call when a message is received.\n     */\n    void setAsyncMessageHandler(\n        const std::function<Task<>(std::string &&message,\n                                   const WebSocketClientPtr &,\n                                   const WebSocketMessageType &)> &callback)\n    {\n        setMessageHandler([callback](std::string &&message,\n                                     const WebSocketClientPtr &client,\n                                     const WebSocketMessageType &type) -> void {\n            [callback](std::string &&message,\n                       const WebSocketClientPtr client,\n                       const WebSocketMessageType type) -> AsyncTask {\n                co_await callback(std::move(message), client, type);\n            }(std::move(message), client, type);\n        });\n    }\n\n    /// Set the connection closing handler. When the connection is established\n    /// or closed, the @param callback is called with a bool parameter.\n\n    /**\n     * @brief Set the connection closing handler. When the websocket connection\n     * is closed, the  callback is called\n     *\n     * @param callback The function to call when the connection is closed.\n     */\n    void setAsyncConnectionClosedHandler(\n        const std::function<Task<>(const WebSocketClientPtr &)> &callback)\n    {\n        setConnectionClosedHandler(\n            [callback](const WebSocketClientPtr &client) {\n                [=]() -> AsyncTask { co_await callback(client); }();\n            });\n    }\n\n    /// Connect to the server.\n    internal::WebSocketConnectionAwaiter connectToServerCoro(\n        const HttpRequestPtr &request)\n    {\n        return internal::WebSocketConnectionAwaiter(this, request);\n    }\n#endif\n\n    /// Get the event loop of the client;\n    virtual trantor::EventLoop *getLoop() = 0;\n\n    /// Stop trying to connect to the server or close the connection.\n    virtual void stop() = 0;\n\n    /**\n     * @brief Create a websocket client using the given ip and port to connect\n     * to server.\n     *\n     * @param ip The ip address of the server.\n     * @param port The port of the server.\n     * @param useSSL If useSSL is set to true, the client connects to the server\n     * using SSL.\n     * @param loop If the loop parameter is set to nullptr, the client uses the\n     * HttpAppFramework's event loop, otherwise it runs in the loop identified\n     * by the parameter.\n     * @param useOldTLS If the parameter is set to true, the TLS1.0/1.1 are\n     * enabled for HTTPS.\n     * @param validateCert If the parameter is set to true, the client validates\n     * the server certificate when SSL handshaking.\n     * @return HttpClientPtr The smart pointer to the new client object.\n     * @return WebSocketClientPtr The smart pointer to the WebSocket client.\n     * @note The ip parameter support for both ipv4 and ipv6 address\n     */\n    static WebSocketClientPtr newWebSocketClient(\n        const std::string &ip,\n        uint16_t port,\n        bool useSSL = false,\n        trantor::EventLoop *loop = nullptr,\n        bool useOldTLS = false,\n        bool validateCert = true);\n\n    /// Create a websocket client using the given hostString to connect to\n    /// server\n    /**\n     * @param hostString must be prefixed by 'ws://' or 'wss://'\n     * Examples for hostString:\n     * @code\n       wss://www.google.com\n       ws://www.google.com\n       wss://127.0.0.1:8080/\n       ws://127.0.0.1\n       @endcode\n     * @param loop if the parameter is set to nullptr, the client uses the\n     * HttpAppFramework's main event loop, otherwise it runs in the loop\n     * identified by the parameter.\n     * @param useOldTLS If the parameter is set to true, the TLS1.0/1.1 are\n     * enabled for HTTPS.\n     * @param validateCert If the parameter is set to true, the client validates\n     * the server certificate when SSL handshaking.\n     * @note\n     * Don't add path and parameters in hostString, the request path and\n     * parameters should be set in HttpRequestPtr when calling the\n     * connectToServer() method.\n     *\n     */\n    static WebSocketClientPtr newWebSocketClient(\n        const std::string &hostString,\n        trantor::EventLoop *loop = nullptr,\n        bool useOldTLS = false,\n        bool validateCert = true);\n\n    virtual ~WebSocketClient() = default;\n};\n\n#ifdef __cpp_impl_coroutine\ninline void internal::WebSocketConnectionAwaiter::await_suspend(\n    std::coroutine_handle<> handle)\n{\n    client_->connectToServer(req_,\n                             [this, handle](ReqResult result,\n                                            const HttpResponsePtr &resp,\n                                            const WebSocketClientPtr &) {\n                                 if (result == ReqResult::Ok)\n                                     setValue(resp);\n                                 else\n                                 {\n                                     std::string reason;\n                                     if (result == ReqResult::BadResponse)\n                                         reason = \"BadResponse\";\n                                     else if (result ==\n                                              ReqResult::NetworkFailure)\n                                         reason = \"NetworkFailure\";\n                                     else if (result ==\n                                              ReqResult::BadServerAddress)\n                                         reason = \"BadServerAddress\";\n                                     else if (result == ReqResult::Timeout)\n                                         reason = \"Timeout\";\n                                     setException(std::make_exception_ptr(\n                                         std::runtime_error(reason)));\n                                 }\n                                 handle.resume();\n                             });\n}\n#endif\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/WebSocketConnection.h",
    "content": "/**\n *\n *  @file WebSocketConnection.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <json/value.h>\n#include <memory>\n#include <string>\n#include <drogon/HttpTypes.h>\n#include <string_view>\n#include <trantor/net/InetAddress.h>\n#include <trantor/utils/NonCopyable.h>\n\nnamespace drogon\n{\nenum class CloseCode\n{\n    /*1000 indicates a normal closure, meaning that the purpose for which the\n       connection was established has been fulfilled.*/\n    kNormalClosure = 1000,\n    /*1001 indicates that an endpoint is \"going away\", such as a server going\n       down or a browser having navigated away from a page.*/\n    kEndpointGone = 1001,\n    /*1002 indicates that an endpoint is terminating the connection due to a\n       protocol error.*/\n    kProtocolError = 1002,\n    /*1003 indicates that an endpoint is terminating the connection because it\n       has received a type of data it cannot accept (e.g., an endpoint that\n       understands only text data MAY send this if it receives a binary\n       message).*/\n    kInvalidMessage = 1003,\n    /*1005 is a reserved value and MUST NOT be set as a status code in a Close\n       control frame by an endpoint.  It is designated for use in applications\n       expecting a status code to indicate that no status code was actually\n       present.*/\n    kNone = 1005,\n    /*1006 is a reserved value and MUST NOT be set as a status code in a Close\n       control frame by an endpoint.  It is designated for use in applications\n       expecting a status code to indicate that the connection was closed\n       abnormally, e.g., without sending or receiving a Close control frame.\n    */\n    kAbnormally = 1006,\n    /*1007 indicates that an endpoint is terminating the connection because it\n       has received data within a message that was not consistent with the type\n       of the message (e.g., non-UTF-8 [RFC3629] data within a text message).*/\n    kWrongMessageContent = 1007,\n    /*1008 indicates that an endpoint is terminating the connection because it\n       has received a message that violates its policy.  This is a generic\n       status code that can be returned when there is no other more suitable\n       status code (e.g., 1003 or 1009) or if there is a need to hide specific\n       details about the policy.\n    */\n    kViolation = 1008,\n    /*1009 indicates that an endpoint is terminating the connection because it\n       has received a message that is too big for it to process.*/\n    kMessageTooBig = 1009,\n    /*1010 indicates that an endpoint (client) is terminating the connection\n       because it has expected the server to negotiate one or more extension,\n       but the server didn't return them in the response message of the\n       WebSocket handshake.  The list of extensions that are needed SHOULD\n       appear in the /reason/ part of the Close frame. Note that this status\n       code is not used by the server, because it can fail the WebSocket\n       handshake instead.*/\n    kNeedMoreExtensions = 1010,\n    /*1011 indicates that a server is terminating the connection because it\n       encountered an unexpected condition that prevented it from fulfilling the\n       request.*/\n    kUnexpectedCondition = 1011,\n    /*1015 is a reserved value and MUST NOT be set as a status code in a Close\n       control frame by an endpoint.  It is designated for use in applications\n       expecting a status code to indicate that the connection was closed due to\n       a failure to perform a TLS handshake (e.g., the server certificate can't\n       be verified).*/\n    kTLSFailed = 1015\n};\n\n/**\n * @brief The WebSocket connection abstract class.\n *\n */\nclass WebSocketConnection\n{\n  public:\n    WebSocketConnection() = default;\n    virtual ~WebSocketConnection(){};\n\n    /**\n     * @brief Send a message to the peer\n     *\n     * @param msg The message to be sent.\n     * @param len The message length.\n     * @param type The message type.\n     */\n    virtual void send(\n        const char *msg,\n        uint64_t len,\n        const WebSocketMessageType type = WebSocketMessageType::Text) = 0;\n\n    /**\n     * @brief Send a message to the peer\n     *\n     * @param msg The message to be sent.\n     * @param type The message type.\n     */\n    virtual void send(\n        std::string_view msg,\n        const WebSocketMessageType type = WebSocketMessageType::Text) = 0;\n\n    /**\n     * @brief Send a message to the peer\n     *\n     * @param json The JSON message to be sent.\n     * @param type The message type.\n     */\n    virtual void sendJson(\n        const Json::Value &json,\n        const WebSocketMessageType type = WebSocketMessageType::Text) = 0;\n\n    /// Return the local IP address and port number of the connection\n    virtual const trantor::InetAddress &localAddr() const = 0;\n\n    /// Return the remote IP address and port number of the connection\n    virtual const trantor::InetAddress &peerAddr() const = 0;\n\n    /// Return true if the connection is open\n    virtual bool connected() const = 0;\n\n    /// Return true if the connection is closed\n    virtual bool disconnected() const = 0;\n\n    /**\n     * @brief Shut down the write direction, which means that further send\n     * operations are disabled.\n     *\n     * @param code Please refer to the enum class CloseCode. (RFC6455 7.4.1)\n     * @param reason The reason for closing the connection.\n     */\n    virtual void shutdown(const CloseCode code = CloseCode::kNormalClosure,\n                          const std::string &reason = \"\") = 0;\n\n    /// Close the connection\n    virtual void forceClose() = 0;\n\n    /**\n     * @brief Set custom data on the connection\n     *\n     * @param context The custom data.\n     */\n    void setContext(const std::shared_ptr<void> &context)\n    {\n        contextPtr_ = context;\n    }\n\n    /**\n     * @brief Set custom data on the connection\n     *\n     * @param context The custom data.\n     */\n    void setContext(std::shared_ptr<void> &&context)\n    {\n        contextPtr_ = std::move(context);\n    }\n\n    /**\n     * @brief Get custom data from the connection\n     *\n     * @tparam T The type of the data\n     * @return std::shared_ptr<T> The smart pointer to the data object.\n     */\n    template <typename T>\n    std::shared_ptr<T> getContext() const\n    {\n        return std::static_pointer_cast<T>(contextPtr_);\n    }\n\n    /**\n     * @brief Get the custom data reference from the connection.\n     * @note Please make sure that the context is available.\n     * @tparam T The type of the data stored in the context.\n     * @return T&\n     */\n    template <typename T>\n    T &getContextRef() const\n    {\n        return *(static_cast<T *>(contextPtr_.get()));\n    }\n\n    /// Return true if the context is set by user.\n    bool hasContext()\n    {\n        return (bool)contextPtr_;\n    }\n\n    /// Clear the context.\n    void clearContext()\n    {\n        contextPtr_.reset();\n    }\n\n    /**\n     * @brief Set the heartbeat(ping) message sent to the peer.\n     *\n     * @param message The ping message.\n     * @param interval The sending interval.\n     * @note\n     * Both the server and the client in Drogon automatically send the pong\n     * message after receiving the ping message.\n     * An empty ping message is sent every 30 seconds by default. The method\n     * overrides the default behavior.\n     */\n    virtual void setPingMessage(\n        const std::string &message,\n        const std::chrono::duration<double> &interval) = 0;\n\n    /**\n     * @brief Disable sending ping messages to the peer.\n     */\n    virtual void disablePing() = 0;\n\n  private:\n    std::shared_ptr<void> contextPtr_;\n};\n\nusing WebSocketConnectionPtr = std::shared_ptr<WebSocketConnection>;\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/WebSocketController.h",
    "content": "/**\n *\n *  WebSocketController.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/WebSocketConnection.h>\n#include <drogon/HttpTypes.h>\n#include <trantor/utils/Logger.h>\n#include <iostream>\n#include <memory>\n#include <string>\n#include <vector>\n\n#define WS_PATH_LIST_BEGIN        \\\n    static void initPathRouting() \\\n    {\n#define WS_PATH_ADD(path, ...) registerSelf__(path, {__VA_ARGS__})\n#define WS_ADD_PATH_VIA_REGEX(regExp, ...) \\\n    registerSelfRegex__(regExp, {__VA_ARGS__})\n#define WS_PATH_LIST_END }\n\nnamespace drogon\n{\n/**\n * @brief The abstract base class for WebSocket controllers.\n *\n */\nclass WebSocketControllerBase : public virtual DrObjectBase\n{\n  public:\n    // This function is called when a new message is received\n    virtual void handleNewMessage(const WebSocketConnectionPtr &,\n                                  std::string &&,\n                                  const WebSocketMessageType &) = 0;\n\n    // This function is called after a new connection of WebSocket is\n    // established.\n    virtual void handleNewConnection(const HttpRequestPtr &,\n                                     const WebSocketConnectionPtr &) = 0;\n\n    // This function is called after a WebSocket connection is closed\n    virtual void handleConnectionClosed(const WebSocketConnectionPtr &) = 0;\n\n    virtual ~WebSocketControllerBase()\n    {\n    }\n};\n\nusing WebSocketControllerBasePtr = std::shared_ptr<WebSocketControllerBase>;\n\n/**\n * @brief The reflection base class template for WebSocket controllers\n *\n * @tparam T the type of the implementation class\n * @tparam AutoCreation The flag for automatically creating, user can set this\n * flag to false for classes that have nondefault constructors.\n */\ntemplate <typename T, bool AutoCreation = true>\nclass WebSocketController : public DrObject<T>, public WebSocketControllerBase\n{\n  public:\n    static const bool isAutoCreation = AutoCreation;\n\n    virtual ~WebSocketController()\n    {\n    }\n\n  protected:\n    WebSocketController()\n    {\n    }\n\n    static void registerSelf__(\n        const std::string &path,\n        const std::vector<internal::HttpConstraint> &constraints)\n    {\n        LOG_TRACE << \"register websocket controller(\"\n                  << WebSocketController<T, AutoCreation>::classTypeName()\n                  << \") on path:\" << path;\n        app().registerWebSocketController(\n            path,\n            WebSocketController<T, AutoCreation>::classTypeName(),\n            constraints);\n    }\n\n    static void registerSelfRegex__(\n        const std::string &regExp,\n        const std::vector<internal::HttpConstraint> &constraints)\n    {\n        LOG_TRACE << \"register websocket controller(\"\n                  << WebSocketController<T, AutoCreation>::classTypeName()\n                  << \") on regExp:\" << regExp;\n        app().registerWebSocketControllerRegex(\n            regExp,\n            WebSocketController<T, AutoCreation>::classTypeName(),\n            constraints);\n    }\n\n  private:\n    class pathRegistrator\n    {\n      public:\n        pathRegistrator()\n        {\n            if (AutoCreation)\n            {\n                T::initPathRouting();\n            }\n        }\n    };\n\n    friend pathRegistrator;\n    static pathRegistrator registrator_;\n\n    virtual void *touch()\n    {\n        return &registrator_;\n    }\n};\n\ntemplate <typename T, bool AutoCreation>\ntypename WebSocketController<T, AutoCreation>::pathRegistrator\n    WebSocketController<T, AutoCreation>::registrator_;\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/drogon.h",
    "content": "/**\n *\n *  drogon.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <trantor/net/EventLoop.h>\n#include <trantor/net/InetAddress.h>\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n\n#include <drogon/CacheMap.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/HttpClient.h>\n#include <drogon/HttpController.h>\n#include <drogon/HttpSimpleController.h>\n#include <drogon/utils/Utilities.h>\n#include <drogon/MultiPart.h>\n#include <drogon/plugins/Plugin.h>\n#include <drogon/plugins/SecureSSLRedirector.h>\n#include <drogon/plugins/AccessLogger.h>\n#include <drogon/plugins/RealIpResolver.h>\n#include <drogon/plugins/Hodor.h>\n#include <drogon/plugins/SlashRemover.h>\n#include <drogon/plugins/GlobalFilters.h>\n#include <drogon/plugins/PromExporter.h>\n#include <drogon/IntranetIpFilter.h>\n#include <drogon/LocalHostFilter.h>\n#include <drogon/Cookie.h>\n#include <drogon/Session.h>\n#include <drogon/IOThreadStorage.h>\n#include <drogon/UploadFile.h>\n#include <drogon/orm/DbClient.h>\n\n/**\n * @mainpage\n * ### Overview\n * Drogon is a C++14/17-based HTTP application framework. Drogon can be used to\n * easily build various types of web application server programs using C++.\n */\n"
  },
  {
    "path": "lib/inc/drogon/drogon_callbacks.h",
    "content": "/**\n *\n *  drogon_callbacks.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/HttpTypes.h>\n#include <functional>\n#include <memory>\n\nnamespace drogon\n{\nclass HttpResponse;\nusing HttpResponsePtr = std::shared_ptr<HttpResponse>;\nclass HttpRequest;\nusing HttpRequestPtr = std::shared_ptr<HttpRequest>;\nusing AdviceCallback = std::function<void(const HttpResponsePtr &)>;\nusing AdviceChainCallback = std::function<void()>;\nusing AdviceStartSessionCallback = std::function<void(const std::string &)>;\nusing AdviceDestroySessionCallback = std::function<void(const std::string &)>;\nusing FilterCallback = std::function<void(const HttpResponsePtr &)>;\nusing FilterChainCallback = std::function<void()>;\nusing HttpReqCallback = std::function<void(ReqResult, const HttpResponsePtr &)>;\n\nusing MiddlewareCallback = std::function<void(const HttpResponsePtr &)>;\nusing MiddlewareNextCallback =\n    std::function<void(std::function<void(const HttpResponsePtr &)> &&)>;\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/drogon_test.h",
    "content": "#pragma once\n#include <trantor/utils/NonCopyable.h>\n#include <drogon/DrObject.h>\n#include <drogon/exports.h>\n\n#include <memory>\n#include <mutex>\n#include <sstream>\n#include <atomic>\n#include <string_view>\n#include <cstddef>\n\n/**\n * @brief Drogon Test is a minimal effort test framework developed because the\n * major C++ test frameworks doesn't handle async programs well. Drogon Test's\n * syntax is inspired by both Google Test and Catch2\n */\nnamespace drogon\n{\nnamespace test\n{\n#define TEST_CTX drogon_test_ctx_\n#define DROGON_TESTCASE_PREIX_ drtest__\n#define DROGON_TESTCASE_PREIX_STR_ \"drtest__\"\n#define TEST_FLAG_ drgood__\n\n#define DROGON_TEST_STRINGIFY__(x) #x\n#define DROGON_TEST_STRINGIFY(x) DROGON_TEST_STRINGIFY__(x)\n#define DROGON_TEST_CONCAT__(a, b) a##b\n#define DROGON_TEST_CONCAT(a, b) DROGON_TEST_CONCAT__(a, b)\n\n#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC)\n#define DROGON_TEST_START_SUPRESSION_ _Pragma(\"GCC diagnostic push\")\n#define DROGON_TEST_END_SUPRESSION_ _Pragma(\"GCC diagnostic pop\")\n#define DROGON_TEST_SUPPRESS_PARENTHESES_WARNING_ \\\n    _Pragma(\"GCC diagnostic ignored \\\"-Wparentheses\\\"\")\n#define DROGON_TEST_SUPPRESS_UNUSED_VALUE_WARNING_ \\\n    _Pragma(\"GCC diagnostic ignored \\\"-Wunused-value\\\"\")\n#elif defined(__clang__) && !defined(_MSC_VER)\n#define DROGON_TEST_START_SUPRESSION_ _Pragma(\"clang diagnostic push\")\n#define DROGON_TEST_END_SUPRESSION_ _Pragma(\"clang diagnostic pop\")\n#define DROGON_TEST_SUPPRESS_PARENTHESES_WARNING_ \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wparentheses\\\"\")\n#define DROGON_TEST_SUPPRESS_UNUSED_VALUE_WARNING_ \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wunused-value\\\"\")\n// MSVC don't have an equlivent. Add other compilers here\n#else\n#define DROGON_TEST_START_SUPRESSION_\n#define DROGON_TEST_END_SUPRESSION_\n#define DROGON_TEST_SUPPRESS_PARENTHESES_WARNING_\n#define DROGON_TEST_SUPPRESS_UNUSED_VALUE_WARNING_\n#endif\n\nclass Case;\n\nnamespace internal\n{\nDROGON_EXPORT extern std::atomic<size_t> numAssertions;\nDROGON_EXPORT extern std::atomic<size_t> numCorrectAssertions;\nDROGON_EXPORT extern std::atomic<size_t> numFailedTestCases;\nDROGON_EXPORT extern bool printSuccessfulTests;\n\nDROGON_EXPORT void registerCase(Case *test);\nDROGON_EXPORT void unregisterCase(Case *test);\n\ntemplate <typename _Tp, typename dummy = void>\nstruct is_printable : std::false_type\n{\n};\n\ntemplate <typename _Tp>\nstruct is_printable<\n    _Tp,\n    std::enable_if_t<std::is_same_v<decltype(std::cout << std::declval<_Tp>()),\n                                    std::ostream &>>> : std::true_type\n{\n};\n\ninline std::string escapeString(const std::string_view sv)\n{\n    std::string result;\n    result.reserve(sv.size());\n    for (auto ch : sv)\n    {\n        if (ch == '\\n')\n            result += \"\\\\n\";\n        else if (ch == '\\r')\n            result += \"\\\\r\";\n        else if (ch == '\\t')\n            result += \"\\\\t\";\n        else if (ch == '\\b')\n            result += \"\\\\b\";\n        else if (ch == '\\\\')\n            result += \"\\\\\\\\\";\n        else if (ch == '\"')\n            result += \"\\\"\";\n        else if (ch == '\\v')\n            result += \"\\\\v\";\n        else if (ch == '\\a')\n            result += \"\\\\a\";\n        else\n            result.push_back(ch);\n    }\n    return result;\n}\n\nDROGON_EXPORT std::string prettifyString(const std::string_view sv,\n                                         size_t maxLength = 120);\n\ntemplate <typename... Args>\ninline void outputReason(Args &&...args)\n{\n    (std::cout << ... << std::forward<Args>(args));\n}\n\ntemplate <typename T>\ninline std::string attemptPrint(T &&v)\n{\n    using Type = std::remove_cv_t<std::remove_reference_t<T>>;\n    if constexpr (std::is_same_v<Type, std::nullptr_t>)\n        return \"nullptr\";\n    else if constexpr (std::is_same_v<Type, char>)\n        return \"'\" + std::string(1, v) + \"'\";\n    else if constexpr (std::is_convertible_v<Type, std::string_view>)\n        return prettifyString(v);\n    else if constexpr (internal::is_printable<Type>::value)\n    {\n        std::stringstream ss;\n        ss << v;\n        return ss.str();\n    }\n    return \"{un-printable}\";\n}\n\ninline std::string stringifyFuncCall(const std::string &funcName)\n{\n    return funcName + \"()\";\n}\n\ninline std::string stringifyFuncCall(const std::string &funcName,\n                                     const std::string &param1)\n{\n    return funcName + \"(\" + param1 + \")\";\n}\n\ninline std::string stringifyFuncCall(const std::string &funcName,\n                                     const std::string &param1,\n                                     const std::string &param2)\n{\n    return funcName + \"(\" + param1 + \", \" + param2 + \")\";\n}\n\nstruct ComparsionResult\n{\n    std::pair<bool, std::string> result() const\n    {\n        return {comparsionResilt_, expansion_};\n    }\n\n    bool comparsionResilt_;\n    std::string expansion_;\n};\n\ntemplate <typename T>\nstruct Lhs\n{\n    template <typename _ = void>  // HACK: prevent this function to be evaluated\n                                  // when not invoked\n    std::pair<bool, std::string> result() const\n    {\n        return {(bool)ref_, attemptPrint(ref_)};\n    }\n\n    Lhs(const T &lhs) : ref_(lhs)\n    {\n    }\n\n    const T &ref_;\n\n    template <typename RhsType>\n    ComparsionResult operator<(const RhsType &rhs)\n    {\n        return ComparsionResult{ref_ < rhs,\n                                attemptPrint(ref_) + \" < \" +\n                                    attemptPrint(ref_)};\n    }\n\n    template <typename RhsType>\n    ComparsionResult operator>(const RhsType &rhs)\n    {\n        return ComparsionResult{ref_ > rhs,\n                                attemptPrint(ref_) + \" > \" + attemptPrint(rhs)};\n    }\n\n    template <typename RhsType>\n    ComparsionResult operator<=(const RhsType &rhs)\n    {\n        return ComparsionResult{ref_ <= rhs,\n                                attemptPrint(ref_) +\n                                    \" <= \" + attemptPrint(rhs)};\n    }\n\n    template <typename RhsType>\n    ComparsionResult operator>=(const RhsType &rhs)\n    {\n        return ComparsionResult{ref_ >= rhs,\n                                attemptPrint(ref_) +\n                                    \" >= \" + attemptPrint(rhs)};\n    }\n\n    template <typename RhsType>\n    ComparsionResult operator==(const RhsType &rhs)\n    {\n        return ComparsionResult{ref_ == rhs,\n                                attemptPrint(ref_) +\n                                    \" == \" + attemptPrint(rhs)};\n    }\n\n    template <typename RhsType>\n    ComparsionResult operator!=(const RhsType &rhs)\n    {\n        return ComparsionResult{ref_ != rhs,\n                                attemptPrint(ref_) +\n                                    \" != \" + attemptPrint(rhs)};\n    }\n\n    template <typename RhsType>\n    ComparsionResult operator&&(const RhsType &rhs)\n    {\n        static_assert(!std::is_same_v<RhsType, void>,\n                      \" && is not supported in expression decomposition\");\n        return {};\n    }\n\n    template <typename RhsType>\n    ComparsionResult operator||(const RhsType &rhs)\n    {\n        static_assert(!std::is_same_v<RhsType, void>,\n                      \" || is not supported in expression decomposition\");\n        return {};\n    }\n\n    template <typename RhsType>\n    ComparsionResult operator|(const RhsType &rhs)\n    {\n        static_assert(!std::is_same_v<RhsType, void>,\n                      \" | is not supported in expression decomposition\");\n        return {};\n    }\n\n    template <typename RhsType>\n    ComparsionResult operator&(const RhsType &rhs)\n    {\n        static_assert(!std::is_same_v<RhsType, void>,\n                      \" & is not supported in expression decomposition\");\n        return {};\n    }\n};\n\nstruct Decomposer\n{\n    template <typename T>\n    Lhs<T> operator<=(const T &other)\n    {\n        return Lhs<T>(other);\n    }\n};\n\n}  // namespace internal\n\nclass DROGON_EXPORT ThreadSafeStream final\n{\n  public:\n    ThreadSafeStream(std::ostream &os) : os_(os)\n    {\n        mtx_.lock();\n    }\n\n    ~ThreadSafeStream()\n    {\n        mtx_.unlock();\n    }\n\n    template <typename T>\n    std::ostream &operator<<(const T &rhs)\n    {\n        return os_ << rhs;\n    }\n\n    static std::mutex mtx_;\n    std::ostream &os_;\n};\n\nDROGON_EXPORT ThreadSafeStream print();\nDROGON_EXPORT ThreadSafeStream printErr();\n\nclass CaseBase : public trantor::NonCopyable\n{\n  public:\n    CaseBase() = default;\n\n    CaseBase(const std::string &name) : name_(name)\n    {\n    }\n\n    CaseBase(std::shared_ptr<CaseBase> parent, const std::string &name)\n        : parent_(parent), name_(name)\n    {\n    }\n\n    virtual ~CaseBase() = default;\n\n    std::string fullname() const\n    {\n        std::string result;\n        auto curr = this;\n        while (curr != nullptr)\n        {\n            result = curr->name() + result;\n            if (curr->parent_ != nullptr)\n                result = \".\" + result;\n            curr = curr->parent_.get();\n        }\n        return result;\n    }\n\n    const std::string &name() const\n    {\n        return name_;\n    }\n\n    void setFailed()\n    {\n        if (failed_ == false)\n        {\n            internal::numFailedTestCases++;\n            failed_ = true;\n        }\n    }\n\n    bool failed() const\n    {\n        return failed_;\n    }\n\n  protected:\n    bool failed_ = false;\n    std::shared_ptr<CaseBase> parent_ = nullptr;\n    std::string name_;\n};\n\nclass Case : public CaseBase\n{\n  public:\n    Case(const std::string &name) : CaseBase(name)\n    {\n        internal::registerCase(this);\n    }\n\n    Case(std::shared_ptr<Case> parent, const std::string &name)\n        : CaseBase(parent, name)\n    {\n        internal::registerCase(this);\n    }\n\n    virtual ~Case()\n    {\n        internal::unregisterCase(this);\n    }\n};\n\nstruct TestCase : public CaseBase\n{\n    TestCase(const std::string &name) : CaseBase(name)\n    {\n    }\n\n    virtual ~TestCase() = default;\n    virtual void doTest_(std::shared_ptr<Case>) = 0;\n};\n\nDROGON_EXPORT void printTestStats();\nDROGON_EXPORT int run(int argc, char **argv);\n}  // namespace test\n}  // namespace drogon\n\n#define ERROR_MSG(func_name, expr)                                    \\\n    drogon::test::printErr()                                          \\\n        << \"\\x1B[1;37mIn test case \" << TEST_CTX->fullname() << \"\\n\"  \\\n        << \"\\x1B[0;37m↳ \" << __FILE__ << \":\" << __LINE__              \\\n        << \" \\x1B[0;31m FAILED:\\x1B[0m\\n\"                             \\\n        << \"  \\033[0;34m\"                                             \\\n        << drogon::test::internal::stringifyFuncCall(func_name, expr) \\\n        << \"\\x1B[0m\\n\"\n\n#define PASSED_MSG(func_name, expr)                                   \\\n    drogon::test::print()                                             \\\n        << \"\\x1B[1;37mIn test case \" << TEST_CTX->fullname() << \"\\n\"  \\\n        << \"\\x1B[0;37m↳ \" << __FILE__ << \":\" << __LINE__              \\\n        << \" \\x1B[0;32m PASSED:\\x1B[0m\\n\"                             \\\n        << \"  \\033[0;34m\"                                             \\\n        << drogon::test::internal::stringifyFuncCall(func_name, expr) \\\n        << \"\\x1B[0m\\n\"\n\n#define SET_TEST_SUCCESS__ \\\n    do                     \\\n    {                      \\\n        TEST_FLAG_ = true; \\\n    } while (0);\n\n#define TEST_INTERNAL__(func_name,                          \\\n                        expr,                               \\\n                        eval,                               \\\n                        on_exception,                       \\\n                        on_non_standard_exception,          \\\n                        on_leaving)                         \\\n    do                                                      \\\n    {                                                       \\\n        bool TEST_FLAG_ = false;                            \\\n        using drogon::test::internal::stringifyFuncCall;    \\\n        using drogon::test::printErr;                       \\\n        drogon::test::internal::numAssertions++;            \\\n        try                                                 \\\n        {                                                   \\\n            eval;                                           \\\n        }                                                   \\\n        catch (const std::exception &e)                     \\\n        {                                                   \\\n            (void)e;                                        \\\n            on_exception;                                   \\\n        }                                                   \\\n        catch (...)                                         \\\n        {                                                   \\\n            on_non_standard_exception;                      \\\n        }                                                   \\\n        if (TEST_FLAG_)                                     \\\n            drogon::test::internal::numCorrectAssertions++; \\\n        else                                                \\\n            TEST_CTX->setFailed();                          \\\n        on_leaving;                                         \\\n    } while (0);\n\n#define EVAL_AND_CHECK_TRUE__(func_name, expr)                       \\\n    do                                                               \\\n    {                                                                \\\n        bool drresult__;                                             \\\n        std::string drexpansion__;                                   \\\n        DROGON_TEST_START_SUPRESSION_                                \\\n        DROGON_TEST_SUPPRESS_PARENTHESES_WARNING_                    \\\n        std::tie(drresult__, drexpansion__) =                        \\\n            (drogon::test::internal::Decomposer() <= expr).result(); \\\n        DROGON_TEST_END_SUPRESSION_                                  \\\n        if (!drresult__)                                             \\\n        {                                                            \\\n            ERROR_MSG(func_name, #expr)                              \\\n                << \"With expansion\\n\"                                \\\n                << \"  \\033[0;33m\" << drexpansion__ << \"\\x1B[0m\\n\\n\"; \\\n        }                                                            \\\n        else                                                         \\\n            SET_TEST_SUCCESS__;                                      \\\n    } while (0);\n\n#define PRINT_UNEXPECTED_EXCEPTION__(func_name, expr)         \\\n    do                                                        \\\n    {                                                         \\\n        ERROR_MSG(func_name, expr)                            \\\n            << \"An unexpected exception is thrown. what():\\n\" \\\n            << \"  \\033[0;33m\" << e.what() << \"\\x1B[0m\\n\\n\";   \\\n    } while (0);\n\n#define PRINT_PASSED__(func_name, expr)                                 \\\n    do                                                                  \\\n    {                                                                   \\\n        if (drogon::test::internal::printSuccessfulTests && TEST_FLAG_) \\\n        {                                                               \\\n            PASSED_MSG(func_name, expr) << \"\\n\";                        \\\n        }                                                               \\\n    } while (0);\n\n#define RETURN_ON_FAILURE__ \\\n    do                      \\\n    {                       \\\n        if (!TEST_FLAG_)    \\\n            return;         \\\n    } while (0);\n\n#define CO_RETURN_ON_FAILURE__ \\\n    do                         \\\n    {                          \\\n        if (!TEST_FLAG_)       \\\n            co_return;         \\\n    } while (0);\n\n#define DIE_ON_FAILURE__                                                \\\n    do                                                                  \\\n    {                                                                   \\\n        using namespace drogon::test;                                   \\\n        if (!TEST_FLAG_)                                                \\\n        {                                                               \\\n            printTestStats();                                           \\\n            printErr() << \"Force exiting due to a mandation failed.\\n\"; \\\n            exit(1);                                                    \\\n        }                                                               \\\n    } while (0);\n\n#define PRINT_NONSTANDARD_EXCEPTION__(func_name, expr)        \\\n    do                                                        \\\n    {                                                         \\\n        ERROR_MSG(func_name, expr)                            \\\n            << \"Unexpected unknown exception is thrown.\\n\\n\"; \\\n    } while (0);\n\n#define EVAL__(expr) \\\n    do               \\\n    {                \\\n        expr;        \\\n    } while (0);\n\n#define NOTHING__ \\\n    {             \\\n    }\n\n#define PRINT_ERR_NOEXCEPTION__(expr, func_name)                     \\\n    do                                                               \\\n    {                                                                \\\n        if (!TEST_FLAG_)                                             \\\n            ERROR_MSG(func_name, expr)                               \\\n                << \"With expecitation\\n\"                             \\\n                << \"  Expected to throw an exception. But none are \" \\\n                   \"thrown.\\n\\n\";                                    \\\n    } while (0);\n\n#define PRINT_ERR_WITHEXCEPTION__(expr, func_name)                   \\\n    do                                                               \\\n    {                                                                \\\n        if (!TEST_FLAG_)                                             \\\n            ERROR_MSG(func_name, expr)                               \\\n                << \"With expecitation\\n\"                             \\\n                << \"  Should to not throw an exception. But one is \" \\\n                   \"thrown.\\n\\n\";                                    \\\n    } while (0);\n\n#define PRINT_ERR_BAD_EXCEPTION__(                                             \\\n    expr, func_name, excep_type, exceptionThrown, correctExceptionType)        \\\n    do                                                                         \\\n    {                                                                          \\\n        assert((exceptionThrown && correctExceptionType) || !exceptionThrown); \\\n        if (exceptionThrown == true && correctExceptionType == false)          \\\n        {                                                                      \\\n            ERROR_MSG(func_name, expr)                                         \\\n                << \"With expecitation\\n\"                                       \\\n                << \"  Exception have been throw but not of type \\033[0;33m\"    \\\n                << #excep_type << \"\\033[0m.\\n\\n\";                              \\\n        }                                                                      \\\n        else if (exceptionThrown == false)                                     \\\n        {                                                                      \\\n            ERROR_MSG(func_name, expr)                                         \\\n                << \"With expecitation\\n\"                                       \\\n                << \"  A \\033[0;33m\" << #excep_type                             \\\n                << \"\\033[0m exception is expected. But nothing was thrown\"     \\\n                << \"\\033[0m.\\n\\n\";                                             \\\n        }                                                                      \\\n    } while (0);\n\n#define CHECK_INTERNAL__(expr, func_name, on_leave)                      \\\n    do                                                                   \\\n    {                                                                    \\\n        TEST_INTERNAL__(func_name,                                       \\\n                        expr,                                            \\\n                        EVAL_AND_CHECK_TRUE__(func_name, expr),          \\\n                        PRINT_UNEXPECTED_EXCEPTION__(func_name, #expr),  \\\n                        PRINT_NONSTANDARD_EXCEPTION__(func_name, #expr), \\\n                        on_leave PRINT_PASSED__(func_name, #expr));      \\\n    } while (0)\n\n#define CHECK_THROWS_INTERNAL__(expr, func_name, on_leave)              \\\n    do                                                                  \\\n    {                                                                   \\\n        TEST_INTERNAL__(func_name,                                      \\\n                        expr,                                           \\\n                        EVAL__(expr),                                   \\\n                        SET_TEST_SUCCESS__,                             \\\n                        SET_TEST_SUCCESS__,                             \\\n                        PRINT_ERR_NOEXCEPTION__(#expr, func_name)       \\\n                            on_leave PRINT_PASSED__(func_name, #expr)); \\\n    } while (0)\n\n#define CHECK_THROWS_AS_INTERNAL__(expr, func_name, except_type, on_leave)    \\\n    do                                                                        \\\n    {                                                                         \\\n        bool exceptionThrown = false;                                         \\\n        TEST_INTERNAL__(                                                      \\\n            func_name,                                                        \\\n            expr,                                                             \\\n            EVAL__(expr),                                                     \\\n            {                                                                 \\\n                exceptionThrown = true;                                       \\\n                if (dynamic_cast<const except_type *>(&e) != nullptr)         \\\n                    SET_TEST_SUCCESS__;                                       \\\n            },                                                                \\\n            { exceptionThrown = true; },                                      \\\n            PRINT_ERR_BAD_EXCEPTION__(#expr \", \" #except_type,                \\\n                                      func_name,                              \\\n                                      except_type,                            \\\n                                      exceptionThrown,                        \\\n                                      TEST_FLAG_)                             \\\n                on_leave PRINT_PASSED__(func_name, #expr \", \" #except_type)); \\\n    } while (0)\n\n#define CHECK_NOTHROW_INTERNAL__(expr, func_name, on_leave)             \\\n    do                                                                  \\\n    {                                                                   \\\n        TEST_INTERNAL__(func_name,                                      \\\n                        expr,                                           \\\n                        EVAL__(expr) SET_TEST_SUCCESS__,                \\\n                        NOTHING__,                                      \\\n                        NOTHING__,                                      \\\n                        PRINT_ERR_WITHEXCEPTION__(#expr, func_name)     \\\n                            on_leave PRINT_PASSED__(func_name, #expr)); \\\n    } while (0)\n\n#define CHECK(expr) CHECK_INTERNAL__(expr, \"CHECK\", NOTHING__)\n#define CHECK_THROWS(expr) \\\n    CHECK_THROWS_INTERNAL__(expr, \"CHECK_THROWS\", NOTHING__)\n#define CHECK_NOTHROW(expr) \\\n    CHECK_NOTHROW_INTERNAL__(expr, \"CHECK_NOTHROW\", NOTHING__)\n#define CHECK_THROWS_AS(expr, except_type) \\\n    CHECK_THROWS_AS_INTERNAL__(expr, \"CHECK_THROWS_AS\", except_type, NOTHING__)\n\n#define REQUIRE(expr) CHECK_INTERNAL__(expr, \"REQUIRE\", RETURN_ON_FAILURE__)\n#define REQUIRE_THROWS(expr) \\\n    CHECK_THROWS_INTERNAL__(expr, \"REQUIRE_THROWS\", RETURN_ON_FAILURE__)\n#define REQUIRE_NOTHROW(expr) \\\n    CHECK_NOTHROW_INTERNAL__(expr, \"REQUIRE_NOTHROW\", RETURN_ON_FAILURE__)\n#define REQUIRE_THROWS_AS(expr, except_type)        \\\n    CHECK_THROWS_AS_INTERNAL__(expr,                \\\n                               \"REQUIRE_THROWS_AS\", \\\n                               except_type,         \\\n                               RETURN_ON_FAILURE__)\n\n#define CO_REQUIRE(expr) \\\n    CHECK_INTERNAL__(expr, \"CO_REQUIRE\", CO_RETURN_ON_FAILURE__)\n#define CO_REQUIRE_THROWS(expr) \\\n    CHECK_THROWS_INTERNAL__(expr, \"CO_REQUIRE_THROWS\", CO_RETURN_ON_FAILURE__)\n#define CO_REQUIRE_NOTHROW(expr) \\\n    CHECK_NOTHROW_INTERNAL__(expr, \"CO_REQUIRE_NOTHROW\", CO_RETURN_ON_FAILURE__)\n#define CO_REQUIRE_THROWS_AS(expr, except_type)        \\\n    CHECK_THROWS_AS_INTERNAL__(expr,                   \\\n                               \"CO_REQUIRE_THROWS_AS\", \\\n                               except_type,            \\\n                               CO_RETURN_ON_FAILURE__)\n\n#define MANDATE(expr) CHECK_INTERNAL__(expr, \"MANDATE\", DIE_ON_FAILURE__)\n#define MANDATE_THROWS(expr) \\\n    CHECK_THROWS_INTERNAL__(expr, \"MANDATE_THROWS\", DIE_ON_FAILURE__)\n#define MANDATE_NOTHROW(expr) \\\n    CHECK_NOTHROW_INTERNAL__(expr, \"MANDATE_NOTHROW\", DIE_ON_FAILURE__)\n#define MANDATE_THROWS_AS(expr, except_type)        \\\n    CHECK_THROWS_AS_INTERNAL__(expr,                \\\n                               \"MANDATE_THROWS_AS\", \\\n                               except_type,         \\\n                               DIE_ON_FAILURE__)\n\n#define STATIC_REQUIRE(expr)                            \\\n    do                                                  \\\n    {                                                   \\\n        DROGON_TEST_START_SUPRESSION_                   \\\n        DROGON_TEST_SUPPRESS_UNUSED_VALUE_WARNING_      \\\n        TEST_CTX;                                       \\\n        DROGON_TEST_END_SUPRESSION_                     \\\n        drogon::test::internal::numAssertions++;        \\\n        static_assert((expr), #expr \" failed.\");        \\\n        drogon::test::internal::numCorrectAssertions++; \\\n    } while (0)\n\n#define FAIL(...)                                                       \\\n    do                                                                  \\\n    {                                                                   \\\n        using namespace drogon::test;                                   \\\n        TEST_CTX->setFailed();                                          \\\n        printErr() << \"\\x1B[1;37mIn test case \" << TEST_CTX->fullname() \\\n                   << \"\\n\"                                              \\\n                   << \"\\x1B[0;37m\" << __FILE__ << \":\" << __LINE__       \\\n                   << \" \\x1B[0;31m FAILED:\\x1B[0m\\n\"                    \\\n                   << \"  Reason: \";                                     \\\n        drogon::test::internal::outputReason(__VA_ARGS__);              \\\n        printErr() << \"\\n\\n\";                                           \\\n        drogon::test::internal::numAssertions++;                        \\\n    } while (0)\n#define FAULT(...)                                                 \\\n    do                                                             \\\n    {                                                              \\\n        using namespace drogon::test;                              \\\n        FAIL(__VA_ARGS__);                                         \\\n        printTestStats();                                          \\\n        printErr() << \"Force exiting due to a FAULT statement.\\n\"; \\\n        exit(1);                                                   \\\n    } while (0)\n\n#define SUCCESS()                                                            \\\n    do                                                                       \\\n    {                                                                        \\\n        DROGON_TEST_START_SUPRESSION_                                        \\\n        DROGON_TEST_SUPPRESS_UNUSED_VALUE_WARNING_                           \\\n        TEST_CTX;                                                            \\\n        DROGON_TEST_END_SUPRESSION_                                          \\\n        if (drogon::test::internal::printSuccessfulTests)                    \\\n            drogon::test::print()                                            \\\n                << \"\\x1B[1;37mIn test case \" << TEST_CTX->fullname() << \"\\n\" \\\n                << \"\\x1B[0;37m↳ \" << __FILE__ << \":\" << __LINE__             \\\n                << \" \\x1B[0;32m PASSED:\\x1B[0m\\n\"                            \\\n                << \"  \\033[0;34mSUCCESS()\\x1B[0m\\n\\n\";                       \\\n        drogon::test::internal::numAssertions++;                             \\\n        drogon::test::internal::numCorrectAssertions++;                      \\\n    } while (0)\n\n#define DROGON_TEST_CLASS_NAME_(test_name) \\\n    DROGON_TEST_CONCAT(DROGON_TESTCASE_PREIX_, test_name)\n\n#define DROGON_TEST(test_name)                                             \\\n    struct DROGON_TEST_CLASS_NAME_(test_name)                              \\\n        : public drogon::DrObject<DROGON_TEST_CLASS_NAME_(test_name)>,     \\\n          public drogon::test::TestCase                                    \\\n    {                                                                      \\\n        DROGON_TEST_CLASS_NAME_(test_name)                                 \\\n        () : drogon::test::TestCase(#test_name)                            \\\n        {                                                                  \\\n        }                                                                  \\\n        inline void doTest_(std::shared_ptr<drogon::test::Case>) override; \\\n    };                                                                     \\\n    void DROGON_TEST_CLASS_NAME_(test_name)::doTest_(                      \\\n        std::shared_ptr<drogon::test::Case> TEST_CTX)\n#define SUBTEST(name) (std::make_shared<drogon::test::Case>(TEST_CTX, name))\n#define SUBSECTION(name)                                                 \\\n    for (std::shared_ptr<drogon::test::Case> ctx_hold__ = TEST_CTX,      \\\n                                             ctx_tmp__ = SUBTEST(#name); \\\n         ctx_tmp__ != nullptr;                                           \\\n         TEST_CTX = ctx_hold__, ctx_tmp__ = nullptr)                     \\\n        if (TEST_CTX = ctx_tmp__, TEST_CTX != nullptr)\n"
  },
  {
    "path": "lib/inc/drogon/plugins/AccessLogger.h",
    "content": "/**\n *\n *  AccessLogger.h\n *\n */\n\n#pragma once\n\n#include <drogon/HttpRequest.h>\n#include <drogon/HttpResponse.h>\n#include <drogon/plugins/Plugin.h>\n#include <trantor/utils/AsyncFileLogger.h>\n#include <vector>\n\nnamespace drogon\n{\nnamespace plugin\n{\n/**\n * @brief This plugin is used to print all requests to the log.\n *\n * The json configuration is as follows:\n *\n * @code\n   {\n      \"name\": \"drogon::plugin::AccessLogger\",\n      \"dependencies\": [],\n      \"config\": {\n            \"use_spdlog\": false,\n            \"log_path\": \"./\",\n            \"log_format\": \"\",\n            \"log_file\": \"access.log\",\n            \"log_size_limit\": 0,\n            \"use_local_time\": true,\n            \"log_index\": 0,\n            // \"show_microseconds\": true,\n            // \"custom_time_format\": \"\",\n            // \"use_real_ip\": false\n            // \"path_exempt\": \"\"\n      }\n   }\n   @endcode\n *\n * log_format: a format string for access logging, there are several\n * placeholders that represent particular data.\n *     $date: the time when the log was printed.\n *     $request_date: the time when the request was created.\n *     $request_path|$path: the path of the request.\n *     $request_query|$query: the query string of the request.\n *     $request_url|$url: the URL of the request, equals to\n *                   $request_path+\"?\"+$request_query.\n *     $request_version|$version: the http version string.\n *     $request: the full request line.\n *     $remote_addr: the remote address\n *     $local_addr: the local address\n *     $request_len|$body_bytes_received: the content length of the request.\n *     $method: the HTTP method of the request.\n *     $thread: the current thread number.\n *     $response_len|$body_bytes_sent: the content length of the response.\n *     $http_[header_name]: the header of the request.\n *     $cookie_[cookie_name]: the cookie of the request.\n *     $upstream_http_[header_name]: the header of the response sent to the\n *                                   client.\n *     $status_code: the status code of the response.\n *     $status: the status code and string of the response.\n *     $processing_time: request processing time in seconds with a microseconds\n *                       resolution; time elapsed between the request object was\n *                       created and response object was created.\n * @note If the format string is empty or not configured, a default value of\n * \"$request_date $method $url [$body_bytes_received] ($remote_addr -\n * $local_addr) $status $body_bytes_sent $processing_time\" is applied.\n *\n * use_spdlog: log using spdlog, disabled by default.\n *\n * log_path: Log file path, empty by default,in which case,logs are output to\n * the regular log file (or stdout based on the log configuration).\n *\n * log_file: The access log file name, 'access.log' by default. if the file name\n * does not contain a extension, the .log extension is used.\n *\n * log_size_limit: 0 bytes by default, when the log file size reaches\n * \"log_size_limit\", the log file is switched. Zero value means never switch\n *\n * max_files: 0 by default, when the number of old log files exceeds max_files,\n * the oldest log file will be deleted. 0 means never delete.\n *\n * log_index: The index of log output, 0 by default.\n *\n * show_microseconds: Whether print microsecond in time. True by default.\n *\n * custom_time_format: Provide a custom format for time. If not provided or\n * empty, the default format is \"%Y%m%d %H:%M:%S\", with microseconds followed if\n * show_microseconds is true. For detailed information about formats, please\n * refer to cpp reference about strftime().\n *\n * use_real_ip: Log the real ip of peer. This option only takes effects when\n * set to true and RealIpResolver is enabled. False by default.\n *\n * Enable the plugin by adding the configuration to the list of plugins in the\n * configuration file.\n *\n * path_exempt: must be a string or a string array, present a regular expression\n * (for matching the path of a request) or a regular expression list for URLs\n * that don't have to be logged.\n *\n */\nclass DROGON_EXPORT AccessLogger : public drogon::Plugin<AccessLogger>\n{\n  public:\n    AccessLogger()\n    {\n    }\n\n    void initAndStart(const Json::Value &config) override;\n    void shutdown() override;\n\n  private:\n    trantor::AsyncFileLogger asyncFileLogger_;\n    int logIndex_{0};\n    bool useLocalTime_{true};\n    bool showMicroseconds_{true};\n    bool useCustomTimeFormat_{false};\n    std::string timeFormat_;\n    static bool useRealIp_;\n    std::regex exemptRegex_;\n    bool regexFlag_{false};\n\n    using LogFunction = std::function<void(trantor::LogStream &,\n                                           const drogon::HttpRequestPtr &,\n                                           const drogon::HttpResponsePtr &)>;\n    std::vector<LogFunction> logFunctions_;\n    void logging(trantor::LogStream &stream,\n                 const drogon::HttpRequestPtr &req,\n                 const drogon::HttpResponsePtr &resp);\n    void createLogFunctions(std::string format);\n    LogFunction newLogFunction(const std::string &placeholder);\n    std::map<std::string, LogFunction> logFunctionMap_;\n    //$request_path\n    static void outputReqPath(trantor::LogStream &,\n                              const drogon::HttpRequestPtr &,\n                              const drogon::HttpResponsePtr &);\n    //$request_query\n    static void outputReqQuery(trantor::LogStream &,\n                               const drogon::HttpRequestPtr &,\n                               const drogon::HttpResponsePtr &);\n    //$request_url\n    static void outputReqURL(trantor::LogStream &,\n                             const drogon::HttpRequestPtr &,\n                             const drogon::HttpResponsePtr &);\n    //$version\n    static void outputVersion(trantor::LogStream &,\n                              const drogon::HttpRequestPtr &,\n                              const drogon::HttpResponsePtr &);\n    //$request\n    static void outputReqLine(trantor::LogStream &,\n                              const drogon::HttpRequestPtr &,\n                              const drogon::HttpResponsePtr &);\n    //$date\n    void outputDate(trantor::LogStream &,\n                    const drogon::HttpRequestPtr &,\n                    const drogon::HttpResponsePtr &) const;\n    //$request_date\n    void outputReqDate(trantor::LogStream &,\n                       const drogon::HttpRequestPtr &,\n                       const drogon::HttpResponsePtr &) const;\n    //$remote_addr\n    static void outputRemoteAddr(trantor::LogStream &,\n                                 const drogon::HttpRequestPtr &,\n                                 const drogon::HttpResponsePtr &);\n    //$local_addr\n    static void outputLocalAddr(trantor::LogStream &,\n                                const drogon::HttpRequestPtr &,\n                                const drogon::HttpResponsePtr &);\n    //$request_len $body_bytes_received\n    static void outputReqLength(trantor::LogStream &,\n                                const drogon::HttpRequestPtr &,\n                                const drogon::HttpResponsePtr &);\n    //$response_len $body_bytes_sent\n    static void outputRespLength(trantor::LogStream &,\n                                 const drogon::HttpRequestPtr &,\n                                 const drogon::HttpResponsePtr &);\n    //$method\n    static void outputMethod(trantor::LogStream &,\n                             const drogon::HttpRequestPtr &,\n                             const drogon::HttpResponsePtr &);\n    //$thread\n    static void outputThreadNumber(trantor::LogStream &,\n                                   const drogon::HttpRequestPtr &,\n                                   const drogon::HttpResponsePtr &);\n    //$http_[header_name]\n    static void outputReqHeader(trantor::LogStream &stream,\n                                const drogon::HttpRequestPtr &req,\n                                const std::string &headerName);\n    //$cookie_[cookie_name]\n    static void outputReqCookie(trantor::LogStream &stream,\n                                const drogon::HttpRequestPtr &req,\n                                const std::string &cookie);\n    //$upstream_http_[header_name]\n    static void outputRespHeader(trantor::LogStream &stream,\n                                 const drogon::HttpResponsePtr &resp,\n                                 const std::string &headerName);\n    //$status\n    static void outputStatusString(trantor::LogStream &,\n                                   const drogon::HttpRequestPtr &,\n                                   const drogon::HttpResponsePtr &);\n    //$status_code\n    static void outputStatusCode(trantor::LogStream &,\n                                 const drogon::HttpRequestPtr &,\n                                 const drogon::HttpResponsePtr &);\n    //$processing_time\n    static void outputProcessingTime(trantor::LogStream &,\n                                     const drogon::HttpRequestPtr &,\n                                     const drogon::HttpResponsePtr &);\n    //$upstream_http_content-type $upstream_http_content_type\n    static void outputRespContentType(trantor::LogStream &,\n                                      const drogon::HttpRequestPtr &,\n                                      const drogon::HttpResponsePtr &);\n};\n}  // namespace plugin\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/plugins/GlobalFilters.h",
    "content": "#pragma once\n\n#include <drogon/plugins/Plugin.h>\n#include <regex>\n#include <vector>\n#include <memory>\n#include <drogon/HttpFilter.h>\n\nnamespace drogon\n{\nnamespace plugin\n{\n/**\n * @brief This plugin is used to add global filters to all HTTP requests.\n * The json configuration is as follows:\n *\n * @code\n   {\n        \"name\": \"drogon::plugin::GlobalFilters\",\n        \"dependencies\": [],\n        \"config\": {\n            // filters: the list of global filter names.\n            \"filters\": [\n                \"FilterName1\", \"FilterName2\",...\n            ],\n            // exempt: exempt must be a string or string array, regular\n expressions for\n            // URLs that don't have to be filtered.\n            \"exempt\": [\n                \"^/static/.*\\\\.css\", \"^/images/.*\",...\n            ]\n        }\n   }\n   @endcode\n *\n */\nclass DROGON_EXPORT GlobalFilters\n    : public drogon::Plugin<GlobalFilters>,\n      public std::enable_shared_from_this<GlobalFilters>\n{\n  public:\n    GlobalFilters()\n    {\n    }\n\n    void initAndStart(const Json::Value &config) override;\n    void shutdown() override;\n\n  private:\n    std::vector<std::shared_ptr<drogon::HttpFilterBase>> filters_;\n    std::regex exemptPegex_;\n    bool regexFlag_{false};\n};\n}  // namespace plugin\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/plugins/Hodor.h",
    "content": "/**\n *  @file Hodor.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n#include <drogon/RateLimiter.h>\n#include <drogon/plugins/Plugin.h>\n#include <drogon/plugins/RealIpResolver.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/CacheMap.h>\n#include <regex>\n#include <optional>\n\nnamespace drogon\n{\nnamespace plugin\n{\n/**\n * @brief The Hodor plugin implements a global rate limiter that limits the\n * number of requests in a particular time unit.\n * The json configuration is as follows:\n *\n * @code\n  {\n     \"name\": \"drogon::plugin::Hodor\",\n     \"dependencies\": [],\n     \"config\": {\n        // The algorithm used to limit the number of requests.\n        // The default value is \"token_bucket\". other values are \"fixed_window\"\nor \"sliding_window\".\n        \"algorithm\": \"token_bucket\",\n        // a regular expression (for matching the path of a request) list for\nURLs that have to be limited. if the list is empty, all URLs are limited.\n        \"urls\": [\"^/api/.*\", ...],\n        // The time unit in seconds. the default value is 60.\n        \"time_unit\": 60,\n        // The maximum number of requests in a time unit. the default value 0\nmeans no limit.\n        \"capacity\": 0,\n        // The maximum number of requests in a time unit for a single IP. the\ndefault value 0 means no limit.\n        \"ip_capacity\": 0,\n        // The maximum number of requests in a time unit for a single user.\na function must be provided to the plugin to get the user id from the request.\nthe default value 0 means no limit.\n        \"user_capacity\": 0,\n        // Use the RealIpResolver plugin to get the real IP address of the\nrequest. if this option is true, the RealIpResolver plugin should be added to\nthe dependencies list. the default value is false.\n        \"use_real_ip_resolver\": false,\n        // Multiple threads mode: the default value is true. if this option is\ntrue, some mutexes are used for thread-safe.\n        \"multi_threads\": true,\n        // The message body of the response when the request is rejected.\n        \"rejection_message\": \"Too many requests\",\n        // In seconds, the minimum expiration time of the limiters for different\nIPs or users. the default value is 600.\n        \"limiter_expire_time\": 600,\n        \"sub_limits\": [\n            {\n                \"urls\": [\"^/api/1/.*\", ...],\n                \"capacity\": 0,\n                \"ip_capacity\": 0,\n                \"user_capacity\": 0\n            },...\n        ],\n        // Trusted proxy ip or cidr\n        \"trust_ips\": [\"127.0.0.1\", \"172.16.0.0/12\"],\n     }\n  }\n  @endcode\n *\n * Enable the plugin by adding the configuration to the list of plugins in the\n * configuration file.\n * */\nclass DROGON_EXPORT Hodor : public drogon::Plugin<Hodor>\n{\n  public:\n    Hodor()\n    {\n    }\n\n    void initAndStart(const Json::Value &config) override;\n    void shutdown() override;\n\n    /**\n     * @brief the method is used to set a function to get the user id from the\n     * request. users should call this method after calling the app().run()\n     * method. etc. use the beginning advice of AOP.\n     * */\n    void setUserIdGetter(\n        std::function<std::optional<std::string>(const HttpRequestPtr &)> func)\n    {\n        userIdGetter_ = std::move(func);\n    }\n\n    /**\n     * @brief the method is used to set a function to create the response when\n     * the rate limit is exceeded. users should call this method after calling\n     * the app().run() method. etc. use the beginning advice of AOP.\n     * */\n    void setRejectResponseFactory(\n        std::function<HttpResponsePtr(const HttpRequestPtr &)> func)\n    {\n        rejectResponseFactory_ = std::move(func);\n    }\n\n  private:\n    struct LimitStrategy\n    {\n        std::regex urlsRegex;\n        size_t capacity{0};\n        size_t ipCapacity{0};\n        size_t userCapacity{0};\n        bool regexFlag{false};\n        RateLimiterPtr globalLimiterPtr;\n        std::unique_ptr<CacheMap<std::string, RateLimiterPtr>> ipLimiterMapPtr;\n        std::unique_ptr<CacheMap<std::string, RateLimiterPtr>>\n            userLimiterMapPtr;\n    };\n\n    LimitStrategy makeLimitStrategy(const Json::Value &config);\n    std::vector<LimitStrategy> limitStrategies_;\n    RateLimiterType algorithm_{RateLimiterType::kTokenBucket};\n    std::chrono::duration<double> timeUnit_{1.0};\n    bool multiThreads_{true};\n    bool useRealIpResolver_{false};\n    size_t limiterExpireTime_{600};\n    std::function<std::optional<std::string>(const drogon::HttpRequestPtr &)>\n        userIdGetter_;\n    std::function<HttpResponsePtr(const drogon::HttpRequestPtr &)>\n        rejectResponseFactory_;\n\n    RealIpResolver::CIDRs trustCIDRs_;\n\n    void onHttpRequest(const drogon::HttpRequestPtr &,\n                       AdviceCallback &&,\n                       AdviceChainCallback &&);\n    bool checkLimit(const drogon::HttpRequestPtr &req,\n                    const LimitStrategy &strategy,\n                    const trantor::InetAddress &ip,\n                    const std::optional<std::string> &userId);\n    HttpResponsePtr rejectResponse_;\n};\n}  // namespace plugin\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/plugins/Plugin.h",
    "content": "/**\n *  @file Plugin.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include <json/json.h>\n#include <memory>\n#include <trantor/utils/Logger.h>\n#include <trantor/utils/NonCopyable.h>\n\nnamespace drogon\n{\nenum class PluginStatus\n{\n    None,\n    Initializing,\n    Initialized\n};\n\n/**\n * @brief The abstract base class for plugins.\n *\n */\nclass DROGON_EXPORT PluginBase : public virtual DrObjectBase,\n                                 public trantor::NonCopyable\n{\n  public:\n    /// This method must be called by drogon.\n    void initialize()\n    {\n        if (status_ == PluginStatus::None)\n        {\n            status_ = PluginStatus::Initializing;\n            for (auto dependency : dependencies_)\n            {\n                dependency->initialize();\n            }\n            initAndStart(config_);\n            status_ = PluginStatus::Initialized;\n            if (initializedCallback_)\n                initializedCallback_(this);\n        }\n        else if (status_ == PluginStatus::Initialized)\n        {\n            // Do nothing;\n        }\n        else\n        {\n            LOG_FATAL << \"There are a circular dependency within plugins.\";\n            abort();\n        }\n    }\n\n    /// This method must be called by drogon to initialize and start the plugin.\n    /// It must be implemented by the user.\n    virtual void initAndStart(const Json::Value &config) = 0;\n\n    /// This method must be called by drogon to shutdown the plugin.\n    /// It must be implemented by the user.\n    virtual void shutdown() = 0;\n\n    virtual ~PluginBase()\n    {\n    }\n\n  protected:\n    PluginBase()\n    {\n    }\n\n  private:\n    PluginStatus status_{PluginStatus::None};\n    friend class PluginsManager;\n\n    void setConfig(const Json::Value &config)\n    {\n        config_ = config;\n    }\n\n    void addDependency(PluginBase *dp)\n    {\n        dependencies_.push_back(dp);\n    }\n\n    void setInitializedCallback(const std::function<void(PluginBase *)> &cb)\n    {\n        initializedCallback_ = cb;\n    }\n\n    Json::Value config_;\n    std::vector<PluginBase *> dependencies_;\n    std::function<void(PluginBase *)> initializedCallback_;\n};\n\ntemplate <typename T>\nstruct IsPlugin\n{\n    using TYPE = std::remove_cv_t<typename std::remove_reference_t<T>>;\n\n    static int test(void *)\n    {\n        return 0;\n    }\n\n    static char test(PluginBase *)\n    {\n        return 0;\n    }\n\n    static constexpr bool value =\n        (sizeof(test((TYPE *)nullptr)) == sizeof(char));\n};\n\n/**\n * @brief The reflection base class for plugins.\n *\n * @tparam T The type of the implementation plugin classes.\n */\ntemplate <typename T>\nclass Plugin : public PluginBase, public DrObject<T>\n{\n  public:\n    virtual ~Plugin()\n    {\n    }\n\n  protected:\n    Plugin()\n    {\n    }\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/plugins/PromExporter.h",
    "content": "/**\n *  @file PromExporter.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <drogon/plugins/Plugin.h>\n#include <drogon/utils/monitoring/Registry.h>\n#include <drogon/utils/monitoring/Collector.h>\n#include <memory>\n#include <mutex>\n\nnamespace drogon\n{\nnamespace plugin\n{\n/**\n * @brief The PromExporter plugin implements a prometheus exporter.\n * The json configuration is as follows:\n * @code\n   {\n      \"name\": \"drogon::plugin::PromExporter\",\n      \"dependencies\": [],\n      \"config\": {\n         // The path of the metrics. the default value is \"/metrics\".\n         \"path\": \"/metrics\",\n         // The list of collectors.\n         \"collectors\":[\n            {\n               // The name of the collector.\n               \"name\": \"http_requests_total\",\n               // The help message of the collector.\n               \"help\": \"The total number of http requests\",\n               // The type of the collector. The default value is \"counter\".\n               // The other possible value is as following:\n               // \"gauge\", \"histogram\".\n               \"type\": \"counter\",\n               // The labels of the collector.\n               \"labels\": [\"method\", \"status\"]\n            }\n         ]\n      }\n    }\n    @endcode\n * */\nclass DROGON_EXPORT PromExporter\n    : public drogon::Plugin<PromExporter>,\n      public std::enable_shared_from_this<PromExporter>,\n      public drogon::monitoring::Registry\n{\n  public:\n    PromExporter()\n    {\n    }\n\n    void initAndStart(const Json::Value &config) override;\n\n    void shutdown() override\n    {\n    }\n\n    ~PromExporter() override\n    {\n    }\n\n    void registerCollector(\n        const std::shared_ptr<drogon::monitoring::CollectorBase> &collector)\n        override;\n\n    std::shared_ptr<drogon::monitoring::CollectorBase> getCollector(\n        const std::string &name) const noexcept(false);\n\n    template <typename T>\n    std::shared_ptr<drogon::monitoring::Collector<T>> getCollector(\n        const std::string &name) const\n    {\n        return std::dynamic_pointer_cast<drogon::monitoring::Collector<T>>(\n            getCollector(name));\n    }\n\n  private:\n    mutable std::mutex mutex_;\n    std::unordered_map<std::string,\n                       std::shared_ptr<drogon::monitoring::CollectorBase>>\n        collectors_;\n    std::string path_{\"/metrics\"};\n    std::string exportMetrics();\n};\n}  // namespace plugin\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/plugins/RealIpResolver.h",
    "content": "/**\n *\n *  RealIpResolver.h\n *\n */\n\n#pragma once\n\n#include <drogon/plugins/Plugin.h>\n#include <trantor/net/InetAddress.h>\n#include <drogon/HttpRequest.h>\n#include <vector>\n\nnamespace drogon\n{\nnamespace plugin\n{\n/**\n* @brief This plugin is used to resolve client real ip from HTTP request.\n* @note This plugin currently supports only ipv4 address or cidr.\n*\n* The json configuration is as follows:\n*\n* @code\n  {\n     \"name\": \"drogon::plugin::RealIpResolver\",\n     \"dependencies\": [],\n     \"config\": {\n        // Trusted proxy ip or cidr\n        \"trust_ips\": [\"127.0.0.1\", \"172.16.0.0/12\"],\n        // Which header to parse ip form. Default is x-forwarded-for\n        \"from_header\": \"x-forwarded-for\",\n        // The result will be inserted to HttpRequest attribute map with this\n        // key. Default is \"real-ip\"\n        \"attribute_key\": \"real-ip\"\n     }\n  }\n  @endcode\n*\n* Enable the plugin by adding the configuration to the list of plugins in the\n* configuration file.\n*\n*/\nclass DROGON_EXPORT RealIpResolver : public drogon::Plugin<RealIpResolver>\n{\n  public:\n    RealIpResolver()\n    {\n    }\n\n    void initAndStart(const Json::Value &config) override;\n    void shutdown() override;\n\n    static const trantor::InetAddress &GetRealAddr(\n        const drogon::HttpRequestPtr &req);\n\n  private:\n    const trantor::InetAddress &getRealAddr(\n        const drogon::HttpRequestPtr &req) const;\n\n    struct CIDR\n    {\n        explicit CIDR(const std::string &ipOrCidr);\n        in_addr_t addr_{0};\n        in_addr_t mask_{32};\n    };\n\n    using CIDRs = std::vector<CIDR>;\n    static bool matchCidr(const trantor::InetAddress &addr,\n                          const CIDRs &trustCIDRs);\n\n    friend class Hodor;\n    CIDRs trustCIDRs_;\n    std::string fromHeader_;\n    std::string attributeKey_;\n    bool useXForwardedFor_{false};\n};\n}  // namespace plugin\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/plugins/Redirector.h",
    "content": "/**\n *  @file Redirector.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/plugins/Plugin.h>\n#include <drogon/HttpRequest.h>\n#include <vector>\n\nnamespace drogon\n{\nnamespace plugin\n{\n/**\n * @brief The RedirectorHandler is a function object that can be registered to\n * the Redirector plugin. It is used to redirect requests to proper URLs. Users\n * can modify the protocol, host and path of the request. If a false value is\n * returned, the request will be considered as invalid and a 404 response will\n * be sent to the client.\n */\nusing RedirectorHandler =\n    std::function<bool(const drogon::HttpRequestPtr &,\n                       std::string &,  //\"http://\" or \"https://\"\n                       std::string &,  // host\n                       bool &)>;       // path changed or not\n/**\n * @brief The PathRewriteHandler is a function object that can be registered to\n * the Redirector plugin. It is used to rewrite the path of the request. The\n * Redirector plugin will call all registered PathRewriteHandlers in the order\n * of registration. If one or more handlers return true, the request will be\n * redirected to the new path.\n */\nusing PathRewriteHandler = std::function<bool(const drogon::HttpRequestPtr &)>;\n\n/**\n * @brief The ForwardHandler is a function object that can be registered to the\n * Redirector plugin. It is used to forward the request to next processing steps\n * in the framework. The Redirector plugin will call all registered\n * ForwardHandlers in the order of registration. Users can use this handler to\n * change the request path or any other part of the request.\n */\nusing ForwardHandler = std::function<void(const drogon::HttpRequestPtr &)>;\n\n/**\n * @brief This plugin is used to redirect requests to proper URLs. It is a\n * helper plugin for other plugins, e.g. SlashRemover.\n * Users can register a handler to this plugin to redirect requests. All\n * handlers will be called in the order of registration.\n * The json configuration is as follows:\n *\n * @code\n   {\n      \"name\": \"drogon::plugin::Redirector\",\n      \"dependencies\": [],\n      \"config\": {\n      }\n   }\n   @endcode\n *\n */\nclass DROGON_EXPORT Redirector : public drogon::Plugin<Redirector>,\n                                 public std::enable_shared_from_this<Redirector>\n{\n  public:\n    Redirector()\n    {\n    }\n\n    void initAndStart(const Json::Value &config) override;\n    void shutdown() override;\n\n    void registerRedirectHandler(RedirectorHandler &&handler)\n    {\n        handlers_.emplace_back(std::move(handler));\n    }\n\n    void registerRedirectHandler(const RedirectorHandler &handler)\n    {\n        handlers_.emplace_back(handler);\n    }\n\n    void registerPathRewriteHandler(PathRewriteHandler &&handler)\n    {\n        pathRewriteHandlers_.emplace_back(std::move(handler));\n    }\n\n    void registerPathRewriteHandler(const PathRewriteHandler &handler)\n    {\n        pathRewriteHandlers_.emplace_back(handler);\n    }\n\n    void registerForwardHandler(ForwardHandler &&handler)\n    {\n        forwardHandlers_.emplace_back(std::move(handler));\n    }\n\n    void registerForwardHandler(const ForwardHandler &handler)\n    {\n        forwardHandlers_.emplace_back(handler);\n    }\n\n  private:\n    std::vector<RedirectorHandler> handlers_;\n    std::vector<PathRewriteHandler> pathRewriteHandlers_;\n    std::vector<ForwardHandler> forwardHandlers_;\n};\n\n}  // namespace plugin\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/plugins/SecureSSLRedirector.h",
    "content": "/**\n *\n *  @file drogon_plugin_SecureSSLRedirector.h\n *\n */\n\n#pragma once\n#include <drogon/exports.h>\n#include <drogon/drogon_callbacks.h>\n#include <drogon/plugins/Plugin.h>\n#include <regex>\n#include <memory>\n\nnamespace drogon\n{\nnamespace plugin\n{\n/**\n * @brief This plugin is used to redirect all non-HTTPS requests to HTTPS\n * (except for those URLs matching a regular expression listed in\n * the 'ssl_redirect_exempt' list).\n *\n * The json configuration is as follows:\n *\n * @code\n   {\n      \"name\": \"drogon::plugin::SecureSSLRedirector\",\n      \"dependencies\": [\"drogon::plugin::Redirector\"],\n      \"config\": {\n            \"ssl_redirect_exempt\": [\"^/.*\\\\.jpg\", ...],\n            \"secure_ssl_host\": \"localhost:8849\"\n      }\n   }\n   @endcode\n *\n * ssl_redirect_exempt: must be a string or a string array, present a regular\n expression\n * (for matching the path of a request) or a regular expression list for URLs\n that don't\n * have to be redirected.\n *\n * secure_ssl_host: If this string is not empty, all SSL redirects\n * will be directed to this host rather than the originally-requested host.\n *\n * Enable the plugin by adding the configuration to the list of plugins in the\n * configuration file.\n *\n */\nclass DROGON_EXPORT SecureSSLRedirector\n    : public drogon::Plugin<SecureSSLRedirector>,\n      public std::enable_shared_from_this<SecureSSLRedirector>\n{\n  public:\n    SecureSSLRedirector()\n    {\n    }\n\n    /// This method must be called by drogon to initialize and start the plugin.\n    /// It must be implemented by the user.\n    void initAndStart(const Json::Value &config) override;\n\n    /// This method must be called by drogon to shutdown the plugin.\n    /// It must be implemented by the user.\n    void shutdown() override;\n\n  private:\n    bool redirectingAdvice(const HttpRequestPtr &,\n                           std::string &,\n                           std::string &) const;\n    bool redirectToSSL(const HttpRequestPtr &,\n                       std::string &,\n                       std::string &) const;\n\n    std::regex exemptRegex_;\n    bool regexFlag_{false};\n    std::string secureHost_;\n};\n\n}  // namespace plugin\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/plugins/SlashRemover.h",
    "content": "/**\n *  @file SlashRemover.h\n *  @author Mis1eader\n *\n *  Copyright 2023, Mis1eader.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"drogon/plugins/Plugin.h\"\n#include \"drogon/utils/FunctionTraits.h\"\n#include <json/value.h>\n\nnamespace drogon::plugin\n{\n/**\n * @brief The SlashRemover plugin redirects requests to proper paths if they\n * contain excessive slashes.\n * The json configuration is as follows:\n *\n * @code\n  {\n     \"name\": \"drogon::plugin::SlashRemover\",\n     \"dependencies\": [\"drogon::plugin::Redirector\"],\n     \"config\": {\n        // If true, it removes all trailing slashes, e.g.\n///home// -> ///home\n        \"remove_trailing_slashes\": true,\n        // If true, it removes all duplicate slashes, e.g.\n///home// -> /home/\n        \"remove_duplicate_slashes\": true,\n        // If true, redirects the request, otherwise forwards\ninternally.\n        \"redirect\": true\n     }\n  }\n  @endcode\n *\n * Enable the plugin by adding the configuration to the list of plugins in the\n * configuration file.\n * */\nclass DROGON_EXPORT SlashRemover : public drogon::Plugin<SlashRemover>\n{\n  public:\n    SlashRemover()\n    {\n    }\n\n    void initAndStart(const Json::Value &config) override;\n    void shutdown() override;\n\n  private:\n    bool trailingSlashes_{true}, duplicateSlashes_{true}, redirect_{true};\n};\n}  // namespace drogon::plugin\n"
  },
  {
    "path": "lib/inc/drogon/utils/FunctionTraits.h",
    "content": "/**\n *\n *  FunctionTraits.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/DrObject.h>\n#include <drogon/RequestStream.h>\n#include <functional>\n#include <memory>\n#include <tuple>\n#include <type_traits>\n\n#ifdef __cpp_impl_coroutine\n#include <drogon/utils/coroutine.h>\n#endif\n\nnamespace drogon\n{\nclass HttpRequest;\nclass HttpResponse;\nusing HttpRequestPtr = std::shared_ptr<HttpRequest>;\nusing HttpResponsePtr = std::shared_ptr<HttpResponse>;\n\nnamespace internal\n{\n#ifdef __cpp_impl_coroutine\ntemplate <typename T>\nusing resumable_type = is_resumable<T>;\n#else\ntemplate <typename T>\nstruct resumable_type : std::false_type\n{\n};\n#endif\n\ntemplate <typename>\nstruct FunctionTraits;\n\n//\n// Basic match, inherited by all other matches\n//\ntemplate <typename ReturnType, typename... Arguments>\nstruct FunctionTraits<ReturnType (*)(Arguments...)>\n{\n    using result_type = ReturnType;\n\n    template <std::size_t Index>\n    using argument =\n        typename std::tuple_element_t<Index, std::tuple<Arguments...>>;\n\n    static const std::size_t arity = sizeof...(Arguments);\n    using class_type = void;\n    using return_type = ReturnType;\n    static const bool isHTTPFunction = false;\n    static const bool isClassFunction = false;\n    static const bool isStreamHandler = false;\n    static const bool isDrObjectClass = false;\n    static const bool isCoroutine = false;\n\n    static const std::string name()\n    {\n        return std::string(\"Normal or Static Function\");\n    }\n};\n\n//\n// Match normal functions\n//\n\n// normal function for HTTP handling\ntemplate <typename ReturnType, typename... Arguments>\nstruct FunctionTraits<\n    ReturnType (*)(const HttpRequestPtr &req,\n                   std::function<void(const HttpResponsePtr &)> &&callback,\n                   Arguments...)> : FunctionTraits<ReturnType (*)(Arguments...)>\n{\n    static const bool isHTTPFunction = !resumable_type<ReturnType>::value;\n    static const bool isCoroutine = false;\n    using class_type = void;\n    using first_param_type = HttpRequestPtr;\n    using return_type = ReturnType;\n};\n\n// normal function with custom request object\ntemplate <typename T, typename ReturnType, typename... Arguments>\nstruct FunctionTraits<\n    ReturnType (*)(T &&customReq,\n                   std::function<void(const HttpResponsePtr &)> &&callback,\n                   Arguments...)> : FunctionTraits<ReturnType (*)(Arguments...)>\n{\n    static const bool isHTTPFunction = !resumable_type<ReturnType>::value;\n    static const bool isCoroutine = false;\n    using class_type = void;\n    using first_param_type = T;\n    using return_type = ReturnType;\n};\n\n// normal function with stream handler\ntemplate <typename ReturnType, typename... Arguments>\nstruct FunctionTraits<\n    ReturnType (*)(const HttpRequestPtr &req,\n                   RequestStreamPtr &&streamCtx,\n                   std::function<void(const HttpResponsePtr &)> &&callback,\n                   Arguments...)> : FunctionTraits<ReturnType (*)(Arguments...)>\n{\n    static const bool isHTTPFunction = !resumable_type<ReturnType>::value;\n    static const bool isCoroutine = false;\n    static const bool isStreamHandler = true;\n    using class_type = void;\n    using first_param_type = HttpRequestPtr;\n    using return_type = ReturnType;\n};\n\n//\n// Match functor,lambda,std::function... inherits normal function matches\n//\ntemplate <typename Function>\nstruct FunctionTraits\n    : public FunctionTraits<\n          decltype(&std::remove_reference_t<Function>::operator())>\n{\n    static const bool isClassFunction = false;\n    static const bool isDrObjectClass = false;\n    using class_type = void;\n\n    static const std::string name()\n    {\n        return std::string(\"Functor\");\n    }\n};\n\n//\n// Match class functions, inherits normal function matches\n//\n\n// class const method\ntemplate <typename ClassType, typename ReturnType, typename... Arguments>\nstruct FunctionTraits<ReturnType (ClassType::*)(Arguments...) const>\n    : FunctionTraits<ReturnType (*)(Arguments...)>\n{\n    static const bool isClassFunction = true;\n    static const bool isDrObjectClass =\n        std::is_base_of<DrObject<ClassType>, ClassType>::value;\n    using class_type = ClassType;\n\n    static const std::string name()\n    {\n        return std::string(\"Class Function\");\n    }\n};\n\n// class non-const method\ntemplate <typename ClassType, typename ReturnType, typename... Arguments>\nstruct FunctionTraits<ReturnType (ClassType::*)(Arguments...)>\n    : FunctionTraits<ReturnType (*)(Arguments...)>\n{\n    static const bool isClassFunction = true;\n    static const bool isDrObjectClass =\n        std::is_base_of<DrObject<ClassType>, ClassType>::value;\n    using class_type = ClassType;\n\n    static const std::string name()\n    {\n        return std::string(\"Class Function\");\n    }\n};\n\n//\n// Match coroutine functions\n//\n#ifdef __cpp_impl_coroutine\ntemplate <typename... Arguments>\nstruct FunctionTraits<\n    AsyncTask (*)(HttpRequestPtr req,\n                  std::function<void(const HttpResponsePtr &)> callback,\n                  Arguments...)> : FunctionTraits<AsyncTask (*)(Arguments...)>\n{\n    static const bool isHTTPFunction = true;\n    static const bool isCoroutine = true;\n    using class_type = void;\n    using first_param_type = HttpRequestPtr;\n    using return_type = AsyncTask;\n};\n\ntemplate <typename... Arguments>\nstruct FunctionTraits<\n    Task<> (*)(HttpRequestPtr req,\n               std::function<void(const HttpResponsePtr &)> callback,\n               Arguments...)> : FunctionTraits<AsyncTask (*)(Arguments...)>\n{\n    static const bool isHTTPFunction = true;\n    static const bool isCoroutine = true;\n    using class_type = void;\n    using first_param_type = HttpRequestPtr;\n    using return_type = Task<>;\n};\n\ntemplate <typename... Arguments>\nstruct FunctionTraits<Task<HttpResponsePtr> (*)(HttpRequestPtr req,\n                                                Arguments...)>\n    : FunctionTraits<AsyncTask (*)(Arguments...)>\n{\n    static const bool isHTTPFunction = true;\n    static const bool isCoroutine = true;\n    using class_type = void;\n    using first_param_type = HttpRequestPtr;\n    using return_type = Task<HttpResponsePtr>;\n};\n#endif\n\n//\n// Bad matches\n//\n\ntemplate <typename ReturnType, typename... Arguments>\nstruct FunctionTraits<\n    ReturnType (*)(HttpRequestPtr &req,\n                   std::function<void(const HttpResponsePtr &)> &&callback,\n                   Arguments...)> : FunctionTraits<ReturnType (*)(Arguments...)>\n{\n    static const bool isHTTPFunction = false;\n    using class_type = void;\n};\n\ntemplate <typename ReturnType, typename... Arguments>\nstruct FunctionTraits<\n    ReturnType (*)(HttpRequestPtr &&req,\n                   std::function<void(const HttpResponsePtr &)> &&callback,\n                   Arguments...)> : FunctionTraits<ReturnType (*)(Arguments...)>\n{\n    static const bool isHTTPFunction = false;\n    using class_type = void;\n};\n\n}  // namespace internal\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/utils/HttpConstraint.h",
    "content": "/**\n *\n *  HttpConstraint.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/HttpTypes.h>\n#include <string>\n\nnamespace drogon\n{\nnamespace internal\n{\nenum class ConstraintType\n{\n    None,\n    HttpMethod,\n    HttpMiddleware\n};\n\nclass HttpConstraint\n{\n  public:\n    HttpConstraint(HttpMethod method)\n        : type_(ConstraintType::HttpMethod), method_(method)\n    {\n    }\n\n    HttpConstraint(std::string middlewareName)\n        : type_(ConstraintType::HttpMiddleware),\n          middlewareName_(std::move(middlewareName))\n    {\n    }\n\n    HttpConstraint(const char *middlewareName)\n        : type_(ConstraintType::HttpMiddleware), middlewareName_(middlewareName)\n    {\n    }\n\n    ConstraintType type() const\n    {\n        return type_;\n    }\n\n    HttpMethod getHttpMethod() const\n    {\n        return method_;\n    }\n\n    const std::string &getMiddlewareName() const\n    {\n        return middlewareName_;\n    }\n\n  private:\n    ConstraintType type_{ConstraintType::None};\n    HttpMethod method_{HttpMethod::Invalid};\n    std::string middlewareName_;\n};\n}  // namespace internal\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/utils/OStringStream.h",
    "content": "/**\n *\n *  OStringStream.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <string>\n#include <sstream>\n#include <string_view>\n\nnamespace drogon\n{\nnamespace internal\n{\ntemplate <typename T, typename = void>\nstruct CanConvertToString : std::false_type\n{\n};\n\ntemplate <typename T>\nstruct CanConvertToString<\n    T,\n    std::void_t<decltype(std::to_string(std::declval<T>()))>> : std::true_type\n{\n};\n}  // namespace internal\n\nclass OStringStream\n{\n  public:\n    OStringStream() = default;\n\n    void reserve(size_t size)\n    {\n        buffer_.reserve(size);\n    }\n\n    template <typename T>\n    OStringStream &operator<<(T &&value)\n    {\n        if constexpr (internal::CanConvertToString<T>::value)\n        {\n            buffer_.append(std::to_string(std::forward<T>(value)));\n            return *this;\n        }\n        else\n        {\n            std::stringstream ss;\n            ss << std::forward<T>(value);\n            buffer_.append(ss.str());\n            return *this;\n        }\n    }\n\n    template <int N>\n    OStringStream &operator<<(const char (&buf)[N])\n    {\n        buffer_.append(buf, N - 1);\n        return *this;\n    }\n\n    OStringStream &operator<<(const std::string_view &str)\n    {\n        buffer_.append(str.data(), str.length());\n        return *this;\n    }\n\n    OStringStream &operator<<(std::string_view &&str)\n    {\n        buffer_.append(str.data(), str.length());\n        return *this;\n    }\n\n    OStringStream &operator<<(const std::string &str)\n    {\n        buffer_.append(str);\n        return *this;\n    }\n\n    OStringStream &operator<<(std::string &&str)\n    {\n        buffer_.append(std::move(str));\n        return *this;\n    }\n\n    OStringStream &operator<<(const double &d)\n    {\n        std::stringstream ss;\n        ss << d;\n        buffer_.append(ss.str());\n        return *this;\n    }\n\n    OStringStream &operator<<(const float &f)\n    {\n        std::stringstream ss;\n        ss << f;\n        buffer_.append(ss.str());\n        return *this;\n    }\n\n    OStringStream &operator<<(double &&d)\n    {\n        std::stringstream ss;\n        ss << d;\n        buffer_.append(ss.str());\n        return *this;\n    }\n\n    OStringStream &operator<<(float &&f)\n    {\n        std::stringstream ss;\n        ss << f;\n        buffer_.append(ss.str());\n        return *this;\n    }\n\n    std::string &str()\n    {\n        return buffer_;\n    }\n\n    const std::string &str() const\n    {\n        return buffer_;\n    }\n\n  private:\n    std::string buffer_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/utils/Utilities.h",
    "content": "/**\n *\n *  @file Utilities.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Funcs.h>\n#include <trantor/utils/Utilities.h>\n#include <trantor/utils/LogStream.h>\n#include <memory>\n#include <string>\n#include <vector>\n#include <set>\n#include <limits>\n#include <sstream>\n#include <algorithm>\n#include <filesystem>\n#include <string_view>\n#include <unordered_map>\n#include <type_traits>\n#ifdef _WIN32\n#include <time.h>\nDROGON_EXPORT char *strptime(const char *s, const char *f, struct tm *tm);\nDROGON_EXPORT time_t timegm(struct tm *tm);\n#endif\nnamespace drogon\n{\nnamespace internal\n{\ntemplate <typename T, typename = void>\nstruct CanConvertFromStringStream : std::false_type\n{\n};\n\ntemplate <typename T>\nstruct CanConvertFromStringStream<\n    T,\n    std::void_t<decltype(std::declval<std::stringstream &>() >>\n                         std::declval<T &>())>> : std::true_type\n{\n};\n\ntemplate <typename T>\nstruct CanConstructFromString : std::is_constructible<T, std::string>\n{\n};\n\ntemplate <typename T>\nstruct CanConvertFromString : std::is_assignable<T &, std::string>\n{\n};\n\n}  // namespace internal\n\n/**\n * @brief Get the HTTP messages corresponding to the HTTP status codes\n *\n * @param code HTTP status code\n *\n * @return the corresponding message\n */\nDROGON_EXPORT const std::string_view &statusCodeToString(int code);\n\nnamespace utils\n{\n/// Determine if the string is an integer\nDROGON_EXPORT bool isInteger(std::string_view str);\n\n/// Determine if the string is base64 encoded\nDROGON_EXPORT bool isBase64(std::string_view str);\n\n/// Generate random a string\n/**\n * @param length The string length\n * The returned string consists of uppercase and lowercase letters and numbers\n */\nDROGON_EXPORT std::string genRandomString(int length);\n\n/// Convert a binary string to hex format\nDROGON_EXPORT std::string binaryStringToHex(const unsigned char *ptr,\n                                            size_t length,\n                                            bool lowerCase = false);\n\n/// Get a binary string from hexadecimal format\nDROGON_EXPORT std::string hexToBinaryString(const char *ptr, size_t length);\n\n/// Get a binary vector from hexadecimal format\nDROGON_EXPORT std::vector<char> hexToBinaryVector(const char *ptr,\n                                                  size_t length);\n\nDROGON_EXPORT void binaryStringToHex(const char *ptr,\n                                     size_t length,\n                                     char *out,\n                                     bool lowerCase = false);\n\n/// Split the string into multiple separated strings.\n/**\n * @param str string to split\n * @param separator element separator\n * @param acceptEmptyString if true, empty strings are accepted in the\n * result, for example, splitting the \",1,2,,3,\" by \",\" produces\n * [\"\",\"1\",\"2\",\"\",\"3\",\"\"]\n */\ninline std::vector<std::string> splitString(const std::string &str,\n                                            const std::string &separator,\n                                            bool acceptEmptyString = false)\n{\n    return trantor::splitString(str, separator, acceptEmptyString);\n}\n\nDROGON_EXPORT std::set<std::string> splitStringToSet(\n    const std::string &str,\n    const std::string &separator);\n\n/// Get UUID string.\nDROGON_EXPORT std::string getUuid(bool lowercase = true);\n\n/// Get the encoded length of base64.\nconstexpr size_t base64EncodedLength(size_t in_len, bool padded = true)\n{\n    return padded ? ((in_len + 3 - 1) / 3) * 4 : (in_len * 8 + 6 - 1) / 6;\n}\n\n/// Encode the string to base64 format.\nDROGON_EXPORT void base64Encode(const unsigned char *bytesToEncode,\n                                size_t inLen,\n                                unsigned char *outputBuffer,\n                                bool urlSafe = false,\n                                bool padded = true);\n\n/// Encode the string to base64 format.\ninline std::string base64Encode(const unsigned char *bytesToEncode,\n                                size_t inLen,\n                                bool urlSafe = false,\n                                bool padded = true)\n{\n    std::string ret;\n    ret.resize(base64EncodedLength(inLen, padded));\n    base64Encode(\n        bytesToEncode, inLen, (unsigned char *)ret.data(), urlSafe, padded);\n    return ret;\n}\n\n/// Encode the string to base64 format.\ninline std::string base64Encode(std::string_view data,\n                                bool urlSafe = false,\n                                bool padded = true)\n{\n    return base64Encode((unsigned char *)data.data(),\n                        data.size(),\n                        urlSafe,\n                        padded);\n}\n\n/// Encode the string to base64 format with no padding.\ninline void base64EncodeUnpadded(const unsigned char *bytesToEncode,\n                                 size_t inLen,\n                                 unsigned char *outputBuffer,\n                                 bool urlSafe = false)\n{\n    base64Encode(bytesToEncode, inLen, outputBuffer, urlSafe, false);\n}\n\n/// Encode the string to base64 format with no padding.\ninline std::string base64EncodeUnpadded(const unsigned char *bytesToEncode,\n                                        size_t inLen,\n                                        bool urlSafe = false)\n{\n    return base64Encode(bytesToEncode, inLen, urlSafe, false);\n}\n\n/// Encode the string to base64 format with no padding.\ninline std::string base64EncodeUnpadded(std::string_view data,\n                                        bool urlSafe = false)\n{\n    return base64Encode(data, urlSafe, false);\n}\n\n/// Get the decoded length of base64.\nconstexpr size_t base64DecodedLength(size_t inLen)\n{\n    return (inLen * 3) / 4;\n}\n\n/// Decode the base64 format string.\n/// Return the number of bytes written.\nDROGON_EXPORT size_t base64Decode(const char *encodedString,\n                                  size_t inLen,\n                                  unsigned char *outputBuffer);\n\n/// Decode the base64 format string.\ninline std::string base64Decode(std::string_view encodedString)\n{\n    auto inLen = encodedString.size();\n    std::string ret;\n    ret.resize(base64DecodedLength(inLen));\n    ret.resize(\n        base64Decode(encodedString.data(), inLen, (unsigned char *)ret.data()));\n    return ret;\n}\n\nDROGON_EXPORT std::vector<char> base64DecodeToVector(\n    std::string_view encodedString);\n\n/// Check if the string need decoding\nDROGON_EXPORT bool needUrlDecoding(const char *begin, const char *end);\n\n/// Decode from or encode to the URL format string\nDROGON_EXPORT std::string urlDecode(const char *begin, const char *end);\n\ninline std::string urlDecode(const std::string &szToDecode)\n{\n    auto begin = szToDecode.data();\n    return urlDecode(begin, begin + szToDecode.length());\n}\n\ninline std::string urlDecode(const std::string_view &szToDecode)\n{\n    auto begin = szToDecode.data();\n    return urlDecode(begin, begin + szToDecode.length());\n}\n\nDROGON_EXPORT std::string urlEncode(const std::string &);\nDROGON_EXPORT std::string urlEncodeComponent(const std::string &);\n\n/// Get the MD5 digest of a string.\nDROGON_EXPORT std::string getMd5(const char *data, const size_t dataLen);\n\ninline std::string getMd5(const std::string &originalString)\n{\n    return getMd5(originalString.data(), originalString.length());\n}\n\nDROGON_EXPORT std::string getSha1(const char *data, const size_t dataLen);\n\ninline std::string getSha1(const std::string &originalString)\n{\n    return getSha1(originalString.data(), originalString.length());\n}\n\nDROGON_EXPORT std::string getSha256(const char *data, const size_t dataLen);\n\ninline std::string getSha256(const std::string &originalString)\n{\n    return getSha256(originalString.data(), originalString.length());\n}\n\nDROGON_EXPORT std::string getSha3(const char *data, const size_t dataLen);\n\ninline std::string getSha3(const std::string &originalString)\n{\n    return getSha3(originalString.data(), originalString.length());\n}\n\nDROGON_EXPORT std::string getBlake2b(const char *data, const size_t dataLen);\n\ninline std::string getBlake2b(const std::string &originalString)\n{\n    return getBlake2b(originalString.data(), originalString.length());\n}\n\n/// Compress or decompress data using gzip lib.\n/**\n * @param data the input data\n * @param ndata the input data length\n */\nDROGON_EXPORT std::string gzipCompress(const char *data, const size_t ndata);\nDROGON_EXPORT std::string gzipDecompress(const char *data, const size_t ndata);\n\n/// Compress or decompress data using brotli lib.\n/**\n * @param data the input data\n * @param ndata the input data length\n */\nDROGON_EXPORT std::string brotliCompress(const char *data, const size_t ndata);\nDROGON_EXPORT std::string brotliDecompress(const char *data,\n                                           const size_t ndata);\n\n/// Get the http full date string\n/**\n * rfc2616-3.3.1\n * Full Date format(RFC 822)\n * like this:\n * @code\n   Sun, 06 Nov 1994 08:49:37 GMT\n   Wed, 12 Sep 2018 09:22:40 GMT\n   @endcode\n */\nDROGON_EXPORT char *getHttpFullDate(\n    const trantor::Date &date = trantor::Date::now());\n\nDROGON_EXPORT const std::string &getHttpFullDateStr(\n    const trantor::Date &date = trantor::Date::now());\n\nDROGON_EXPORT void dateToCustomFormattedString(const std::string &fmtStr,\n                                               std::string &str,\n                                               const trantor::Date &date);\n/// Get the trantor::Date object according to the http full date string\n/**\n * Returns trantor::Date(std::numeric_limits<int64_t>::max()) upon failure.\n */\nDROGON_EXPORT trantor::Date getHttpDate(const std::string &httpFullDateString);\n\n/// Get a formatted string\nDROGON_EXPORT std::string formattedString(const char *format, ...);\n\n/// Recursively create a file system path\n/**\n * Return 0 or -1 on success or failure.\n */\nDROGON_EXPORT int createPath(const std::string &path);\n\n/**\n * @details Convert a wide string path with arbitrary directory separators\n * to a UTF-8 portable path for use with trantor.\n *\n * This is a helper, mainly for Windows and multi-platform projects.\n *\n * @note On Windows, backslash directory separators are converted to slash to\n * keep portable paths.\n *\n * @remarks On other OSes, backslashes are not converted to slash, since they\n * are valid characters for directory/file names.\n *\n * @param strPath Wide string path.\n *\n * @return std::string UTF-8 path, with slash directory separator.\n */\ninline std::string fromWidePath(const std::wstring &strPath)\n{\n    return trantor::utils::fromWidePath(strPath);\n}\n\n/**\n * @details Convert a UTF-8 path with arbitrary directory separator to a wide\n * string path.\n *\n * This is a helper, mainly for Windows and multi-platform projects.\n *\n * @note On Windows, slash directory separators are converted to backslash.\n * Although it accepts both slash and backslash as directory separator in its\n * API, it is better to stick to its standard.\n\n * @remarks On other OSes, slashes are not converted to backslashes, since they\n * are not interpreted as directory separators and are valid characters for\n * directory/file names.\n *\n * @param strUtf8Path Ascii path considered as being UTF-8.\n *\n * @return std::wstring path with, on windows, standard backslash directory\n * separator to stick to its standard.\n */\ninline std::wstring toWidePath(const std::string &strUtf8Path)\n{\n    return trantor::utils::toWidePath(strUtf8Path);\n}\n\n/**\n * @brief Convert a generic (UTF-8) path with to an OS native path.\n * @details This is a helper, mainly for Windows and multi-platform projects.\n *\n * On Windows, slash directory separators are converted to backslash, and a\n * wide string is returned.\n *\n * On other OSes, returns an UTF-8 string _without_ altering the directory\n * separators.\n *\n * @param strPath Wide string or UTF-8 path.\n *\n * @return An OS path, suitable for use with the OS API.\n */\n#if defined(_WIN32) && !defined(__MINGW32__)\ninline std::wstring toNativePath(const std::string &strPath)\n{\n    return trantor::utils::toNativePath(strPath);\n}\n\ninline const std::wstring &toNativePath(const std::wstring &strPath)\n{\n    return trantor::utils::toNativePath(strPath);\n}\n#else   // __WIN32\ninline const std::string &toNativePath(const std::string &strPath)\n{\n    return trantor::utils::toNativePath(strPath);\n}\n\ninline std::string toNativePath(const std::wstring &strPath)\n{\n    return trantor::utils::toNativePath(strPath);\n}\n#endif  // _WIN32\n/**\n * @brief Convert a OS native path (wide string on Windows) to a generic UTF-8\n * path.\n * @details This is a helper, mainly for Windows and multi-platform projects.\n *\n * On Windows, backslash directory separators are converted to slash, and a\n * a UTF-8 string is returned, suitable for libraries that supports UTF-8 paths\n * like OpenSSL or drogon.\n *\n * On other OSes, returns an UTF-8 string without altering the directory\n * separators (backslashes are *NOT* replaced with slashes, since they\n * are valid characters for directory/file names).\n *\n * @param strPath Wide string or UTF-8 path.\n *\n * @return A generic path.\n */\ninline const std::string &fromNativePath(const std::string &strPath)\n{\n    return trantor::utils::fromNativePath(strPath);\n}\n\n// Convert on all systems\ninline std::string fromNativePath(const std::wstring &strPath)\n{\n    return trantor::utils::fromNativePath(strPath);\n}\n\n/// Replace all occurrences of from to to inplace\n/**\n * @param s string to alter\n * @param from string to replace\n * @param to string to replace with\n */\nDROGON_EXPORT void replaceAll(std::string &s,\n                              const std::string &from,\n                              const std::string &to);\n\n/**\n * @brief Generates cryptographically secure random bytes.\n *\n * @param ptr the pointer which the random bytes are stored to\n * @param size number of bytes to generate\n *\n * @return true if generation is successful. False otherwise\n */\nDROGON_EXPORT bool secureRandomBytes(void *ptr, size_t size);\n\n/**\n * @brief Generates cryptographically secure random string.\n *\n * @param size number of characters to generate\n *\n * @return the random string\n */\nDROGON_EXPORT std::string secureRandomString(size_t size);\n\ntemplate <typename T>\nT fromString(const std::string &p) noexcept(false)\n{\n    if constexpr (std::is_integral<T>::value && std::is_signed<T>::value)\n    {\n        std::size_t pos;\n        auto v = std::stoll(p, &pos);\n        // throw if the whole string could not be parsed\n        // (\"1a\" should not return 1)\n        if (pos != p.size())\n            throw std::invalid_argument(\"Invalid value\");\n        if ((v < static_cast<long long>((std::numeric_limits<T>::min)())) ||\n            (v > static_cast<long long>((std::numeric_limits<T>::max)())))\n            throw std::out_of_range(\"Value out of range\");\n        return static_cast<T>(v);\n    }\n    else if constexpr (std::is_integral<T>::value &&\n                       (!std::is_signed<T>::value))\n    {\n        std::size_t pos;\n        auto v = std::stoull(p, &pos);\n        // throw if the whole string could not be parsed\n        // (\"1a\" should not return 1)\n        if (pos != p.size())\n            throw std::invalid_argument(\"Invalid value\");\n        if (v >\n            static_cast<unsigned long long>((std::numeric_limits<T>::max)()))\n            throw std::out_of_range(\"Value out of range\");\n        return static_cast<T>(v);\n    }\n    else if constexpr (std::is_floating_point<T>::value)\n    {\n        std::size_t pos;\n        auto v = std::stold(p, &pos);\n        // throw if the whole string could not be parsed\n        // (\"1a\" should not return 1)\n        if (pos != p.size())\n            throw std::invalid_argument(\"Invalid value\");\n        if ((v < static_cast<long double>((std::numeric_limits<T>::min)())) ||\n            (v > static_cast<long double>((std::numeric_limits<T>::max)())))\n            throw std::out_of_range(\"Value out of range\");\n        return static_cast<T>(v);\n    }\n    else if constexpr (internal::CanConvertFromStringStream<T>::value)\n    {\n        T value{};\n        if (!p.empty())\n        {\n            std::stringstream ss(p);\n            // must except in case of invalid value, not return a default value\n            // (else it returns 0 for integers if the string is empty or\n            // non-numeric)\n            ss.exceptions(std::ios_base::failbit);\n            ss >> value;\n            // throw if the whole string could not be parsed\n            // (\"1a\" should not return 1)\n            if (!ss.eof())\n                std::runtime_error(\"Bad type conversion\");\n        }\n        return value;\n    }\n    else\n    {\n        throw std::runtime_error(\"Bad type conversion\");\n    }\n}\n\ntemplate <>\ninline std::string fromString<std::string>(const std::string &p) noexcept(false)\n{\n    return p;\n}\n\ntemplate <>\ninline bool fromString<bool>(const std::string &p) noexcept(false)\n{\n    if (!p.empty() && std::all_of(p.begin(), p.end(), [](unsigned char c) {\n            return std::isdigit(c);\n        }))\n        return (std::stoll(p) != 0);\n    std::string l{p};\n    std::transform(p.begin(), p.end(), l.begin(), [](unsigned char c) {\n        return (char)tolower(c);\n    });\n    if (l == \"true\")\n    {\n        return true;\n    }\n    else if (l == \"false\")\n    {\n        return false;\n    }\n    throw std::runtime_error(\"Can't convert from string '\" + p + \"' to bool\");\n}\n\nDROGON_EXPORT bool supportsTls() noexcept;\n\nnamespace internal\n{\nDROGON_EXPORT extern const size_t fixedRandomNumber;\n\nstruct SafeStringHash\n{\n    size_t operator()(const std::string &str) const\n    {\n        const size_t A = 6665339;\n        const size_t B = 2534641;\n        size_t h = fixedRandomNumber;\n        for (char ch : str)\n            h = (h * A) ^ (ch * B);\n        return h;\n    }\n};\n}  // namespace internal\n}  // namespace utils\n\ntemplate <typename T>\nusing SafeStringMap =\n    std::unordered_map<std::string, T, utils::internal::SafeStringHash>;\n}  // namespace drogon\n\nnamespace trantor\n{\ninline LogStream &operator<<(LogStream &ls, const std::string_view &v)\n{\n    if (!v.empty())\n        ls.append(v.data(), v.length());\n    return ls;\n}\n\ninline LogStream &operator<<(LogStream &ls, const std::filesystem::path &p)\n{\n    return ls << p.string();\n}\n}  // namespace trantor\n"
  },
  {
    "path": "lib/inc/drogon/utils/coroutine.h",
    "content": "/**\n *\n *  @file coroutine.h\n *  @author Martin Chang\n *\n *  Copyright 2021, Martin Chang.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include <trantor/utils/NonCopyable.h>\n#include <trantor/net/EventLoop.h>\n#include <trantor/utils/Logger.h>\n#include <algorithm>\n#include <atomic>\n#include <cassert>\n#include <condition_variable>\n#include <coroutine>\n#include <exception>\n#include <future>\n#include <mutex>\n#include <type_traits>\n#include <optional>\n\nnamespace drogon\n{\nnamespace internal\n{\ntemplate <typename T>\nauto getAwaiterImpl(T &&value) noexcept(\n    noexcept(static_cast<T &&>(value).operator co_await()))\n    -> decltype(static_cast<T &&>(value).operator co_await())\n{\n    return static_cast<T &&>(value).operator co_await();\n}\n\ntemplate <typename T>\nauto getAwaiterImpl(T &&value) noexcept(\n    noexcept(operator co_await(static_cast<T &&>(value))))\n    -> decltype(operator co_await(static_cast<T &&>(value)))\n{\n    return operator co_await(static_cast<T &&>(value));\n}\n\ntemplate <typename T>\nauto getAwaiter(T &&value) noexcept(\n    noexcept(getAwaiterImpl(static_cast<T &&>(value))))\n    -> decltype(getAwaiterImpl(static_cast<T &&>(value)))\n{\n    return getAwaiterImpl(static_cast<T &&>(value));\n}\n\ntemplate <typename T>\nusing void_to_false_t =\n    std::conditional_t<std::is_same_v<T, void>, std::false_type, T>;\n\n}  // end namespace internal\n\ntemplate <typename T>\nstruct await_result\n{\n    using awaiter_t = decltype(internal::getAwaiter(std::declval<T>()));\n    using type = decltype(std::declval<awaiter_t>().await_resume());\n};\n\ntemplate <typename T>\nusing await_result_t = typename await_result<T>::type;\n\ntemplate <typename T, typename = std::void_t<>>\nstruct is_awaitable : std::false_type\n{\n};\n\ntemplate <typename T>\nstruct is_awaitable<\n    T,\n    std::void_t<decltype(internal::getAwaiter(std::declval<T>()))>>\n    : std::true_type\n{\n};\n\ntemplate <typename T>\nconstexpr bool is_awaitable_v = is_awaitable<T>::value;\n\n/**\n * @struct final_awaiter\n * @brief An awaiter for `Task::promise_type::final_suspend()`. Transfer\n * execution back to the coroutine who is co_awaiting this Task.\n */\nstruct final_awaiter\n{\n    bool await_ready() noexcept\n    {\n        return false;\n    }\n\n    template <typename T>\n    auto await_suspend(std::coroutine_handle<T> handle) noexcept\n    {\n        return handle.promise().continuation_;\n    }\n\n    void await_resume() noexcept\n    {\n    }\n};\n\n/**\n * @struct task_awaiter\n * @brief Convert Task to an awaiter when it is co_awaited.\n * Following things will happen:\n * 1. Suspend current coroutine\n * 2. Set current coroutine as continuation of this Task\n * 3. Transfer execution to the co_awaited Task\n */\ntemplate <typename Promise>\nstruct task_awaiter\n{\n    using handle_type = std::coroutine_handle<Promise>;\n\n  public:\n    explicit task_awaiter(handle_type coro) : coro_(coro)\n    {\n    }\n\n    bool await_ready() noexcept\n    {\n        return !coro_ || coro_.done();\n    }\n\n    auto await_suspend(std::coroutine_handle<> handle) noexcept\n    {\n        coro_.promise().setContinuation(handle);\n        return coro_;\n    }\n\n    auto await_resume()\n    {\n        if constexpr (std::is_void_v<decltype(coro_.promise().result())>)\n        {\n            coro_.promise().result();  // throw exception if any\n            return;\n        }\n        else\n        {\n            return std::move(coro_.promise().result());\n        }\n    }\n\n  private:\n    handle_type coro_;\n};\n\ntemplate <typename T = void>\nstruct [[nodiscard]] Task\n{\n    struct promise_type;\n    using handle_type = std::coroutine_handle<promise_type>;\n\n    Task(handle_type h) : coro_(h)\n    {\n    }\n\n    Task(const Task &) = delete;\n\n    Task(Task &&other) noexcept\n    {\n        coro_ = other.coro_;\n        other.coro_ = nullptr;\n    }\n\n    ~Task()\n    {\n        if (coro_)\n            coro_.destroy();\n    }\n\n    Task &operator=(const Task &) = delete;\n\n    Task &operator=(Task &&other) noexcept\n    {\n        if (std::addressof(other) == this)\n            return *this;\n        if (coro_)\n            coro_.destroy();\n\n        coro_ = other.coro_;\n        other.coro_ = nullptr;\n        return *this;\n    }\n\n    struct promise_type\n    {\n        Task<T> get_return_object()\n        {\n            return Task<T>{handle_type::from_promise(*this)};\n        }\n\n        std::suspend_always initial_suspend()\n        {\n            return {};\n        }\n\n        void return_value(const T &v)\n        {\n            value = v;\n        }\n\n        void return_value(T &&v)\n        {\n            value = std::move(v);\n        }\n\n        auto final_suspend() noexcept\n        {\n            return final_awaiter{};\n        }\n\n        void unhandled_exception()\n        {\n            exception_ = std::current_exception();\n        }\n\n        T &&result() &&\n        {\n            if (exception_ != nullptr)\n                std::rethrow_exception(exception_);\n            assert(value.has_value() == true);\n            return std::move(value.value());\n        }\n\n        T &result() &\n        {\n            if (exception_ != nullptr)\n                std::rethrow_exception(exception_);\n            assert(value.has_value() == true);\n            return value.value();\n        }\n\n        void setContinuation(std::coroutine_handle<> handle)\n        {\n            continuation_ = handle;\n        }\n\n        std::optional<T> value;\n        std::exception_ptr exception_;\n        std::coroutine_handle<> continuation_{std::noop_coroutine()};\n    };\n\n    auto operator co_await() const noexcept\n    {\n        return task_awaiter(coro_);\n    }\n\n    handle_type coro_;\n};\n\ntemplate <>\nstruct [[nodiscard]] Task<void>\n{\n    struct promise_type;\n    using handle_type = std::coroutine_handle<promise_type>;\n\n    Task(handle_type handle) : coro_(handle)\n    {\n    }\n\n    Task(const Task &) = delete;\n\n    Task(Task &&other) noexcept\n    {\n        coro_ = other.coro_;\n        other.coro_ = nullptr;\n    }\n\n    ~Task()\n    {\n        if (coro_)\n            coro_.destroy();\n    }\n\n    Task &operator=(const Task &) = delete;\n\n    Task &operator=(Task &&other) noexcept\n    {\n        if (std::addressof(other) == this)\n            return *this;\n        if (coro_)\n            coro_.destroy();\n\n        coro_ = other.coro_;\n        other.coro_ = nullptr;\n        return *this;\n    }\n\n    struct promise_type\n    {\n        Task<> get_return_object()\n        {\n            return Task<>{handle_type::from_promise(*this)};\n        }\n\n        std::suspend_always initial_suspend()\n        {\n            return {};\n        }\n\n        void return_void()\n        {\n        }\n\n        auto final_suspend() noexcept\n        {\n            return final_awaiter{};\n        }\n\n        void unhandled_exception()\n        {\n            exception_ = std::current_exception();\n        }\n\n        void result()\n        {\n            if (exception_ != nullptr)\n                std::rethrow_exception(exception_);\n        }\n\n        void setContinuation(std::coroutine_handle<> handle)\n        {\n            continuation_ = handle;\n        }\n\n        std::exception_ptr exception_;\n        std::coroutine_handle<> continuation_{std::noop_coroutine()};\n    };\n\n    auto operator co_await() const noexcept\n    {\n        return task_awaiter(coro_);\n    }\n\n    handle_type coro_;\n};\n\n/// Fires a coroutine and doesn't force waiting nor deallocates upon promise\n/// destructs\n// NOTE: AsyncTask is designed to be not awaitable. And kills the entire process\n// if exception escaped.\nstruct AsyncTask\n{\n    struct promise_type;\n    using handle_type = std::coroutine_handle<promise_type>;\n\n    AsyncTask() = default;\n\n    AsyncTask(handle_type h) : coro_(h)\n    {\n    }\n\n    AsyncTask(const AsyncTask &) = delete;\n\n    AsyncTask(AsyncTask &&other) noexcept\n    {\n        coro_ = other.coro_;\n        other.coro_ = nullptr;\n    }\n\n    AsyncTask &operator=(const AsyncTask &) = delete;\n\n    AsyncTask &operator=(AsyncTask &&other) noexcept\n    {\n        if (std::addressof(other) == this)\n            return *this;\n\n        coro_ = other.coro_;\n        other.coro_ = nullptr;\n        return *this;\n    }\n\n    struct promise_type\n    {\n        AsyncTask get_return_object() noexcept\n        {\n            return {std::coroutine_handle<promise_type>::from_promise(*this)};\n        }\n\n        std::suspend_never initial_suspend() const noexcept\n        {\n            return {};\n        }\n\n        void unhandled_exception()\n        {\n            LOG_FATAL << \"Exception escaping AsyncTask.\";\n            std::terminate();\n        }\n\n        void return_void() noexcept\n        {\n        }\n\n        std::suspend_never final_suspend() const noexcept\n        {\n            return {};\n        }\n    };\n\n    handle_type coro_;\n};\n\n/// Helper class that provides the infrastructure for turning callback into\n/// coroutines\n// The user is responsible to fill in `await_suspend()` and constructors.\ntemplate <typename T = void>\nstruct CallbackAwaiter : public trantor::NonCopyable\n{\n    bool await_ready() noexcept\n    {\n        return false;\n    }\n\n    bool hasException() const noexcept\n    {\n        return exception_ != nullptr;\n    }\n\n    const T &await_resume() const noexcept(false)\n    {\n        // await_resume() should always be called after co_await\n        // (await_suspend()) is called. Therefore the value should always be set\n        // (or there's an exception)\n        assert(result_.has_value() == true || exception_ != nullptr);\n\n        if (exception_)\n            std::rethrow_exception(exception_);\n        return result_.value();\n    }\n\n  private:\n    // HACK: Not all desired types are default constructable. But we need the\n    // entire struct to be constructed for awaiting. std::optional takes care of\n    // that.\n    std::optional<T> result_;\n    std::exception_ptr exception_{nullptr};\n\n  protected:\n    void setException(const std::exception_ptr &e)\n    {\n        exception_ = e;\n    }\n\n    void setValue(const T &v)\n    {\n        result_.emplace(v);\n    }\n\n    void setValue(T &&v)\n    {\n        result_.emplace(std::move(v));\n    }\n};\n\ntemplate <>\nstruct CallbackAwaiter<void> : public trantor::NonCopyable\n{\n    bool await_ready() noexcept\n    {\n        return false;\n    }\n\n    void await_resume() noexcept(false)\n    {\n        if (exception_)\n            std::rethrow_exception(exception_);\n    }\n\n    bool hasException() const noexcept\n    {\n        return exception_ != nullptr;\n    }\n\n  private:\n    std::exception_ptr exception_{nullptr};\n\n  protected:\n    void setException(const std::exception_ptr &e)\n    {\n        exception_ = e;\n    }\n};\n\n// An ok implementation of sync_await. This allows one to call\n// coroutines and wait for the result from a function.\ntemplate <typename Await>\nauto sync_wait(Await &&await)\n{\n    static_assert(is_awaitable_v<std::decay_t<Await>>);\n    using value_type = typename await_result<Await>::type;\n    std::condition_variable cv;\n    std::mutex mtx;\n    std::atomic<bool> flag = false;\n    std::exception_ptr exception_ptr;\n    std::unique_lock lk(mtx);\n\n    if constexpr (std::is_same_v<value_type, void>)\n    {\n        auto task = [&]() -> AsyncTask {\n            try\n            {\n                co_await await;\n            }\n            catch (...)\n            {\n                exception_ptr = std::current_exception();\n            }\n            std::unique_lock lk(mtx);\n            flag = true;\n            cv.notify_all();\n        };\n\n        std::thread thr([&]() { task(); });\n        cv.wait(lk, [&]() { return (bool)flag; });\n        thr.join();\n        if (exception_ptr)\n            std::rethrow_exception(exception_ptr);\n    }\n    else\n    {\n        std::optional<value_type> value;\n        auto task = [&]() -> AsyncTask {\n            try\n            {\n                value = co_await await;\n            }\n            catch (...)\n            {\n                exception_ptr = std::current_exception();\n            }\n            std::unique_lock lk(mtx);\n            flag = true;\n            cv.notify_all();\n        };\n\n        std::thread thr([&]() { task(); });\n        cv.wait(lk, [&]() { return (bool)flag; });\n        assert(value.has_value() == true || exception_ptr);\n        thr.join();\n\n        if (exception_ptr)\n            std::rethrow_exception(exception_ptr);\n\n        return std::move(value.value());\n    }\n}\n\n// Converts a task (or task like) promise into std::future for old-style async\ntemplate <typename Await>\ninline auto co_future(Await &&await) noexcept\n    -> std::future<await_result_t<Await>>\n{\n    using Result = await_result_t<Await>;\n    std::promise<Result> prom;\n    auto fut = prom.get_future();\n    [](std::promise<Result> prom, Await await) -> AsyncTask {\n        try\n        {\n            if constexpr (std::is_void_v<Result>)\n            {\n                co_await std::move(await);\n                prom.set_value();\n            }\n            else\n                prom.set_value(co_await std::move(await));\n        }\n        catch (...)\n        {\n            prom.set_exception(std::current_exception());\n        }\n    }(std::move(prom), std::move(await));\n    return fut;\n}\n\nnamespace internal\n{\nstruct [[nodiscard]] TimerAwaiter : CallbackAwaiter<void>\n{\n    TimerAwaiter(trantor::EventLoop *loop,\n                 const std::chrono::duration<double> &delay)\n        : loop_(loop), delay_(delay.count())\n    {\n    }\n\n    TimerAwaiter(trantor::EventLoop *loop, double delay)\n        : loop_(loop), delay_(delay)\n    {\n    }\n\n    void await_suspend(std::coroutine_handle<> handle)\n    {\n        loop_->runAfter(delay_, [handle]() { handle.resume(); });\n    }\n\n  private:\n    trantor::EventLoop *loop_;\n    double delay_;\n};\n\nstruct [[nodiscard]] LoopAwaiter : CallbackAwaiter<void>\n{\n    LoopAwaiter(trantor::EventLoop *workLoop,\n                std::function<void()> &&taskFunc,\n                trantor::EventLoop *resumeLoop = nullptr)\n        : workLoop_(workLoop),\n          resumeLoop_(resumeLoop),\n          taskFunc_(std::move(taskFunc))\n    {\n        assert(workLoop);\n    }\n\n    void await_suspend(std::coroutine_handle<> handle)\n    {\n        workLoop_->queueInLoop([handle, this]() {\n            try\n            {\n                taskFunc_();\n                if (resumeLoop_ && resumeLoop_ != workLoop_)\n                    resumeLoop_->queueInLoop([handle]() { handle.resume(); });\n                else\n                    handle.resume();\n            }\n            catch (...)\n            {\n                setException(std::current_exception());\n                if (resumeLoop_ && resumeLoop_ != workLoop_)\n                    resumeLoop_->queueInLoop([handle]() { handle.resume(); });\n                else\n                    handle.resume();\n            }\n        });\n    }\n\n  private:\n    trantor::EventLoop *workLoop_{nullptr};\n    trantor::EventLoop *resumeLoop_{nullptr};\n    std::function<void()> taskFunc_;\n};\n\nstruct [[nodiscard]] SwitchThreadAwaiter : CallbackAwaiter<void>\n{\n    explicit SwitchThreadAwaiter(trantor::EventLoop *loop) : loop_(loop)\n    {\n    }\n\n    void await_suspend(std::coroutine_handle<> handle)\n    {\n        loop_->runInLoop([handle]() { handle.resume(); });\n    }\n\n  private:\n    trantor::EventLoop *loop_;\n};\n\nstruct [[nodiscard]] EndAwaiter : CallbackAwaiter<void>\n{\n    EndAwaiter(trantor::EventLoop *loop) : loop_(loop)\n    {\n        assert(loop);\n    }\n\n    void await_suspend(std::coroutine_handle<> handle)\n    {\n        loop_->runOnQuit([handle]() { handle.resume(); });\n    }\n\n  private:\n    trantor::EventLoop *loop_{nullptr};\n};\n\n}  // namespace internal\n\ninline internal::TimerAwaiter sleepCoro(\n    trantor::EventLoop *loop,\n    const std::chrono::duration<double> &delay) noexcept\n{\n    assert(loop);\n    return {loop, delay};\n}\n\ninline internal::TimerAwaiter sleepCoro(trantor::EventLoop *loop,\n                                        double delay) noexcept\n{\n    assert(loop);\n    return {loop, delay};\n}\n\ninline internal::LoopAwaiter queueInLoopCoro(\n    trantor::EventLoop *workLoop,\n    std::function<void()> taskFunc,\n    trantor::EventLoop *resumeLoop = nullptr)\n{\n    assert(workLoop);\n    return {workLoop, std::move(taskFunc), resumeLoop};\n}\n\ninline internal::SwitchThreadAwaiter switchThreadCoro(\n    trantor::EventLoop *loop) noexcept\n{\n    assert(loop);\n    return internal::SwitchThreadAwaiter{loop};\n}\n\ninline internal::EndAwaiter untilQuit(trantor::EventLoop *loop)\n{\n    assert(loop);\n    return {loop};\n}\n\ntemplate <typename T, typename = std::void_t<>>\nstruct is_resumable : std::false_type\n{\n};\n\ntemplate <typename T>\nstruct is_resumable<\n    T,\n    std::void_t<decltype(internal::getAwaiter(std::declval<T>()))>>\n    : std::true_type\n{\n};\n\ntemplate <>\nstruct is_resumable<AsyncTask, std::void_t<AsyncTask>> : std::true_type\n{\n};\n\ntemplate <typename T>\nconstexpr bool is_resumable_v = is_resumable<T>::value;\n\n/**\n * @brief Runs a coroutine from a regular function\n * @param coro A coroutine that is awaitable\n */\ntemplate <typename Coro>\nvoid async_run(Coro &&coro)\n{\n    using CoroValueType = std::decay_t<Coro>;\n    auto functor = [](CoroValueType coro) -> AsyncTask {\n        auto frame = coro();\n\n        using FrameType = std::decay_t<decltype(frame)>;\n        static_assert(is_awaitable_v<FrameType>);\n\n        co_await frame;\n        co_return;\n    };\n    functor(std::forward<Coro>(coro));\n}\n\n/**\n * @brief returns a function that calls a coroutine\n * @param coro A coroutine that is awaitable\n */\ntemplate <typename Coro>\nstd::function<void()> async_func(Coro &&coro)\n{\n    return [coro = std::forward<Coro>(coro)]() mutable {\n        async_run(std::move(coro));\n    };\n}\n\nnamespace internal\n{\ntemplate <typename T>\nstruct [[nodiscard]] EventLoopAwaiter : public drogon::CallbackAwaiter<T>\n{\n    EventLoopAwaiter(std::function<T()> &&task, trantor::EventLoop *loop)\n        : task_(std::move(task)), loop_(loop)\n    {\n    }\n\n    void await_suspend(std::coroutine_handle<> handle)\n    {\n        loop_->queueInLoop([this, handle]() {\n            try\n            {\n                if constexpr (!std::is_same_v<T, void>)\n                {\n                    this->setValue(task_());\n                    handle.resume();\n                }\n                else\n                {\n                    task_();\n                    handle.resume();\n                }\n            }\n            catch (const std::exception &err)\n            {\n                LOG_ERROR << err.what();\n                this->setException(std::current_exception());\n                handle.resume();\n            }\n        });\n    }\n\n  private:\n    std::function<T()> task_;\n    trantor::EventLoop *loop_;\n};\n\ntemplate <typename... Tasks>\nstruct WhenAllAwaiter\n    : public CallbackAwaiter<\n          std::tuple<internal::void_to_false_t<await_result_t<Tasks>>...>>\n{\n    WhenAllAwaiter(Tasks... tasks)\n        : tasks_(std::forward<Tasks>(tasks)...), counter_(sizeof...(tasks))\n    {\n    }\n\n    void await_suspend(std::coroutine_handle<> handle)\n    {\n        if (counter_ == 0)\n        {\n            handle.resume();\n            return;\n        }\n\n        await_suspend_impl(handle, std::index_sequence_for<Tasks...>{});\n    }\n\n  private:\n    std::tuple<Tasks...> tasks_;\n    std::atomic<size_t> counter_;\n    std::tuple<internal::void_to_false_t<await_result_t<Tasks>>...> results_;\n    std::atomic_flag exceptionFlag_;\n\n    template <size_t Idx>\n    void launch_task(std::coroutine_handle<> handle)\n    {\n        using Self = WhenAllAwaiter<Tasks...>;\n        [](Self *self, std::coroutine_handle<> handle) -> AsyncTask {\n            try\n            {\n                using TaskType = std::tuple_element_t<\n                    Idx,\n                    std::remove_cvref_t<decltype(results_)>>;\n                if constexpr (std::is_same_v<TaskType, std::false_type>)\n                {\n                    co_await std::get<Idx>(self->tasks_);\n                    std::get<Idx>(self->results_) = std::false_type{};\n                }\n                else\n                {\n                    std::get<Idx>(self->results_) =\n                        co_await std::get<Idx>(self->tasks_);\n                }\n            }\n            catch (...)\n            {\n                if (self->exceptionFlag_.test_and_set() == false)\n                    self->setException(std::current_exception());\n            }\n\n            if (self->counter_.fetch_sub(1, std::memory_order_acq_rel) == 1)\n            {\n                if (!self->hasException())\n                    self->setValue(std::move(self->results_));\n                handle.resume();\n            }\n        }(this, handle);\n    }\n\n    template <size_t... Is>\n    void await_suspend_impl(std::coroutine_handle<> handle,\n                            std::index_sequence<Is...>)\n    {\n        ((launch_task<Is>(handle)), ...);\n    }\n};\n\ntemplate <typename T>\nstruct WhenAllAwaiter<std::vector<Task<T>>>\n    : public CallbackAwaiter<std::vector<T>>\n{\n    WhenAllAwaiter(std::vector<Task<T>> tasks)\n        : tasks_(std::move(tasks)),\n          counter_(tasks_.size()),\n          results_(tasks_.size())\n    {\n    }\n\n    void await_suspend(std::coroutine_handle<> handle)\n    {\n        if (tasks_.empty())\n        {\n            this->setValue(std::vector<T>{});\n            handle.resume();\n            return;\n        }\n\n        const size_t count = tasks_.size();\n        for (size_t i = 0; i < count; ++i)\n        {\n            [](WhenAllAwaiter *self,\n               std::coroutine_handle<> handle,\n               Task<T> task,\n               size_t index) -> AsyncTask {\n                try\n                {\n                    auto result = co_await task;\n                    self->results_[index] = std::move(result);\n                }\n                catch (...)\n                {\n                    if (self->exceptionFlag_.test_and_set() == false)\n                        self->setException(std::current_exception());\n                }\n\n                if (self->counter_.fetch_sub(1, std::memory_order_acq_rel) == 1)\n                {\n                    if (!self->hasException())\n                    {\n                        self->setValue(std::move(self->results_));\n                    }\n                    handle.resume();\n                }\n            }(this, handle, std::move(tasks_[i]), i);\n        }\n    }\n\n  private:\n    std::vector<Task<T>> tasks_;\n    std::atomic<size_t> counter_;\n    std::vector<T> results_;\n    std::atomic_flag exceptionFlag_;\n};\n\ntemplate <>\nstruct WhenAllAwaiter<std::vector<Task<void>>> : public CallbackAwaiter<void>\n{\n    WhenAllAwaiter(std::vector<Task<void>> &&t)\n        : tasks_(std::move(t)), counter_(tasks_.size())\n    {\n    }\n\n    void await_suspend(std::coroutine_handle<> handle)\n    {\n        if (tasks_.empty())\n        {\n            handle.resume();\n            return;\n        }\n\n        const size_t count =\n            tasks_\n                .size();  // capture the size fist (see lifetime comment beflow)\n        for (size_t i = 0; i < count; ++i)\n        {\n            [](WhenAllAwaiter *self,\n               std::coroutine_handle<> handle,\n               Task<> task) -> AsyncTask {\n                try\n                {\n                    co_await task;\n                }\n                catch (...)\n                {\n                    if (self->exceptionFlag_.test_and_set() == false)\n                        self->setException(std::current_exception());\n                }\n                if (self->counter_.fetch_sub(1, std::memory_order_acq_rel) == 1)\n                    // This line CAN delete `this` at last iteration. We MUST\n                    // NOT depend on this after last iteration\n                    handle.resume();\n            }(this, handle, std::move(tasks_[i]));\n        }\n    }\n\n    std::vector<Task<void>> tasks_;\n    std::atomic<size_t> counter_;\n    std::atomic_flag exceptionFlag_;\n};\n}  // namespace internal\n\n/**\n * @brief Run a task in a given event loop and returns a resumable object that\n * can be co_awaited in a coroutine.\n */\ntemplate <typename T>\ninline internal::EventLoopAwaiter<T> queueInLoopCoro(trantor::EventLoop *loop,\n                                                     std::function<T()> task)\n{\n    return internal::EventLoopAwaiter<T>(std::move(task), loop);\n}\n\nclass Mutex final\n{\n    class ScopedCoroMutexAwaiter;\n    class CoroMutexAwaiter;\n\n  public:\n    Mutex() noexcept : state_(unlockedValue()), waiters_(nullptr)\n    {\n    }\n\n    Mutex(const Mutex &) = delete;\n    Mutex(Mutex &&) = delete;\n    Mutex &operator=(const Mutex &) = delete;\n    Mutex &operator=(Mutex &&) = delete;\n\n    ~Mutex()\n    {\n        [[maybe_unused]] auto state = state_.load(std::memory_order_relaxed);\n        assert(state == unlockedValue() || state == nullptr);\n        assert(waiters_ == nullptr);\n    }\n\n    bool try_lock() noexcept\n    {\n        void *oldValue = unlockedValue();\n        return state_.compare_exchange_strong(oldValue,\n                                              nullptr,\n                                              std::memory_order_acquire,\n                                              std::memory_order_relaxed);\n    }\n\n    [[nodiscard]] ScopedCoroMutexAwaiter scoped_lock(\n        trantor::EventLoop *loop =\n            trantor::EventLoop::getEventLoopOfCurrentThread()) noexcept\n    {\n        return ScopedCoroMutexAwaiter(*this, loop);\n    }\n\n    [[nodiscard]] CoroMutexAwaiter lock(\n        trantor::EventLoop *loop =\n            trantor::EventLoop::getEventLoopOfCurrentThread()) noexcept\n    {\n        return CoroMutexAwaiter(*this, loop);\n    }\n\n    void unlock() noexcept\n    {\n        assert(state_.load(std::memory_order_relaxed) != unlockedValue());\n        auto *waitersHead = waiters_;\n        if (waitersHead == nullptr)\n        {\n            void *currentState = state_.load(std::memory_order_relaxed);\n            if (currentState == nullptr)\n            {\n                const bool releasedLock =\n                    state_.compare_exchange_strong(currentState,\n                                                   unlockedValue(),\n                                                   std::memory_order_release,\n                                                   std::memory_order_relaxed);\n                if (releasedLock)\n                {\n                    return;\n                }\n            }\n            currentState = state_.exchange(nullptr, std::memory_order_acquire);\n            assert(currentState != unlockedValue());\n            assert(currentState != nullptr);\n            auto *waiter = static_cast<CoroMutexAwaiter *>(currentState);\n            do\n            {\n                auto *temp = waiter->next_;\n                waiter->next_ = waitersHead;\n                waitersHead = waiter;\n                waiter = temp;\n            } while (waiter != nullptr);\n        }\n        assert(waitersHead != nullptr);\n        waiters_ = waitersHead->next_;\n        if (waitersHead->loop_)\n        {\n            auto handle = waitersHead->handle_;\n            waitersHead->loop_->runInLoop([handle] { handle.resume(); });\n        }\n        else\n        {\n            waitersHead->handle_.resume();\n        }\n    }\n\n  private:\n    class CoroMutexAwaiter\n    {\n      public:\n        CoroMutexAwaiter(Mutex &mutex, trantor::EventLoop *loop) noexcept\n            : mutex_(mutex), loop_(loop)\n        {\n        }\n\n        bool await_ready() noexcept\n        {\n            return mutex_.try_lock();\n        }\n\n        bool await_suspend(std::coroutine_handle<> handle) noexcept\n        {\n            handle_ = handle;\n            return mutex_.asynclockImpl(this);\n        }\n\n        void await_resume() noexcept\n        {\n        }\n\n      private:\n        friend class Mutex;\n\n        Mutex &mutex_;\n        trantor::EventLoop *loop_;\n        std::coroutine_handle<> handle_;\n        CoroMutexAwaiter *next_;\n    };\n\n    class ScopedCoroMutexAwaiter : public CoroMutexAwaiter\n    {\n      public:\n        ScopedCoroMutexAwaiter(Mutex &mutex, trantor::EventLoop *loop)\n            : CoroMutexAwaiter(mutex, loop)\n        {\n        }\n\n        [[nodiscard]] auto await_resume() noexcept\n        {\n            return std::unique_lock<Mutex>{mutex_, std::adopt_lock};\n        }\n    };\n\n    bool asynclockImpl(CoroMutexAwaiter *awaiter)\n    {\n        void *oldValue = state_.load(std::memory_order_relaxed);\n        while (true)\n        {\n            if (oldValue == unlockedValue())\n            {\n                void *newValue = nullptr;\n                if (state_.compare_exchange_weak(oldValue,\n                                                 newValue,\n                                                 std::memory_order_acquire,\n                                                 std::memory_order_relaxed))\n                {\n                    return false;\n                }\n            }\n            else\n            {\n                void *newValue = awaiter;\n                awaiter->next_ = static_cast<CoroMutexAwaiter *>(oldValue);\n                if (state_.compare_exchange_weak(oldValue,\n                                                 newValue,\n                                                 std::memory_order_release,\n                                                 std::memory_order_relaxed))\n                {\n                    return true;\n                }\n            }\n        }\n    }\n\n    void *unlockedValue() noexcept\n    {\n        return this;\n    }\n\n    std::atomic<void *> state_;\n    CoroMutexAwaiter *waiters_;\n};\n\ntemplate <typename... Tasks>\ninternal::WhenAllAwaiter<Tasks...> when_all(Tasks... tasks)\n{\n    return internal::WhenAllAwaiter<Tasks...>(std::move(tasks)...);\n}\n\ntemplate <typename T>\ninternal::WhenAllAwaiter<std::vector<Task<T>>> when_all(\n    std::vector<Task<T>> tasks)\n{\n    return internal::WhenAllAwaiter(std::move(tasks));\n}\n\ninline internal::WhenAllAwaiter<std::vector<Task<void>>> when_all(\n    std::vector<Task<void>> tasks)\n{\n    return internal::WhenAllAwaiter(std::move(tasks));\n}\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/utils/monitoring/Collector.h",
    "content": "/**\n *\n *  Collector.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <trantor/utils/Date.h>\n#include <drogon/utils/monitoring/Sample.h>\n#include <drogon/utils/monitoring/Metric.h>\n#include <drogon/utils/monitoring/Registry.h>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <mutex>\n#include <map>\n#include <algorithm>\n#include <memory>\n\nnamespace drogon\n{\nnamespace monitoring\n{\nstruct SamplesGroup\n{\n    std::shared_ptr<Metric> metric;\n    std::vector<Sample> samples;\n};\n\nclass CollectorBase : public std::enable_shared_from_this<CollectorBase>\n{\n  public:\n    virtual ~CollectorBase() = default;\n    virtual std::vector<SamplesGroup> collect() const = 0;\n    virtual const std::string &name() const = 0;\n    virtual const std::string &help() const = 0;\n    virtual const std::string_view type() const = 0;\n};\n\n/**\n * @brief The Collector class template is used to collect samples from a group\n * of metric.\n */\ntemplate <typename T>\nclass Collector : public CollectorBase\n{\n  public:\n    Collector(const std::string &name,\n              const std::string &help,\n              const std::vector<std::string> &labelNames)\n        : name_(name), help_(help), labelsNames_(labelNames)\n    {\n    }\n\n    template <typename... Arguments>\n    const std::shared_ptr<T> &metric(\n        const std::vector<std::string> &labelValues,\n        Arguments... args) noexcept(false)\n    {\n        if (labelValues.size() != labelsNames_.size())\n        {\n            throw std::runtime_error(\n                \"The number of label values is not equal to the number of \"\n                \"label names!\");\n        }\n        std::lock_guard<std::mutex> guard(mutex_);\n        auto iter = metrics_.find(labelValues);\n        if (iter != metrics_.end())\n        {\n            return iter->second;\n        }\n        auto metric =\n            std::make_shared<T>(name_, labelsNames_, labelValues, args...);\n        metrics_[labelValues] = metric;\n        return metrics_[labelValues];\n    }\n\n    std::vector<SamplesGroup> collect() const override\n    {\n        std::lock_guard<std::mutex> guard(mutex_);\n        std::vector<SamplesGroup> samples;\n        for (auto &pair : metrics_)\n        {\n            SamplesGroup samplesGroup;\n            auto &metric = pair.second;\n            samplesGroup.metric = metric;\n            auto metricSamples = metric->collect();\n            samplesGroup.samples = std::move(metricSamples);\n            samples.emplace_back(std::move(samplesGroup));\n        }\n        return samples;\n    }\n\n    const std::string &name() const override\n    {\n        return name_;\n    }\n\n    const std::string &help() const override\n    {\n        return help_;\n    }\n\n    const std::string_view type() const override\n    {\n        return T::type();\n    }\n\n    void registerTo(Registry &registry)\n    {\n        registry.registerCollector(shared_from_this());\n    }\n\n    const std::vector<std::string> &labelsNames() const\n    {\n        return labelsNames_;\n    }\n\n  private:\n    const std::string name_;\n    const std::string help_;\n    const std::vector<std::string> labelsNames_;\n    std::map<std::vector<std::string>, std::shared_ptr<T>> metrics_;\n    mutable std::mutex mutex_;\n};\n}  // namespace monitoring\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/utils/monitoring/Counter.h",
    "content": "/**\n *\n *  Counter.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <drogon/utils/monitoring/Metric.h>\n#include <string_view>\n#include <mutex>\n\nnamespace drogon\n{\nnamespace monitoring\n{\n/**\n * This class is used to collect samples for a counter metric.\n * */\nclass Counter : public Metric\n{\n  public:\n    Counter(const std::string &name,\n            const std::vector<std::string> &labelNames,\n            const std::vector<std::string> &labelValues) noexcept(false)\n        : Metric(name, labelNames, labelValues)\n    {\n    }\n\n    std::vector<Sample> collect() const override\n    {\n        Sample s;\n        s.name = name_;\n        {\n            std::lock_guard<std::mutex> lock(mutex_);\n            s.value = value_;\n        }\n        return {s};\n    }\n\n    /**\n     * Increment the counter by 1.\n     * */\n    void increment()\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        value_++;\n    }\n\n    /**\n     * Increment the counter by the given value.\n     * */\n    void increment(double value)\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        value_ += value;\n    }\n\n    void reset()\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        value_ = 0;\n    }\n\n    static std::string_view type()\n    {\n        return \"counter\";\n    }\n\n  private:\n    mutable std::mutex mutex_;\n    double value_{0};\n};\n}  // namespace monitoring\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/utils/monitoring/Gauge.h",
    "content": "/**\n *\n *  Gauge.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <drogon/utils/monitoring/Metric.h>\n#include <string_view>\n#include <atomic>\n\nnamespace drogon\n{\nnamespace monitoring\n{\n/**\n * This class is used to collect samples for a gauge metric.\n * */\nclass Gauge : public Metric\n{\n  public:\n    /**\n     * Construct a gauge metric with a name and a help string.\n     * */\n    Gauge(const std::string &name,\n          const std::vector<std::string> &labelNames,\n          const std::vector<std::string> &labelValues) noexcept(false)\n        : Metric(name, labelNames, labelValues)\n    {\n    }\n\n    std::vector<Sample> collect() const override\n    {\n        Sample s;\n        std::lock_guard<std::mutex> lock(mutex_);\n        s.name = name_;\n        s.value = value_;\n        s.timestamp = timestamp_;\n        return {s};\n    }\n\n    /**\n     * Increment the counter by 1.\n     * */\n    void increment()\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        value_ += 1;\n    }\n\n    void decrement()\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        value_ -= 1;\n    }\n\n    void decrement(double value)\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        value_ -= value;\n    }\n\n    /**\n     * Increment the counter by the given value.\n     * */\n    void increment(double value)\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        value_ += value;\n    }\n\n    void reset()\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        value_ = 0;\n    }\n\n    void set(double value)\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        value_ = value;\n    }\n\n    static std::string_view type()\n    {\n        return \"gauge\";\n    }\n\n    void setToCurrentTime()\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        timestamp_ = trantor::Date::now();\n    }\n\n  private:\n    mutable std::mutex mutex_;\n    double value_{0};\n    trantor::Date timestamp_{0};\n};\n}  // namespace monitoring\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/utils/monitoring/Histogram.h",
    "content": "/**\n *\n *  Histogram.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <drogon/exports.h>\n#include <drogon/utils/monitoring/Metric.h>\n#include <trantor/net/EventLoopThread.h>\n#include <string_view>\n#include <atomic>\n#include <mutex>\n\nnamespace drogon\n{\nnamespace monitoring\n{\n/**\n * This class is used to collect samples for a counter metric.\n * */\nclass DROGON_EXPORT Histogram : public Metric\n{\n  public:\n    struct TimeBucket\n    {\n        std::vector<uint64_t> buckets;\n        uint64_t count{0};\n        double sum{0};\n    };\n\n    Histogram(const std::string &name,\n              const std::vector<std::string> &labelNames,\n              const std::vector<std::string> &labelValues,\n              const std::vector<double> &bucketBoundaries,\n              const std::chrono::duration<double> &maxAge,\n              uint64_t timeBucketsCount,\n              trantor::EventLoop *loop = nullptr) noexcept(false)\n        : Metric(name, labelNames, labelValues),\n          maxAge_(maxAge),\n          timeBucketCount_(timeBucketsCount),\n          bucketBoundaries_(bucketBoundaries)\n    {\n        if (loop == nullptr)\n        {\n            loopThreadPtr_ = std::make_unique<trantor::EventLoopThread>();\n            loopPtr_ = loopThreadPtr_->getLoop();\n            loopThreadPtr_->run();\n        }\n        else\n        {\n            loopPtr_ = loop;\n        }\n        if (maxAge > std::chrono::seconds(0))\n        {\n            if (timeBucketsCount == 0)\n            {\n                throw std::runtime_error(\n                    \"timeBucketsCount must be greater than 0\");\n            }\n        }\n        timeBuckets_.emplace_back();\n        timeBuckets_.back().buckets.resize(bucketBoundaries.size() + 1);\n        // check the bucket boundaries are sorted\n        for (size_t i = 1; i < bucketBoundaries.size(); i++)\n        {\n            if (bucketBoundaries[i] <= bucketBoundaries[i - 1])\n            {\n                throw std::runtime_error(\n                    \"The bucket boundaries must be sorted\");\n            }\n        }\n    }\n\n    void observe(double value);\n    std::vector<Sample> collect() const override;\n\n    ~Histogram() override\n    {\n        if (timerId_ != trantor::InvalidTimerId)\n        {\n            loopPtr_->invalidateTimer(timerId_);\n        }\n    }\n\n    static std::string_view type()\n    {\n        return \"histogram\";\n    }\n\n  private:\n    std::deque<TimeBucket> timeBuckets_;\n    std::unique_ptr<trantor::EventLoopThread> loopThreadPtr_;\n    trantor::EventLoop *loopPtr_{nullptr};\n    mutable std::mutex mutex_;\n    std::chrono::duration<double> maxAge_;\n    trantor::TimerId timerId_{trantor::InvalidTimerId};\n    size_t timeBucketCount_{0};\n    const std::vector<double> bucketBoundaries_;\n\n    void rotateTimeBuckets()\n    {\n        std::lock_guard<std::mutex> guard(mutex_);\n        TimeBucket bucket;\n        bucket.buckets.resize(bucketBoundaries_.size() + 1);\n        timeBuckets_.emplace_back(std::move(bucket));\n        if (timeBuckets_.size() > timeBucketCount_)\n        {\n            auto expiredTimeBucket = timeBuckets_.front();\n            timeBuckets_.erase(timeBuckets_.begin());\n        }\n    }\n};\n}  // namespace monitoring\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/utils/monitoring/Metric.h",
    "content": "/**\n *\n *  Metric.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/utils/monitoring/Sample.h>\n#include <string>\n#include <vector>\n#include <memory>\n#include <stdexcept>\n\nnamespace drogon\n{\nnamespace monitoring\n{\n/**\n * This class is used to collect samples for a metric.\n * */\nclass Metric : public std::enable_shared_from_this<Metric>\n{\n  public:\n    /**\n     * Construct a metric with a name and a help string.\n     * */\n\n    Metric(const std::string &name,\n           const std::vector<std::string> &labelNames,\n           const std::vector<std::string> &labelValues) noexcept(false)\n        : name_(name)\n    {\n        if (labelNames.size() != labelValues.size())\n        {\n            throw std::runtime_error(\n                \"The number of label names is not equal to the number of label \"\n                \"values!\");\n        }\n        labels_.resize(labelNames.size());\n        for (size_t i = 0; i < labelNames.size(); i++)\n        {\n            labels_[i].first = labelNames[i];\n            labels_[i].second = labelValues[i];\n        }\n    };\n\n    const std::string &name() const\n    {\n        return name_;\n    }\n\n    const std::vector<std::pair<std::string, std::string>> &labels() const\n    {\n        return labels_;\n    }\n\n    virtual ~Metric() = default;\n    virtual std::vector<Sample> collect() const = 0;\n\n  protected:\n    const std::string name_;\n    std::vector<std::pair<std::string, std::string>> labels_;\n};\n\nusing MetricPtr = std::shared_ptr<Metric>;\n\n}  // namespace monitoring\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/utils/monitoring/Registry.h",
    "content": "/**\n *\n *  Registry.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <memory>\n\nnamespace drogon\n{\nnamespace monitoring\n{\nclass CollectorBase;\n\n/**\n * This class is used to register metrics.\n * */\nclass Registry\n{\n  public:\n    virtual ~Registry() = default;\n    virtual void registerCollector(\n        const std::shared_ptr<CollectorBase> &collector) = 0;\n};\n}  // namespace monitoring\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/utils/monitoring/Sample.h",
    "content": "/**\n *\n *  Sample.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <trantor/utils/Date.h>\n#include <vector>\n#include <string>\n\nnamespace drogon\n{\nnamespace monitoring\n{\n/**\n * This class is used to collect samples for a metric.\n * */\nstruct Sample\n{\n    double value{0};\n    trantor::Date timestamp{0};\n    std::string name;\n    std::vector<std::pair<std::string, std::string>> exLabels;\n};\n}  // namespace monitoring\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/utils/monitoring/StopWatch.h",
    "content": "/**\n *\n *  StopWatch.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include <chrono>\n#include <functional>\n#include <assert.h>\n\nnamespace drogon\n{\n/**\n * @brief This class is used to measure the elapsed time.\n */\nclass StopWatch\n{\n  public:\n    StopWatch() : start_(std::chrono::steady_clock::now())\n    {\n    }\n\n    ~StopWatch()\n    {\n    }\n\n    /**\n     * @brief Reset the start time.\n     */\n    void reset()\n    {\n        start_ = std::chrono::steady_clock::now();\n    }\n\n    /**\n     * @brief Get the elapsed time in seconds.\n     */\n    double elapsed() const\n    {\n        return std::chrono::duration_cast<std::chrono::duration<double>>(\n                   std::chrono::steady_clock::now() - start_)\n            .count();\n    }\n\n  private:\n    std::chrono::steady_clock::time_point start_;\n};\n\nclass LifeTimeWatch\n{\n  public:\n    LifeTimeWatch(std::function<void(double)> callback)\n        : stopWatch_(), callback_(std::move(callback))\n    {\n        assert(callback_);\n    }\n\n    ~LifeTimeWatch()\n    {\n        callback_(stopWatch_.elapsed());\n    }\n\n  private:\n    StopWatch stopWatch_;\n    std::function<void(double)> callback_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/inc/drogon/utils/monitoring.h",
    "content": "/**\n *\n *  monitoring.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n#include <drogon/utils/monitoring/Metric.h>\n#include <drogon/utils/monitoring/Registry.h>\n#include <drogon/utils/monitoring/Collector.h>\n#include <drogon/utils/monitoring/Sample.h>\n"
  },
  {
    "path": "lib/src/AOPAdvice.cc",
    "content": "/**\n *\n *  AOPAdvice.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"AOPAdvice.h\"\n#include \"HttpRequestImpl.h\"\n#include \"HttpResponseImpl.h\"\n#include <trantor/net/TcpConnection.h>\n\nnamespace drogon\n{\n\nstatic void doAdviceChain(\n    const std::vector<std::function<void(const HttpRequestPtr &,\n                                         AdviceCallback &&,\n                                         AdviceChainCallback &&)>> &adviceChain,\n    size_t index,\n    const HttpRequestImplPtr &req,\n    std::shared_ptr<const std::function<void(const HttpResponsePtr &)>>\n        &&callbackPtr);\n\nbool AopAdvice::passNewConnectionAdvices(\n    const trantor::TcpConnectionPtr &conn) const\n{\n    for (auto &advice : newConnectionAdvices_)\n    {\n        if (!advice(conn->localAddr(), conn->peerAddr()))\n        {\n            return false;\n        }\n    }\n    return true;\n}\n\nvoid AopAdvice::passResponseCreationAdvices(const HttpResponsePtr &resp) const\n{\n    if (!responseCreationAdvices_.empty())\n    {\n        for (auto &advice : responseCreationAdvices_)\n        {\n            advice(resp);\n        }\n    }\n}\n\nHttpResponsePtr AopAdvice::passSyncAdvices(const HttpRequestPtr &req) const\n{\n    for (auto &advice : syncAdvices_)\n    {\n        if (auto resp = advice(req))\n        {\n            return resp;\n        }\n    }\n    return nullptr;\n}\n\nvoid AopAdvice::passPreRoutingObservers(const HttpRequestImplPtr &req) const\n{\n    if (!preRoutingObservers_.empty())\n    {\n        for (auto &observer : preRoutingObservers_)\n        {\n            observer(req);\n        }\n    }\n}\n\nvoid AopAdvice::passPreRoutingAdvices(\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback) const\n{\n    if (preRoutingAdvices_.empty())\n    {\n        callback(nullptr);\n        return;\n    }\n\n    auto callbackPtr =\n        std::make_shared<std::decay_t<decltype(callback)>>(std::move(callback));\n    doAdviceChain(preRoutingAdvices_, 0, req, std::move(callbackPtr));\n}\n\nvoid AopAdvice::passPostRoutingObservers(const HttpRequestImplPtr &req) const\n{\n    if (!postRoutingObservers_.empty())\n    {\n        for (auto &observer : postRoutingObservers_)\n        {\n            observer(req);\n        }\n    }\n}\n\nvoid AopAdvice::passPostRoutingAdvices(\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback) const\n{\n    if (postRoutingAdvices_.empty())\n    {\n        callback(nullptr);\n        return;\n    }\n\n    auto callbackPtr =\n        std::make_shared<std::decay_t<decltype(callback)>>(std::move(callback));\n    doAdviceChain(postRoutingAdvices_, 0, req, std::move(callbackPtr));\n}\n\nvoid AopAdvice::passPreHandlingObservers(const HttpRequestImplPtr &req) const\n{\n    if (!preHandlingObservers_.empty())\n    {\n        for (auto &observer : preHandlingObservers_)\n        {\n            observer(req);\n        }\n    }\n}\n\nvoid AopAdvice::passPreHandlingAdvices(\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback) const\n{\n    if (preHandlingAdvices_.empty())\n    {\n        callback(nullptr);\n        return;\n    }\n\n    auto callbackPtr =\n        std::make_shared<std::decay_t<decltype(callback)>>(std::move(callback));\n    doAdviceChain(preHandlingAdvices_, 0, req, std::move(callbackPtr));\n}\n\nvoid AopAdvice::passPostHandlingAdvices(const HttpRequestImplPtr &req,\n                                        const HttpResponsePtr &resp) const\n{\n    for (auto &advice : postHandlingAdvices_)\n    {\n        advice(req, resp);\n    }\n}\n\nvoid AopAdvice::passPreSendingAdvices(const HttpRequestImplPtr &req,\n                                      const HttpResponsePtr &resp) const\n{\n    for (auto &advice : preSendingAdvices_)\n    {\n        advice(req, resp);\n    }\n}\n\nstatic void doAdviceChain(\n    const std::vector<std::function<void(const HttpRequestPtr &,\n                                         AdviceCallback &&,\n                                         AdviceChainCallback &&)>> &adviceChain,\n    size_t index,\n    const HttpRequestImplPtr &req,\n    std::shared_ptr<const std::function<void(const HttpResponsePtr &)>>\n        &&callbackPtr)\n{\n    if (index < adviceChain.size())\n    {\n        auto &advice = adviceChain[index];\n        advice(\n            req,\n            [/*copy*/ callbackPtr](const HttpResponsePtr &resp) {\n                (*callbackPtr)(resp);\n            },\n            [index, req, callbackPtr, &adviceChain]() mutable {\n                auto ioLoop = req->getLoop();\n                if (ioLoop && !ioLoop->isInLoopThread())\n                {\n                    ioLoop->queueInLoop([index,\n                                         req,\n                                         callbackPtr = std::move(callbackPtr),\n                                         &adviceChain]() mutable {\n                        doAdviceChain(adviceChain,\n                                      index + 1,\n                                      req,\n                                      std::move(callbackPtr));\n                    });\n                }\n                else\n                {\n                    doAdviceChain(adviceChain,\n                                  index + 1,\n                                  req,\n                                  std::move(callbackPtr));\n                }\n            });\n    }\n    else\n    {\n        (*callbackPtr)(nullptr);\n    }\n}\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/AOPAdvice.h",
    "content": "/**\n *\n *  AOPAdvice.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include \"impl_forwards.h\"\n#include <drogon/drogon_callbacks.h>\n#include <trantor/net/InetAddress.h>\n#include <functional>\n#include <vector>\n\nnamespace drogon\n{\nclass AopAdvice\n{\n  public:\n    static AopAdvice &instance()\n    {\n        static AopAdvice inst;\n        return inst;\n    }\n\n    // Getters?\n    bool hasPreRoutingAdvices() const\n    {\n        return !preRoutingAdvices_.empty();\n    }\n\n    bool hasPostRoutingAdvices() const\n    {\n        return !postRoutingAdvices_.empty();\n    }\n\n    bool hasPreHandlingAdvices() const\n    {\n        return !preHandlingAdvices_.empty();\n    }\n\n    // Setters?\n    void registerNewConnectionAdvice(\n        std::function<bool(const trantor::InetAddress &,\n                           const trantor::InetAddress &)> advice)\n    {\n        newConnectionAdvices_.emplace_back(std::move(advice));\n    }\n\n    void registerHttpResponseCreationAdvice(\n        std::function<void(const HttpResponsePtr &)> advice)\n    {\n        responseCreationAdvices_.emplace_back(std::move(advice));\n    }\n\n    void registerSyncAdvice(\n        std::function<HttpResponsePtr(const HttpRequestPtr &)> advice)\n\n    {\n        syncAdvices_.emplace_back(std::move(advice));\n    }\n\n    void registerPreRoutingObserver(\n        std::function<void(const HttpRequestPtr &)> advice)\n    {\n        preRoutingObservers_.emplace_back(std::move(advice));\n    }\n\n    void registerPreRoutingAdvice(\n        std::function<void(const HttpRequestPtr &,\n                           AdviceCallback &&,\n                           AdviceChainCallback &&)> advice)\n    {\n        preRoutingAdvices_.emplace_back(std::move(advice));\n    }\n\n    void registerPostRoutingObserver(\n        std::function<void(const HttpRequestPtr &)> advice)\n    {\n        postRoutingObservers_.emplace_back(std::move(advice));\n    }\n\n    void registerPostRoutingAdvice(\n        std::function<void(const HttpRequestPtr &,\n                           AdviceCallback &&,\n                           AdviceChainCallback &&)> advice)\n    {\n        postRoutingAdvices_.emplace_back(std::move(advice));\n    }\n\n    void registerPreHandlingObserver(\n        std::function<void(const HttpRequestPtr &)> advice)\n    {\n        preHandlingObservers_.emplace_back(std::move(advice));\n    }\n\n    void registerPreHandlingAdvice(\n        std::function<void(const HttpRequestPtr &,\n                           AdviceCallback &&,\n                           AdviceChainCallback &&)> advice)\n    {\n        preHandlingAdvices_.emplace_back(std::move(advice));\n    }\n\n    void registerPostHandlingAdvice(\n        std::function<void(const HttpRequestPtr &, const HttpResponsePtr &)>\n            advice)\n    {\n        postHandlingAdvices_.emplace_back(std::move(advice));\n    }\n\n    void registerPreSendingAdvice(\n        std::function<void(const HttpRequestPtr &, const HttpResponsePtr &)>\n            advice)\n    {\n        preSendingAdvices_.emplace_back(std::move(advice));\n    }\n\n    // Executors\n    bool passNewConnectionAdvices(const trantor::TcpConnectionPtr &conn) const;\n    void passResponseCreationAdvices(const HttpResponsePtr &resp) const;\n\n    HttpResponsePtr passSyncAdvices(const HttpRequestPtr &req) const;\n    void passPreRoutingObservers(const HttpRequestImplPtr &req) const;\n    void passPreRoutingAdvices(\n        const HttpRequestImplPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) const;\n    void passPostRoutingObservers(const HttpRequestImplPtr &req) const;\n    void passPostRoutingAdvices(\n        const HttpRequestImplPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) const;\n    void passPreHandlingObservers(const HttpRequestImplPtr &req) const;\n    void passPreHandlingAdvices(\n        const HttpRequestImplPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) const;\n    void passPostHandlingAdvices(const HttpRequestImplPtr &req,\n                                 const HttpResponsePtr &resp) const;\n    void passPreSendingAdvices(const HttpRequestImplPtr &req,\n                               const HttpResponsePtr &resp) const;\n\n  private:\n    using SyncAdvice = std::function<HttpResponsePtr(const HttpRequestPtr &)>;\n    using SyncReqObserver = std::function<void(const HttpRequestPtr &)>;\n    using SyncObserver =\n        std::function<void(const HttpRequestPtr &, const HttpResponsePtr &)>;\n    using AsyncAdvice = std::function<void(const HttpRequestPtr &,\n                                           AdviceCallback &&,\n                                           AdviceChainCallback &&)>;\n\n    // If we want to add aop functions anytime, we can add a mutex here\n\n    std::vector<std::function<bool(const trantor::InetAddress &,\n                                   const trantor::InetAddress &)>>\n        newConnectionAdvices_;\n    std::vector<std::function<void(const HttpResponsePtr &)>>\n        responseCreationAdvices_;\n\n    std::vector<SyncAdvice> syncAdvices_;\n    std::vector<SyncReqObserver> preRoutingObservers_;\n    std::vector<AsyncAdvice> preRoutingAdvices_;\n    std::vector<SyncReqObserver> postRoutingObservers_;\n    std::vector<AsyncAdvice> postRoutingAdvices_;\n    std::vector<SyncReqObserver> preHandlingObservers_;\n    std::vector<AsyncAdvice> preHandlingAdvices_;\n    std::vector<SyncObserver> postHandlingAdvices_;\n    std::vector<SyncObserver> preSendingAdvices_;\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/AccessLogger.cc",
    "content": "/**\n *\n *  @file AccessLogger.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpUtils.h\"\n#include <drogon/drogon.h>\n#include <drogon/plugins/AccessLogger.h>\n#include <drogon/plugins/RealIpResolver.h>\n#include <regex>\n#include <thread>\n#if !defined _WIN32 && !defined __HAIKU__\n#include <unistd.h>\n#include <sys/syscall.h>\n#elif defined __HAIKU__\n#include <unistd.h>\n#else\n#include <sstream>\n#endif\n#ifdef __FreeBSD__\n#include <pthread_np.h>\n#endif\n\n#ifdef DROGON_SPDLOG_SUPPORT\n#include <spdlog/spdlog.h>\n#include <spdlog/logger.h>\n#include <spdlog/sinks/stdout_color_sinks.h>\n#include <spdlog/sinks/rotating_file_sink.h>\n#ifdef _WIN32\n#include <spdlog/sinks/msvc_sink.h>\n// Damn antedeluvian M$ macros\n#undef min\n#undef max\n#endif\n#ifndef _WIN32\n#include <sys/wait.h>\n#include <unistd.h>\n#define os_access access\n#elif !defined(_WIN32) || defined(__MINGW32__)\n#include <sys/file.h>\n#include <unistd.h>\n#define os_access access\n#else\n#include <io.h>\n#define os_access _waccess\n#define R_OK 04\n#define W_OK 02\n#endif\n#endif\n\nusing namespace drogon;\nusing namespace drogon::plugin;\n\nbool AccessLogger::useRealIp_ = false;\n\nvoid AccessLogger::initAndStart(const Json::Value &config)\n{\n    useLocalTime_ = config.get(\"use_local_time\", true).asBool();\n    showMicroseconds_ = config.get(\"show_microseconds\", true).asBool();\n    timeFormat_ = config.get(\"custom_time_format\", \"\").asString();\n    useCustomTimeFormat_ = !timeFormat_.empty();\n    useRealIp_ = config.get(\"use_real_ip\", false).asBool();\n\n    logFunctionMap_ = {{\"$request_path\", outputReqPath},\n                       {\"$path\", outputReqPath},\n                       {\"$date\",\n                        [this](trantor::LogStream &stream,\n                               const drogon::HttpRequestPtr &req,\n                               const drogon::HttpResponsePtr &resp) {\n                            outputDate(stream, req, resp);\n                        }},\n                       {\"$request_date\",\n                        [this](trantor::LogStream &stream,\n                               const drogon::HttpRequestPtr &req,\n                               const drogon::HttpResponsePtr &resp) {\n                            outputReqDate(stream, req, resp);\n                        }},\n                       {\"$request_query\", outputReqQuery},\n                       {\"$request_url\", outputReqURL},\n                       {\"$query\", outputReqQuery},\n                       {\"$url\", outputReqURL},\n                       {\"$request_version\", outputVersion},\n                       {\"$version\", outputVersion},\n                       {\"$request\", outputReqLine},\n                       {\"$remote_addr\", outputRemoteAddr},\n                       {\"$local_addr\", outputLocalAddr},\n                       {\"$request_len\", outputReqLength},\n                       {\"$body_bytes_received\", outputReqLength},\n                       {\"$method\", outputMethod},\n                       {\"$thread\", outputThreadNumber},\n                       {\"$response_len\", outputRespLength},\n                       {\"$body_bytes_sent\", outputRespLength},\n                       {\"$status\", outputStatusString},\n                       {\"$status_code\", outputStatusCode},\n                       {\"$processing_time\", outputProcessingTime},\n                       {\"$upstream_http_content-type\", outputRespContentType},\n                       {\"$upstream_http_content_type\", outputRespContentType}};\n    auto format = config.get(\"log_format\", \"\").asString();\n    if (format.empty())\n    {\n        format =\n            \"$request_date $method $url [$body_bytes_received] ($remote_addr - \"\n            \"$local_addr) $status $body_bytes_sent $processing_time\";\n    }\n    createLogFunctions(format);\n    auto logPath = config.get(\"log_path\", \"\").asString();\n\n    if (config.isMember(\"path_exempt\"))\n    {\n        if (config[\"path_exempt\"].isArray())\n        {\n            const auto &exempts = config[\"path_exempt\"];\n            size_t exemptsCount = exempts.size();\n            if (exemptsCount)\n            {\n                std::string regexString;\n                size_t len = 0;\n                for (const auto &exempt : exempts)\n                {\n                    assert(exempt.isString());\n                    len += exempt.size();\n                }\n                regexString.reserve((exemptsCount * (1 + 2)) - 1 + len);\n\n                const auto last = --exempts.end();\n                for (auto exempt = exempts.begin(); exempt != last; ++exempt)\n                    regexString.append(\"(\")\n                        .append(exempt->asString())\n                        .append(\")|\");\n                regexString.append(\"(\").append(last->asString()).append(\")\");\n\n                exemptRegex_ = std::regex(regexString);\n                regexFlag_ = true;\n            }\n        }\n        else if (config[\"path_exempt\"].isString())\n        {\n            exemptRegex_ = std::regex(config[\"path_exempt\"].asString());\n            regexFlag_ = true;\n        }\n        else\n        {\n            LOG_ERROR << \"path_exempt must be a string or string array!\";\n        }\n    }\n\n#ifdef DROGON_SPDLOG_SUPPORT\n    auto logWithSpdlog = trantor::Logger::hasSpdLogSupport() &&\n                         config.get(\"use_spdlog\", false).asBool();\n    if (logWithSpdlog)\n    {\n        logIndex_ = config.get(\"log_index\", 0).asInt();\n        // Do nothing if already initialized...\n        if (!trantor::Logger::getSpdLogger(logIndex_))\n        {\n            trantor::Logger::enableSpdLog(logIndex_);\n            // Get the new logger & replace its sinks with the ones of the\n            // config\n            auto logger = trantor::Logger::getSpdLogger(logIndex_);\n            std::vector<spdlog::sink_ptr> sinks;\n            while (!logPath.empty())\n            {\n                // 1. check existence of folder or try to create it\n                auto fsLogPath =\n                    std::filesystem::path(utils::toNativePath(logPath));\n                std::error_code fsErr;\n                if (!std::filesystem::create_directories(fsLogPath, fsErr) &&\n                    fsErr)\n                {\n                    LOG_ERROR << \"could not create log file path\";\n                    break;\n                }\n                // 2. check if we have rights to create files in the folder\n                if (os_access(fsLogPath.native().c_str(), W_OK) != 0)\n                {\n                    LOG_ERROR << \"cannot create files in log folder\";\n                    break;\n                }\n                std::filesystem::path fileName(\n                    config.get(\"log_file\", \"access.log\").asString());\n                if (fileName.empty())\n                    fileName = \"access.log\";\n                else\n                    fileName.replace_extension(\".log\");\n                auto sizeLimit = config.get(\"log_size_limit\", 0).asUInt64();\n                if (sizeLimit == 0)\n                    sizeLimit = config.get(\"size_limit\", 0).asUInt64();\n                if (sizeLimit == 0)  // 0 is not allowed by this sink\n                    sizeLimit = std::numeric_limits<std::size_t>::max();\n                std::size_t maxFiles = config.get(\"max_files\", 0).asUInt();\n                sinks.push_back(\n                    std::make_shared<spdlog::sinks::rotating_file_sink_mt>(\n                        (fsLogPath / fileName).string(),\n                        sizeLimit,\n                        // spdlog limitation\n                        std::min(maxFiles, std::size_t(20000)),\n                        false));\n                break;\n            }\n            if (sinks.empty())\n                sinks.push_back(\n                    std::make_shared<spdlog::sinks::stderr_color_sink_mt>());\n#if defined(_WIN32) && defined(_DEBUG)\n            // On Windows with debug, it may be interesting to have the logs\n            // directly in the Visual Studio / WinDbg console\n            sinks.push_back(std::make_shared<spdlog::sinks::msvc_sink_mt>());\n#endif\n            logger->sinks() = sinks;\n            // Override the pattern set in\n            // trantor::Logger::getDefaultSpdLogger() and let AccessLogger\n            // format the output\n            logger->set_pattern(\"%v\");\n        }\n    }\n    else\n#endif\n        if (!logPath.empty())\n    {\n        auto fileName = config.get(\"log_file\", \"access.log\").asString();\n        auto extension = std::string(\".log\");\n        auto pos = fileName.rfind('.');\n        if (pos != std::string::npos)\n        {\n            extension = fileName.substr(pos);\n            fileName = fileName.substr(0, pos);\n        }\n        if (fileName.empty())\n        {\n            fileName = \"access\";\n        }\n        asyncFileLogger_.setFileName(fileName, extension, logPath);\n        asyncFileLogger_.startLogging();\n        logIndex_ = config.get(\"log_index\", 0).asInt();\n        trantor::Logger::setOutputFunction(\n            [&](const char *msg, const uint64_t len) {\n                asyncFileLogger_.output(msg, len);\n            },\n            [&]() { asyncFileLogger_.flush(); },\n            logIndex_);\n        auto sizeLimit = config.get(\"log_size_limit\", 0).asUInt64();\n        if (sizeLimit == 0)\n        {\n            // In earlier code, \"size_limit\" is taken instead of\n            // \"log_size_limit\" as it said in the comment in AccessLogger.h.\n            // In order to ensure backward compatibility we still take this\n            // field as a fallback.\n            sizeLimit = config.get(\"size_limit\", 0).asUInt64();\n        }\n        if (sizeLimit > 0)\n        {\n            asyncFileLogger_.setFileSizeLimit(sizeLimit);\n        }\n        auto maxFiles = config.get(\"max_files\", 0).asUInt();\n        asyncFileLogger_.setMaxFiles(maxFiles);\n    }\n    drogon::app().registerPreSendingAdvice(\n        [this](const drogon::HttpRequestPtr &req,\n               const drogon::HttpResponsePtr &resp) {\n            if (regexFlag_)\n            {\n                if (!std::regex_match(req->path(), exemptRegex_))\n                {\n                    logging(LOG_RAW_TO(logIndex_), req, resp);\n                }\n            }\n            else\n            {\n                logging(LOG_RAW_TO(logIndex_), req, resp);\n            }\n        });\n}\n\nvoid AccessLogger::shutdown()\n{\n}\n\nvoid AccessLogger::logging(trantor::LogStream &stream,\n                           const drogon::HttpRequestPtr &req,\n                           const drogon::HttpResponsePtr &resp)\n{\n    for (auto &func : logFunctions_)\n    {\n        func(stream, req, resp);\n    }\n}\n\nvoid AccessLogger::createLogFunctions(std::string format)\n{\n    std::string rawString;\n    while (!format.empty())\n    {\n        LOG_TRACE << format;\n        auto pos = format.find('$');\n        if (pos != std::string::npos)\n        {\n            rawString += format.substr(0, pos);\n\n            format = format.substr(pos);\n            std::regex e{\"^\\\\$[a-zA-Z0-9\\\\-_]+\"};\n            std::smatch m;\n            if (std::regex_search(format, m, e))\n            {\n                if (!rawString.empty())\n                {\n                    logFunctions_.emplace_back(\n                        [rawString](trantor::LogStream &stream,\n                                    const drogon::HttpRequestPtr &,\n                                    const drogon::HttpResponsePtr &) {\n                            stream << rawString;\n                        });\n                    rawString.clear();\n                }\n                auto placeholder = m[0];\n                logFunctions_.emplace_back(newLogFunction(placeholder));\n                format = m.suffix().str();\n            }\n            else\n            {\n                rawString += '$';\n                format = format.substr(1);\n            }\n        }\n        else\n        {\n            rawString += format;\n            break;\n        }\n    }\n    if (!rawString.empty())\n    {\n        logFunctions_.emplace_back(\n            [rawString =\n                 std::move(rawString)](trantor::LogStream &stream,\n                                       const drogon::HttpRequestPtr &,\n                                       const drogon::HttpResponsePtr &) {\n                stream << rawString << \"\\n\";\n            });\n    }\n    else\n    {\n        logFunctions_.emplace_back(\n            [](trantor::LogStream &stream,\n               const drogon::HttpRequestPtr &,\n               const drogon::HttpResponsePtr &) { stream << \"\\n\"; });\n    }\n}\n\nAccessLogger::LogFunction AccessLogger::newLogFunction(\n    const std::string &placeholder)\n{\n    auto iter = logFunctionMap_.find(placeholder);\n    if (iter != logFunctionMap_.end())\n    {\n        return iter->second;\n    }\n    if (placeholder.find(\"$http_\") == 0 && placeholder.size() > 6)\n    {\n        auto headerName = placeholder.substr(6);\n        return [headerName =\n                    std::move(headerName)](trantor::LogStream &stream,\n                                           const drogon::HttpRequestPtr &req,\n                                           const drogon::HttpResponsePtr &) {\n            outputReqHeader(stream, req, headerName);\n        };\n    }\n    if (placeholder.find(\"$cookie_\") == 0 && placeholder.size() > 8)\n    {\n        auto cookieName = placeholder.substr(8);\n        return [cookieName =\n                    std::move(cookieName)](trantor::LogStream &stream,\n                                           const drogon::HttpRequestPtr &req,\n                                           const drogon::HttpResponsePtr &) {\n            outputReqCookie(stream, req, cookieName);\n        };\n    }\n    if (placeholder.find(\"$upstream_http_\") == 0 && placeholder.size() > 15)\n    {\n        auto headerName = placeholder.substr(15);\n        return [headerName = std::move(\n                    headerName)](trantor::LogStream &stream,\n                                 const drogon::HttpRequestPtr &,\n                                 const drogon::HttpResponsePtr &resp) {\n            outputRespHeader(stream, resp, headerName);\n        };\n    }\n    return [placeholder](trantor::LogStream &stream,\n                         const drogon::HttpRequestPtr &,\n                         const drogon::HttpResponsePtr &) {\n        stream << placeholder;\n    };\n}\n\nvoid AccessLogger::outputReqPath(trantor::LogStream &stream,\n                                 const drogon::HttpRequestPtr &req,\n                                 const drogon::HttpResponsePtr &)\n{\n    stream << req->path();\n}\n\nvoid AccessLogger::outputDate(trantor::LogStream &stream,\n                              const drogon::HttpRequestPtr &,\n                              const drogon::HttpResponsePtr &) const\n{\n    if (useCustomTimeFormat_)\n    {\n        if (useLocalTime_)\n        {\n            stream << trantor::Date::now().toCustomFormattedStringLocal(\n                timeFormat_, showMicroseconds_);\n        }\n        else\n        {\n            stream << trantor::Date::now().toCustomFormattedString(\n                timeFormat_, showMicroseconds_);\n        }\n    }\n    else\n    {\n        if (useLocalTime_)\n        {\n            stream << trantor::Date::now().toFormattedStringLocal(\n                showMicroseconds_);\n        }\n        else\n        {\n            stream << trantor::Date::now().toFormattedString(showMicroseconds_);\n        }\n    }\n}\n\nvoid AccessLogger::outputReqDate(trantor::LogStream &stream,\n                                 const drogon::HttpRequestPtr &req,\n                                 const drogon::HttpResponsePtr &) const\n{\n    if (useCustomTimeFormat_)\n    {\n        if (useLocalTime_)\n        {\n            stream << req->creationDate().toCustomFormattedStringLocal(\n                timeFormat_, showMicroseconds_);\n        }\n        else\n        {\n            stream << req->creationDate().toCustomFormattedString(\n                timeFormat_, showMicroseconds_);\n        }\n    }\n    else\n    {\n        if (useLocalTime_)\n        {\n            stream << req->creationDate().toFormattedStringLocal(\n                showMicroseconds_);\n        }\n        else\n        {\n            stream << req->creationDate().toFormattedString(showMicroseconds_);\n        }\n    }\n}\n\n//$request_query\nvoid AccessLogger::outputReqQuery(trantor::LogStream &stream,\n                                  const drogon::HttpRequestPtr &req,\n                                  const drogon::HttpResponsePtr &)\n{\n    stream << req->query();\n}\n\n//$request_url\nvoid AccessLogger::outputReqURL(trantor::LogStream &stream,\n                                const drogon::HttpRequestPtr &req,\n                                const drogon::HttpResponsePtr &)\n{\n    auto &query = req->query();\n    if (query.empty())\n    {\n        stream << req->path();\n    }\n    else\n    {\n        stream << req->path() << '?' << query;\n    }\n}\n\n//$request_version\nvoid AccessLogger::outputVersion(trantor::LogStream &stream,\n                                 const drogon::HttpRequestPtr &req,\n                                 const drogon::HttpResponsePtr &)\n{\n    stream << req->versionString();\n}\n\n//$request\nvoid AccessLogger::outputReqLine(trantor::LogStream &stream,\n                                 const drogon::HttpRequestPtr &req,\n                                 const drogon::HttpResponsePtr &)\n{\n    auto &query = req->query();\n    if (query.empty())\n    {\n        stream << req->methodString() << \" \" << req->path() << \" \"\n               << req->versionString();\n    }\n    else\n    {\n        stream << req->methodString() << \" \" << req->path() << '?' << query\n               << \" \" << req->versionString();\n    }\n}\n\nvoid AccessLogger::outputRemoteAddr(trantor::LogStream &stream,\n                                    const drogon::HttpRequestPtr &req,\n                                    const drogon::HttpResponsePtr &)\n{\n    if (useRealIp_)\n    {\n        stream << RealIpResolver::GetRealAddr(req).toIpPort();\n    }\n    else\n    {\n        stream << req->peerAddr().toIpPort();\n    }\n}\n\nvoid AccessLogger::outputLocalAddr(trantor::LogStream &stream,\n                                   const drogon::HttpRequestPtr &req,\n                                   const drogon::HttpResponsePtr &)\n{\n    stream << req->localAddr().toIpPort();\n}\n\nvoid AccessLogger::outputReqLength(trantor::LogStream &stream,\n                                   const drogon::HttpRequestPtr &req,\n                                   const drogon::HttpResponsePtr &)\n{\n    stream << req->body().length();\n}\n\nvoid AccessLogger::outputRespLength(trantor::LogStream &stream,\n                                    const drogon::HttpRequestPtr &,\n                                    const drogon::HttpResponsePtr &resp)\n{\n    stream << resp->body().length();\n}\n\nvoid AccessLogger::outputMethod(trantor::LogStream &stream,\n                                const drogon::HttpRequestPtr &req,\n                                const drogon::HttpResponsePtr &)\n{\n    stream << req->methodString();\n}\n\nvoid AccessLogger::outputThreadNumber(trantor::LogStream &stream,\n                                      const drogon::HttpRequestPtr &,\n                                      const drogon::HttpResponsePtr &)\n{\n#ifdef __linux__\n    static thread_local pid_t threadId_{0};\n#else\n    static thread_local uint64_t threadId_{0};\n#endif\n#ifdef __linux__\n    if (threadId_ == 0)\n        threadId_ = static_cast<pid_t>(::syscall(SYS_gettid));\n#elif defined __FreeBSD__\n    if (threadId_ == 0)\n    {\n        threadId_ = pthread_getthreadid_np();\n    }\n#elif defined __OpenBSD__\n    if (threadId_ == 0)\n    {\n        threadId_ = getthrid();\n    }\n#elif defined _WIN32 || defined __HAIKU__\n    if (threadId_ == 0)\n    {\n        std::stringstream ss;\n        ss << std::this_thread::get_id();\n        threadId_ = std::stoull(ss.str());\n    }\n#else\n    if (threadId_ == 0)\n    {\n        pthread_threadid_np(NULL, &threadId_);\n    }\n#endif\n    stream << threadId_;\n}\n\n//$http_[header_name]\nvoid AccessLogger::outputReqHeader(trantor::LogStream &stream,\n                                   const drogon::HttpRequestPtr &req,\n                                   const std::string &headerName)\n{\n    stream << headerName << \": \" << req->getHeader(headerName);\n}\n\n//$cookie_[cookie_name]\nvoid AccessLogger::outputReqCookie(trantor::LogStream &stream,\n                                   const drogon::HttpRequestPtr &req,\n                                   const std::string &cookie)\n{\n    stream << \"(cookie)\" << cookie << \"=\" << req->getCookie(cookie);\n}\n\n//$upstream_http_[header_name]\nvoid AccessLogger::outputRespHeader(trantor::LogStream &stream,\n                                    const drogon::HttpResponsePtr &resp,\n                                    const std::string &headerName)\n{\n    stream << headerName << \": \" << resp->getHeader(headerName);\n}\n\n//$status\nvoid AccessLogger::outputStatusString(trantor::LogStream &stream,\n                                      const drogon::HttpRequestPtr &,\n                                      const drogon::HttpResponsePtr &resp)\n{\n    int code = resp->getStatusCode();\n    stream << code << \" \" << statusCodeToString(code);\n}\n\n//$status_code\nvoid AccessLogger::outputStatusCode(trantor::LogStream &stream,\n                                    const drogon::HttpRequestPtr &,\n                                    const drogon::HttpResponsePtr &resp)\n{\n    stream << resp->getStatusCode();\n}\n\n//$processing_time\nvoid AccessLogger::outputProcessingTime(trantor::LogStream &stream,\n                                        const drogon::HttpRequestPtr &req,\n                                        const drogon::HttpResponsePtr &)\n{\n    auto start = req->creationDate();\n    auto end = trantor::Date::now();\n    auto duration =\n        end.microSecondsSinceEpoch() - start.microSecondsSinceEpoch();\n    auto seconds = static_cast<double>(duration) / 1000000.0;\n    stream << seconds;\n}\n\n//$upstream_http_content-type $upstream_http_content_type\nvoid AccessLogger::outputRespContentType(trantor::LogStream &stream,\n                                         const drogon::HttpRequestPtr &,\n                                         const drogon::HttpResponsePtr &resp)\n{\n    stream << resp->contentTypeString();\n}\n"
  },
  {
    "path": "lib/src/CacheFile.cc",
    "content": "/**\n *\n *  CacheFile.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"CacheFile.h\"\n#include <trantor/utils/Logger.h>\n#ifdef _WIN32\n#include <mman.h>\n#include <drogon/utils/Utilities.h>\n#else\n#include <unistd.h>\n#include <sys/mman.h>\n#endif\n\nusing namespace drogon;\n\nCacheFile::CacheFile(const std::string &path, bool autoDelete)\n    : autoDelete_(autoDelete), path_(path)\n{\n#ifndef _MSC_VER\n    file_ = fopen(path_.data(), \"wb+\");\n#else\n    auto wPath{drogon::utils::toNativePath(path)};\n    if (_wfopen_s(&file_, wPath.c_str(), L\"wb+\") != 0)\n    {\n        file_ = nullptr;\n    }\n#endif\n    if (!file_)\n        LOG_SYSERR << \"CacheFile fopen:\";\n}\n\nCacheFile::~CacheFile()\n{\n    if (data_)\n    {\n        munmap(data_, dataLength_);\n    }\n    if (autoDelete_ && file_)\n    {\n        fclose(file_);\n#if defined(_WIN32) && !defined(__MINGW32__)\n        auto wPath{drogon::utils::toNativePath(path_)};\n        _wunlink(wPath.c_str());\n#else\n        unlink(path_.data());\n#endif\n    }\n    else if (file_)\n    {\n        fclose(file_);\n    }\n}\n\nvoid CacheFile::append(const char *data, size_t length)\n{\n    if (file_)\n    {\n        if (!fwrite(data, length, 1, file_))\n            LOG_SYSERR << \"CacheFile append:\";\n    }\n}\n\nsize_t CacheFile::length()\n{\n    if (file_)\n#ifdef _WIN32\n        return _ftelli64(file_);\n#else\n        return ftell(file_);\n#endif\n    return 0;\n}\n\nchar *CacheFile::data()\n{\n    if (!file_)\n        return nullptr;\n    if (!data_)\n    {\n        fflush(file_);\n#ifdef _WIN32\n        auto fd = _fileno(file_);\n#else\n        auto fd = fileno(file_);\n#endif\n        dataLength_ = length();\n        data_ = static_cast<char *>(\n            mmap(nullptr, dataLength_, PROT_READ, MAP_SHARED, fd, 0));\n        if (data_ == MAP_FAILED)\n        {\n            data_ = nullptr;\n            LOG_SYSERR << \"CacheFile mmap:\";\n        }\n    }\n    return data_;\n}\n"
  },
  {
    "path": "lib/src/CacheFile.h",
    "content": "/**\n *\n *  CacheFile.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <trantor/utils/NonCopyable.h>\n#include <string>\n#include <string_view>\n#include <stdio.h>\n\nnamespace drogon\n{\nclass CacheFile : public trantor::NonCopyable\n{\n  public:\n    explicit CacheFile(const std::string &path, bool autoDelete = true);\n    ~CacheFile();\n\n    void append(const std::string &data)\n    {\n        append(data.data(), data.length());\n    }\n\n    void append(const char *data, size_t length);\n\n    std::string_view getStringView()\n    {\n        if (data())\n            return std::string_view(data_, dataLength_);\n        return std::string_view();\n    }\n\n  private:\n    char *data();\n    size_t length();\n    FILE *file_{nullptr};\n    bool autoDelete_{true};\n    const std::string path_;\n    char *data_{nullptr};\n    size_t dataLength_{0};\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/ConfigAdapter.h",
    "content": "#pragma once\n#include <json/json.h>\n#include <vector>\n#include <string>\n#include <memory>\n#include <fstream>\n\nnamespace drogon\n{\nclass ConfigAdapter\n{\n  public:\n    virtual ~ConfigAdapter() = default;\n    virtual Json::Value getJson(const std::string &content) const\n        noexcept(false) = 0;\n    virtual std::vector<std::string> getExtensions() const = 0;\n};\n\nusing ConfigAdapterPtr = std::shared_ptr<ConfigAdapter>;\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/ConfigAdapterManager.cc",
    "content": "#include \"ConfigAdapterManager.h\"\n#include \"JsonConfigAdapter.h\"\n#include \"YamlConfigAdapter.h\"\n#include <algorithm>\n\nusing namespace drogon;\n#define REGISTER_CONFIG_ADAPTER(adapter)                                    \\\n    {                                                                       \\\n        auto adapterPtr = std::make_shared<adapter>();                      \\\n        auto exts = adapterPtr->getExtensions();                            \\\n        for (auto ext : exts)                                               \\\n        {                                                                   \\\n            std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); \\\n            adapters_[ext] = adapterPtr;                                    \\\n        }                                                                   \\\n    }\n\nConfigAdapterManager &ConfigAdapterManager::instance()\n{\n    static ConfigAdapterManager instance;\n    return instance;\n}\n\nJson::Value ConfigAdapterManager::getJson(const std::string &content,\n                                          std::string ext) const noexcept(false)\n{\n    std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);\n    auto it = adapters_.find(ext);\n    if (it == adapters_.end())\n    {\n        throw std::runtime_error(\"No valid parser for this config file!\");\n    }\n    return it->second->getJson(content);\n}\n\nConfigAdapterManager::ConfigAdapterManager()\n{\n    REGISTER_CONFIG_ADAPTER(JsonConfigAdapter);\n    REGISTER_CONFIG_ADAPTER(YamlConfigAdapter);\n}\n"
  },
  {
    "path": "lib/src/ConfigAdapterManager.h",
    "content": "#pragma once\n#include \"ConfigAdapterManager.h\"\n#include \"ConfigAdapter.h\"\n#include <map>\n\nnamespace drogon\n{\nclass ConfigAdapterManager\n{\n  public:\n    static ConfigAdapterManager &instance();\n    Json::Value getJson(const std::string &content, std::string ext) const\n        noexcept(false);\n\n  private:\n    ConfigAdapterManager();\n    std::map<std::string, ConfigAdapterPtr> adapters_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/ConfigLoader.cc",
    "content": "/**\n *\n *  @file ConfigLoader.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"ConfigLoader.h\"\n#include \"HttpAppFrameworkImpl.h\"\n#include <drogon/config.h>\n#include <fstream>\n#include <iostream>\n#include <sstream>\n#include <thread>\n#include <trantor/utils/Logger.h>\n#if !defined(_WIN32)\n#include <unistd.h>\n#define os_access access\n#else\n#include <io.h>\n#ifndef __MINGW32__\n#define os_access _waccess\n#define R_OK 04\n#define W_OK 02\n#else\n#define os_access access\n#endif\n#endif\n\n#include <drogon/utils/Utilities.h>\n#include \"ConfigAdapterManager.h\"\n#include <filesystem>\n\nusing namespace drogon;\n\nstatic bool bytesSize(std::string &sizeStr, size_t &size)\n{\n    if (sizeStr.empty())\n    {\n        size = -1;\n        return true;\n    }\n    else\n    {\n        size = 1;\n        switch (sizeStr[sizeStr.length() - 1])\n        {\n            case 'k':\n            case 'K':\n                size = 1024;\n                sizeStr.resize(sizeStr.length() - 1);\n                break;\n            case 'M':\n            case 'm':\n                size = (1024 * 1024);\n                sizeStr.resize(sizeStr.length() - 1);\n                break;\n            case 'g':\n            case 'G':\n                size = (1024 * 1024 * 1024);\n                sizeStr.resize(sizeStr.length() - 1);\n                break;\n#if ((ULONG_MAX) != (UINT_MAX))\n            // 64bit system\n            case 't':\n            case 'T':\n                size = (1024L * 1024L * 1024L * 1024L);\n                sizeStr.resize(sizeStr.length() - 1);\n                break;\n#endif\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '7':\n            case '8':\n            case '9':\n                break;\n            default:\n                return false;\n                break;\n        }\n        std::istringstream iss(sizeStr);\n        size_t tmpSize;\n        iss >> tmpSize;\n        if (iss.fail())\n        {\n            return false;\n        }\n        if ((size_t(-1) / tmpSize) >= size)\n            size *= tmpSize;\n        else\n        {\n            size = -1;\n        }\n        return true;\n    }\n}\n\nConfigLoader::ConfigLoader(const std::string &configFile)\n{\n    if (os_access(drogon::utils::toNativePath(configFile).c_str(), 0) != 0)\n    {\n        throw std::runtime_error(\"Config file \" + configFile + \" not found!\");\n    }\n    if (os_access(drogon::utils::toNativePath(configFile).c_str(), R_OK) != 0)\n    {\n        throw std::runtime_error(\"No permission to read config file \" +\n                                 configFile);\n    }\n    configFile_ = configFile;\n    auto pos = configFile.find_last_of('.');\n    if (pos == std::string::npos)\n    {\n        throw std::runtime_error(\"Invalid config file name!\");\n    }\n    auto ext = configFile.substr(pos + 1);\n    std::ifstream infile(drogon::utils::toNativePath(configFile).c_str(),\n                         std::ifstream::in);\n    // get the content of the infile\n    std::string content((std::istreambuf_iterator<char>(infile)),\n                        std::istreambuf_iterator<char>());\n    try\n    {\n        configJsonRoot_ =\n            ConfigAdapterManager::instance().getJson(content, std::move(ext));\n    }\n    catch (std::exception &e)\n    {\n        throw std::runtime_error(\"Error reading config file \" + configFile +\n                                 \": \" + e.what());\n    }\n}\n\nConfigLoader::ConfigLoader(const Json::Value &data) : configJsonRoot_(data)\n{\n}\n\nConfigLoader::ConfigLoader(Json::Value &&data)\n    : configJsonRoot_(std::move(data))\n{\n}\n\nConfigLoader::~ConfigLoader()\n{\n}\n\nstatic void loadLogSetting(const Json::Value &log)\n{\n    if (!log)\n        return;\n    auto useSpdlog = log.get(\"use_spdlog\", false).asBool();\n    auto logPath = log.get(\"log_path\", \"\").asString();\n    auto baseName = log.get(\"logfile_base_name\", \"\").asString();\n    auto logSize = log.get(\"log_size_limit\", 100000000).asUInt64();\n    auto maxFiles = log.get(\"max_files\", 0).asUInt();\n    HttpAppFrameworkImpl::instance().setLogPath(\n        logPath, baseName, logSize, maxFiles, useSpdlog);\n    auto logLevel = log.get(\"log_level\", \"DEBUG\").asString();\n    if (logLevel == \"TRACE\")\n    {\n        trantor::Logger::setLogLevel(trantor::Logger::kTrace);\n    }\n    else if (logLevel == \"DEBUG\")\n    {\n        trantor::Logger::setLogLevel(trantor::Logger::kDebug);\n    }\n    else if (logLevel == \"INFO\")\n    {\n        trantor::Logger::setLogLevel(trantor::Logger::kInfo);\n    }\n    else if (logLevel == \"WARN\")\n    {\n        trantor::Logger::setLogLevel(trantor::Logger::kWarn);\n    }\n    auto localTime = log.get(\"display_local_time\", false).asBool();\n    trantor::Logger::setDisplayLocalTime(localTime);\n}\n\nstatic void loadControllers(const Json::Value &controllers)\n{\n    if (!controllers)\n        return;\n    for (auto const &controller : controllers)\n    {\n        auto path = controller.get(\"path\", \"\").asString();\n        auto ctrlName = controller.get(\"controller\", \"\").asString();\n        if (path == \"\" || ctrlName == \"\")\n            continue;\n        std::vector<internal::HttpConstraint> constraints;\n        if (!controller[\"http_methods\"].isNull())\n        {\n            for (auto const &method : controller[\"http_methods\"])\n            {\n                auto strMethod = method.asString();\n                std::transform(strMethod.begin(),\n                               strMethod.end(),\n                               strMethod.begin(),\n                               [](unsigned char c) { return tolower(c); });\n                if (strMethod == \"get\")\n                {\n                    constraints.push_back(Get);\n                }\n                else if (strMethod == \"post\")\n                {\n                    constraints.push_back(Post);\n                }\n                else if (strMethod == \"head\")  // The branch never work\n                {\n                    constraints.push_back(Head);\n                }\n                else if (strMethod == \"put\")\n                {\n                    constraints.push_back(Put);\n                }\n                else if (strMethod == \"delete\")\n                {\n                    constraints.push_back(Delete);\n                }\n                else if (strMethod == \"patch\")\n                {\n                    constraints.push_back(Patch);\n                }\n            }\n        }\n        if (!controller[\"filters\"].isNull())\n        {\n            for (auto const &filter : controller[\"filters\"])\n            {\n                constraints.push_back(filter.asString());\n            }\n        }\n        drogon::app().registerHttpSimpleController(path, ctrlName, constraints);\n    }\n}\n\nstatic void loadApp(const Json::Value &app)\n{\n    if (!app)\n        return;\n    // threads number\n    auto threadsNum = app.get(\"threads_num\", 1).asUInt64();\n    if (threadsNum == 1)\n    {\n        threadsNum = app.get(\"number_of_threads\", 1).asUInt64();\n    }\n    if (threadsNum == 0)\n    {\n        // set the number to the number of processors.\n        threadsNum = std::thread::hardware_concurrency();\n        LOG_TRACE << \"The number of processors is \" << threadsNum;\n    }\n    if (threadsNum < 1)\n        threadsNum = 1;\n    drogon::app().setThreadNum(threadsNum);\n    // session\n    auto enableSession = app.get(\"enable_session\", false).asBool();\n    if (enableSession)\n    {\n        auto timeout = app.get(\"session_timeout\", 0).asUInt64();\n        auto sameSite = app.get(\"session_same_site\", \"Null\").asString();\n        auto cookieKey = app.get(\"session_cookie_key\", \"JSESSIONID\").asString();\n        auto maxAge = app.get(\"session_max_age\", -1).asInt();\n        drogon::app().enableSession(timeout,\n                                    Cookie::convertString2SameSite(sameSite),\n                                    cookieKey,\n                                    maxAge);\n    }\n    else\n        drogon::app().disableSession();\n    // document root\n    auto documentRoot = app.get(\"document_root\", \"\").asString();\n    if (documentRoot != \"\")\n    {\n        drogon::app().setDocumentRoot(documentRoot);\n    }\n    if (!app[\"static_file_headers\"].empty())\n    {\n        if (app[\"static_file_headers\"].isArray())\n        {\n            std::vector<std::pair<std::string, std::string>> headers;\n            for (auto &header : app[\"static_file_headers\"])\n            {\n                headers.emplace_back(\n                    std::make_pair(header[\"name\"].asString(),\n                                   header[\"value\"].asString()));\n            }\n            drogon::app().setStaticFileHeaders(headers);\n        }\n        else\n        {\n            throw std::runtime_error(\n                \"The static_file_headers option must be an array\");\n        }\n    }\n    // upload path\n    auto uploadPath = app.get(\"upload_path\", \"uploads\").asString();\n    drogon::app().setUploadPath(uploadPath);\n    // file types\n    auto fileTypes = app[\"file_types\"];\n    if (fileTypes.isArray() && !fileTypes.empty())\n    {\n        std::vector<std::string> types;\n        for (auto const &fileType : fileTypes)\n        {\n            types.push_back(fileType.asString());\n            LOG_TRACE << \"file type:\" << types.back();\n        }\n        drogon::app().setFileTypes(types);\n    }\n    // locations\n    if (app.isMember(\"locations\"))\n    {\n        auto &locations = app[\"locations\"];\n        if (!locations.isArray())\n        {\n            throw std::runtime_error(\"The locations option must be an array\");\n        }\n        for (auto &location : locations)\n        {\n            auto uri = location.get(\"uri_prefix\", \"\").asString();\n            if (uri.empty())\n                continue;\n            auto defaultContentType =\n                location.get(\"default_content_type\", \"\").asString();\n            auto alias = location.get(\"alias\", \"\").asString();\n            auto isCaseSensitive =\n                location.get(\"is_case_sensitive\", false).asBool();\n            auto allAll = location.get(\"allow_all\", true).asBool();\n            auto isRecursive = location.get(\"is_recursive\", true).asBool();\n            if (!location[\"filters\"].isNull())\n            {\n                if (location[\"filters\"].isArray())\n                {\n                    std::vector<std::string> filters;\n                    for (auto const &filter : location[\"filters\"])\n                    {\n                        filters.push_back(filter.asString());\n                    }\n                    drogon::app().addALocation(uri,\n                                               defaultContentType,\n                                               alias,\n                                               isCaseSensitive,\n                                               allAll,\n                                               isRecursive,\n                                               filters);\n                }\n                else\n                {\n                    throw std::runtime_error(\"the filters of location '\" + uri +\n                                             \"' should be an array\");\n                }\n            }\n            else\n            {\n                drogon::app().addALocation(uri,\n                                           defaultContentType,\n                                           alias,\n                                           isCaseSensitive,\n                                           allAll,\n                                           isRecursive);\n            }\n        }\n    }\n    // max connections\n    auto maxConns = app.get(\"max_connections\", 0).asUInt64();\n    if (maxConns > 0)\n    {\n        drogon::app().setMaxConnectionNum(maxConns);\n    }\n    // max connections per IP\n    auto maxConnsPerIP = app.get(\"max_connections_per_ip\", 0).asUInt64();\n    if (maxConnsPerIP > 0)\n    {\n        drogon::app().setMaxConnectionNumPerIP(maxConnsPerIP);\n    }\n#if !defined(_WIN32) && !TARGET_OS_IOS\n    // dynamic views\n    auto enableDynamicViews = app.get(\"load_dynamic_views\", false).asBool();\n    if (enableDynamicViews)\n    {\n        auto viewsPaths = app[\"dynamic_views_path\"];\n        if (viewsPaths.isArray() && viewsPaths.size() > 0)\n        {\n            std::vector<std::string> paths;\n            for (auto const &viewsPath : viewsPaths)\n            {\n                paths.push_back(viewsPath.asString());\n                LOG_TRACE << \"views path:\" << paths.back();\n            }\n            auto outputPath =\n                app.get(\"dynamic_views_output_path\", \"\").asString();\n            drogon::app().enableDynamicViewsLoading(paths, outputPath);\n        }\n    }\n#endif\n    auto stackLimit = app.get(\"json_parser_stack_limit\", 1000).asUInt64();\n    drogon::app().setJsonParserStackLimit(stackLimit);\n    auto unicodeEscaping =\n        app.get(\"enable_unicode_escaping_in_json\", true).asBool();\n    drogon::app().setUnicodeEscapingInJson(unicodeEscaping);\n    auto &precision = app[\"float_precision_in_json\"];\n    if (!precision.isNull())\n    {\n        auto precisionLength = precision.get(\"precision\", 0).asUInt64();\n        auto precisionType =\n            precision.get(\"precision_type\", \"significant\").asString();\n        drogon::app().setFloatPrecisionInJson((unsigned int)precisionLength,\n                                              precisionType);\n    }\n    // log\n    loadLogSetting(app[\"log\"]);\n    // run as daemon\n    auto runAsDaemon = app.get(\"run_as_daemon\", false).asBool();\n    if (runAsDaemon)\n    {\n        drogon::app().enableRunAsDaemon();\n    }\n    // handle SIGTERM\n    auto handleSigterm = app.get(\"handle_sig_term\", true).asBool();\n    if (!handleSigterm)\n    {\n        drogon::app().disableSigtermHandling();\n    }\n    // relaunch\n    auto relaunch = app.get(\"relaunch_on_error\", false).asBool();\n    if (relaunch)\n    {\n        drogon::app().enableRelaunchOnError();\n    }\n    auto useSendfile = app.get(\"use_sendfile\", true).asBool();\n    drogon::app().enableSendfile(useSendfile);\n    auto useGzip = app.get(\"use_gzip\", true).asBool();\n    drogon::app().enableGzip(useGzip);\n    auto useBr = app.get(\"use_brotli\", false).asBool();\n    drogon::app().enableBrotli(useBr);\n    auto staticFilesCacheTime = app.get(\"static_files_cache_time\", 5).asInt();\n    drogon::app().setStaticFilesCacheTime(staticFilesCacheTime);\n    loadControllers(app[\"simple_controllers_map\"]);\n    // Kick off idle connections\n    auto kickOffTimeout = app.get(\"idle_connection_timeout\", 60).asUInt64();\n    drogon::app().setIdleConnectionTimeout(kickOffTimeout);\n    auto server = app.get(\"server_header_field\", \"\").asString();\n    if (!server.empty())\n        drogon::app().setServerHeaderField(server);\n    auto sendServerHeader = app.get(\"enable_server_header\", true).asBool();\n    drogon::app().enableServerHeader(sendServerHeader);\n    auto sendDateHeader = app.get(\"enable_date_header\", true).asBool();\n    drogon::app().enableDateHeader(sendDateHeader);\n    auto keepaliveReqs = app.get(\"keepalive_requests\", 0).asUInt64();\n    drogon::app().setKeepaliveRequestsNumber(keepaliveReqs);\n    auto pipeliningReqs = app.get(\"pipelining_requests\", 0).asUInt64();\n    drogon::app().setPipeliningRequestsNumber(pipeliningReqs);\n    auto useGzipStatic = app.get(\"gzip_static\", true).asBool();\n    drogon::app().setGzipStatic(useGzipStatic);\n    auto useBrStatic = app.get(\"br_static\", true).asBool();\n    drogon::app().setBrStatic(useBrStatic);\n    auto maxBodySize = app.get(\"client_max_body_size\", \"1M\").asString();\n    size_t size;\n    if (bytesSize(maxBodySize, size))\n    {\n        drogon::app().setClientMaxBodySize(size);\n    }\n    else\n    {\n        throw std::runtime_error(\"Error format of client_max_body_size\");\n    }\n    auto maxMemoryBodySize =\n        app.get(\"client_max_memory_body_size\", \"64K\").asString();\n    if (bytesSize(maxMemoryBodySize, size))\n    {\n        drogon::app().setClientMaxMemoryBodySize(size);\n    }\n    else\n    {\n        throw std::runtime_error(\"Error format of client_max_memory_body_size\");\n    }\n    auto maxWsMsgSize =\n        app.get(\"client_max_websocket_message_size\", \"128K\").asString();\n    if (bytesSize(maxWsMsgSize, size))\n    {\n        drogon::app().setClientMaxWebSocketMessageSize(size);\n    }\n    else\n    {\n        throw std::runtime_error(\n            \"Error format of client_max_websocket_message_size\");\n    }\n    drogon::app().enableReusePort(app.get(\"reuse_port\", false).asBool());\n    drogon::app().setHomePage(app.get(\"home_page\", \"index.html\").asString());\n    drogon::app().setImplicitPageEnable(\n        app.get(\"use_implicit_page\", true).asBool());\n    drogon::app().setImplicitPage(\n        app.get(\"implicit_page\", \"index.html\").asString());\n    auto mimes = app[\"mime\"];\n    if (!mimes.isNull())\n    {\n        auto names = mimes.getMemberNames();\n        for (const auto &mime : names)\n        {\n            auto ext = mimes[mime];\n            std::vector<std::string> exts;\n            if (ext.isString())\n                exts.push_back(ext.asString());\n            else if (ext.isArray())\n            {\n                for (const auto &extension : ext)\n                    exts.push_back(extension.asString());\n            }\n\n            for (const auto &extension : exts)\n                drogon::app().registerCustomExtensionMime(extension, mime);\n        }\n    }\n    bool enableCompressedRequests =\n        app.get(\"enabled_compressed_request\", false).asBool();\n    drogon::app().enableCompressedRequest(enableCompressedRequests);\n\n    drogon::app().enableRequestStream(\n        app.get(\"enable_request_stream\", false).asBool());\n}\n\nstatic void loadDbClients(const Json::Value &dbClients)\n{\n    if (!dbClients)\n        return;\n    for (auto const &client : dbClients)\n    {\n        auto type = client.get(\"rdbms\", \"postgresql\").asString();\n        std::transform(type.begin(),\n                       type.end(),\n                       type.begin(),\n                       [](unsigned char c) { return tolower(c); });\n        auto host = client.get(\"host\", \"127.0.0.1\").asString();\n        unsigned short port = client.get(\"port\", 5432).asUInt();\n        auto dbname = client.get(\"dbname\", \"\").asString();\n        if (dbname.empty() && type != \"sqlite3\")\n        {\n            throw std::runtime_error(\n                \"Please configure dbname in the configuration file\");\n        }\n        auto user = client.get(\"user\", \"postgres\").asString();\n        auto password = client.get(\"passwd\", \"\").asString();\n        if (password.empty())\n        {\n            password = client.get(\"password\", \"\").asString();\n        }\n        auto connNum = client.get(\"connection_number\", 1).asUInt();\n        if (connNum == 1)\n        {\n            connNum = client.get(\"number_of_connections\", 1).asUInt();\n        }\n        auto name = client.get(\"name\", \"default\").asString();\n        auto filename = client.get(\"filename\", \"\").asString();\n        auto isFast = client.get(\"is_fast\", false).asBool();\n        auto characterSet = client.get(\"characterSet\", \"\").asString();\n        if (characterSet.empty())\n        {\n            characterSet = client.get(\"client_encoding\", \"\").asString();\n        }\n        auto connectOptions = client.get(\"connect_options\", Json::Value());\n        auto timeout = client.get(\"timeout\", -1.0).asDouble();\n        auto autoBatch = client.get(\"auto_batch\", false).asBool();\n\n        std::unordered_map<std::string, std::string> options;\n        if (connectOptions.isObject() && !connectOptions.empty())\n        {\n            for (const auto &key : connectOptions.getMemberNames())\n            {\n                options[key] = connectOptions[key].asString();\n            }\n        }\n\n        HttpAppFrameworkImpl::instance().addDbClient(type,\n                                                     host,\n                                                     port,\n                                                     dbname,\n                                                     user,\n                                                     password,\n                                                     connNum,\n                                                     filename,\n                                                     name,\n                                                     isFast,\n                                                     characterSet,\n                                                     timeout,\n                                                     autoBatch,\n                                                     std::move(options));\n    }\n}\n\nstatic void loadRedisClients(const Json::Value &redisClients)\n{\n    if (!redisClients)\n        return;\n    for (auto const &client : redisClients)\n    {\n        std::promise<std::string> promise;\n        auto future = promise.get_future();\n        auto host = client.get(\"host\", \"127.0.0.1\").asString();\n        trantor::Resolver::newResolver()->resolve(\n            host, [&promise](const trantor::InetAddress &address) {\n                promise.set_value(address.toIp());\n            });\n        auto port = client.get(\"port\", 6379).asUInt();\n        auto username = client.get(\"username\", \"\").asString();\n        auto password = client.get(\"passwd\", \"\").asString();\n        if (password.empty())\n        {\n            password = client.get(\"password\", \"\").asString();\n        }\n        auto connNum = client.get(\"connection_number\", 1).asUInt();\n        if (connNum == 1)\n        {\n            connNum = client.get(\"number_of_connections\", 1).asUInt();\n        }\n        auto name = client.get(\"name\", \"default\").asString();\n        auto isFast = client.get(\"is_fast\", false).asBool();\n        auto timeout = client.get(\"timeout\", -1.0).asDouble();\n        auto db = client.get(\"db\", 0).asUInt();\n        auto hostIp = future.get();\n        drogon::app().createRedisClient(hostIp,\n                                        port,\n                                        name,\n                                        password,\n                                        connNum,\n                                        isFast,\n                                        timeout,\n                                        db,\n                                        username);\n    }\n}\n\nstatic void loadListeners(const Json::Value &listeners)\n{\n    if (!listeners)\n        return;\n    LOG_TRACE << \"Has \" << listeners.size() << \" listeners\";\n    for (auto const &listener : listeners)\n    {\n        auto addr = listener.get(\"address\", \"0.0.0.0\").asString();\n        auto port = (uint16_t)listener.get(\"port\", 0).asUInt();\n        auto useSSL = listener.get(\"https\", false).asBool();\n        auto cert = listener.get(\"cert\", \"\").asString();\n        auto key = listener.get(\"key\", \"\").asString();\n        auto useOldTLS = listener.get(\"use_old_tls\", false).asBool();\n        std::vector<std::pair<std::string, std::string>> sslConfCmds;\n        if (listener.isMember(\"ssl_conf\"))\n        {\n            for (const auto &opt : listener[\"ssl_conf\"])\n            {\n                if (opt.size() == 0 || opt.size() > 2)\n                {\n                    LOG_FATAL << \"SSL configuration option should be an 1 or \"\n                                 \"2-element array\";\n                    abort();\n                }\n                sslConfCmds.emplace_back(opt[0].asString(),\n                                         opt.get(1, \"\").asString());\n            }\n        }\n        LOG_TRACE << \"Add listener:\" << addr << \":\" << port;\n        drogon::app().addListener(\n            addr, port, useSSL, cert, key, useOldTLS, sslConfCmds);\n    }\n}\n\nstatic void loadSSL(const Json::Value &sslConf)\n{\n    if (!sslConf)\n        return;\n    auto key = sslConf.get(\"key\", \"\").asString();\n    auto cert = sslConf.get(\"cert\", \"\").asString();\n    drogon::app().setSSLFiles(cert, key);\n    std::vector<std::pair<std::string, std::string>> sslConfCmds;\n    if (sslConf.isMember(\"conf\"))\n    {\n        for (const auto &opt : sslConf[\"conf\"])\n        {\n            if (opt.size() == 0 || opt.size() > 2)\n            {\n                LOG_FATAL << \"SSL configuration option should be an 1 or \"\n                             \"2-element array\";\n                abort();\n            }\n            sslConfCmds.emplace_back(opt[0].asString(),\n                                     opt.get(1, \"\").asString());\n        }\n    }\n    drogon::app().setSSLConfigCommands(sslConfCmds);\n}\n\nvoid ConfigLoader::load()\n{\n    // std::cout<<configJsonRoot_<<std::endl;\n    loadApp(configJsonRoot_[\"app\"]);\n    loadSSL(configJsonRoot_[\"ssl\"]);\n    loadListeners(configJsonRoot_[\"listeners\"]);\n    loadDbClients(configJsonRoot_[\"db_clients\"]);\n    loadRedisClients(configJsonRoot_[\"redis_clients\"]);\n}\n"
  },
  {
    "path": "lib/src/ConfigLoader.h",
    "content": "/**\n *\n *  ConfigLoader.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <json/json.h>\n#include <string>\n#include <trantor/utils/NonCopyable.h>\n\nnamespace drogon\n{\nclass ConfigLoader : public trantor::NonCopyable\n{\n  public:\n    explicit ConfigLoader(const std::string &configFile) noexcept(false);\n    explicit ConfigLoader(const Json::Value &data);\n    explicit ConfigLoader(Json::Value &&data);\n    ~ConfigLoader();\n\n    const Json::Value &jsonValue() const\n    {\n        return configJsonRoot_;\n    }\n\n    void load() noexcept(false);\n\n  private:\n    std::string configFile_;\n    Json::Value configJsonRoot_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/ControllerBinderBase.h",
    "content": "/**\n *\n *  @file ControllerBinderBase.h\n *  @author Nitromelon\n *\n *  Copyright 2023, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <string>\n#include <vector>\n#include <memory>\n#include <drogon/IOThreadStorage.h>\n#include <drogon/HttpResponse.h>\n#include \"HttpRequestImpl.h\"\n\nnamespace drogon\n{\nclass HttpMiddlewareBase;\n\n/**\n * @brief A component to associate router class and controller class\n */\nstruct ControllerBinderBase\n{\n    std::string handlerName_;\n    std::vector<std::string> middlewareNames_;\n    std::vector<std::shared_ptr<HttpMiddlewareBase>> middlewares_;\n    IOThreadStorage<HttpResponsePtr> responseCache_;\n    std::shared_ptr<std::string> corsMethods_;\n    bool isCORS_{false};\n\n    virtual ~ControllerBinderBase() = default;\n    virtual void handleRequest(\n        const HttpRequestImplPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) const = 0;\n\n    virtual bool isStreamHandler() const\n    {\n        return false;\n    }\n};\n\nstruct RouteResult\n{\n    enum\n    {\n        Success,\n        MethodNotAllowed,\n        NotFound\n    } result;\n\n    std::shared_ptr<ControllerBinderBase> binderPtr;\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/Cookie.cc",
    "content": "/**\n *\n *  Cookie.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/Cookie.h>\n#include <drogon/utils/Utilities.h>\n#include <trantor/utils/Logger.h>\nusing namespace drogon;\n\nstd::string Cookie::cookieString() const\n{\n    constexpr std::string_view prefix = \"Set-Cookie: \";\n    std::string ret;\n    // reserve space to reduce frequency allocation\n    ret.reserve(prefix.size() + key_.size() + value_.size() + 30);\n    ret = prefix;\n    ret.append(key_).append(\"=\").append(value_).append(\"; \");\n    if (expiresDate_.microSecondsSinceEpoch() !=\n            (std::numeric_limits<int64_t>::max)() &&\n        expiresDate_.microSecondsSinceEpoch() >= 0)\n    {\n        ret.append(\"Expires=\")\n            .append(utils::getHttpFullDateStr(expiresDate_))\n            .append(\"; \");\n    }\n    if (maxAge_.has_value())\n    {\n        ret.append(\"Max-Age=\")\n            .append(std::to_string(maxAge_.value()))\n            .append(\"; \");\n    }\n    if (!domain_.empty())\n    {\n        ret.append(\"Domain=\").append(domain_).append(\"; \");\n    }\n    if (!path_.empty())\n    {\n        ret.append(\"Path=\").append(path_).append(\"; \");\n    }\n    if (sameSite_ != SameSite::kNull)\n    {\n        switch (sameSite_)\n        {\n            case SameSite::kLax:\n                ret.append(\"SameSite=Lax; \");\n                break;\n            case SameSite::kStrict:\n                ret.append(\"SameSite=Strict; \");\n                break;\n            case SameSite::kNone:\n                ret.append(\"SameSite=None; \");\n                // Cookies with SameSite=None must now also specify the Secure\n                // attribute (they require a secure context/HTTPS).\n                ret.append(\"Secure; \");\n                break;\n            default:\n                // Lax replaced None as the default value to ensure that users\n                // have reasonably robust defense against some CSRF attacks\n                ret.append(\"SameSite=Lax; \");\n        }\n    }\n    if ((secure_ && sameSite_ != SameSite::kNone) || partitioned_)\n    {\n        ret.append(\"Secure; \");\n    }\n    if (httpOnly_)\n    {\n        ret.append(\"HttpOnly; \");\n    }\n    if (partitioned_)\n    {\n        ret.append(\"Partitioned; \");\n    }\n    ret.resize(ret.length() - 2);  // delete last semicolon\n    ret.append(\"\\r\\n\");\n    return ret;\n}\n"
  },
  {
    "path": "lib/src/DbClientManager.h",
    "content": "/**\n *\n *  @file DbClientManager.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/orm/DbClient.h>\n#include <drogon/orm/DbConfig.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/IOThreadStorage.h>\n#include <trantor/utils/NonCopyable.h>\n#include <trantor/net/EventLoop.h>\n#include <string>\n#include <memory>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClientManager : public trantor::NonCopyable\n{\n  public:\n    void createDbClients(const std::vector<trantor::EventLoop *> &ioLoops);\n\n    DbClientPtr getDbClient(const std::string &name)\n    {\n        assert(dbClientsMap_.find(name) != dbClientsMap_.end());\n        return dbClientsMap_[name];\n    }\n\n    ~DbClientManager();\n\n    DbClientPtr getFastDbClient(const std::string &name)\n    {\n        auto iter = dbFastClientsMap_.find(name);\n        assert(iter != dbFastClientsMap_.end());\n        return iter->second.getThreadData();\n    }\n\n    void addDbClient(const DbConfig &config);\n    bool areAllDbClientsAvailable() const noexcept;\n\n  private:\n    std::map<std::string, DbClientPtr> dbClientsMap_;\n\n    struct DbInfo\n    {\n        std::string connectionInfo_;\n        DbConfig config_;\n    };\n\n    std::vector<DbInfo> dbInfos_;\n    std::map<std::string, IOThreadStorage<orm::DbClientPtr>> dbFastClientsMap_;\n};\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/DbClientManagerSkipped.cc",
    "content": "/**\n *\n *  DbClientManagerSkipped.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"DbClientManager.h\"\n#include <algorithm>\n#include <stdlib.h>\n\nusing namespace drogon::orm;\nusing namespace drogon;\n\nvoid DbClientManager::createDbClients(\n    const std::vector<trantor::EventLoop *> & /*ioLoops*/)\n{\n    return;\n}\n\nvoid DbClientManager::addDbClient(const DbConfig &)\n{\n    LOG_FATAL << \"No database is supported by drogon, please install the \"\n                 \"database development library first.\";\n    abort();\n}\n\nbool DbClientManager::areAllDbClientsAvailable() const noexcept\n{\n    LOG_FATAL << \"No database is supported by drogon, please install the \"\n                 \"database development library first.\";\n    abort();\n}\n\nDbClientManager::~DbClientManager()\n{\n}\n"
  },
  {
    "path": "lib/src/DrClassMap.cc",
    "content": "/**\n *\n *  DrClassMap.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/DrClassMap.h>\n#include <drogon/DrObject.h>\n#include <trantor/utils/Logger.h>\n\nusing namespace drogon;\n\nnamespace drogon\n{\nnamespace internal\n{\nstatic std::unordered_map<std::string, std::shared_ptr<DrObjectBase>> &\ngetObjsMap()\n{\n    static std::unordered_map<std::string, std::shared_ptr<DrObjectBase>>\n        singleInstanceMap;\n    return singleInstanceMap;\n}\n\nstatic std::mutex &getMapMutex()\n{\n    static std::mutex mtx;\n    return mtx;\n}\n\n}  // namespace internal\n}  // namespace drogon\n\nvoid DrClassMap::registerClass(const std::string &className,\n                               const DrAllocFunc &func,\n                               const DrSharedAllocFunc &sharedFunc)\n{\n    LOG_TRACE << \"Register class:\" << className;\n    getMap().insert(\n        std::make_pair(className, std::make_pair(func, sharedFunc)));\n}\n\nDrObjectBase *DrClassMap::newObject(const std::string &className)\n{\n    auto iter = getMap().find(className);\n    if (iter != getMap().end())\n    {\n        return iter->second.first();\n    }\n    else\n        return nullptr;\n}\n\nstd::shared_ptr<DrObjectBase> DrClassMap::newSharedObject(\n    const std::string &className)\n{\n    auto iter = getMap().find(className);\n    if (iter != getMap().end())\n    {\n        if (iter->second.second)\n            return iter->second.second();\n        else\n            return std::shared_ptr<DrObjectBase>(iter->second.first());\n    }\n    else\n        return nullptr;\n}\n\nconst std::shared_ptr<DrObjectBase> &DrClassMap::getSingleInstance(\n    const std::string &className)\n{\n    auto &mtx = internal::getMapMutex();\n    auto &singleInstanceMap = internal::getObjsMap();\n    {\n        std::lock_guard<std::mutex> lock(mtx);\n        auto iter = singleInstanceMap.find(className);\n        if (iter != singleInstanceMap.end())\n            return iter->second;\n    }\n    auto newObj = newSharedObject(className);\n    {\n        std::lock_guard<std::mutex> lock(mtx);\n        auto ret = singleInstanceMap.insert(\n            std::make_pair(className, std::move(newObj)));\n        return ret.first->second;\n    }\n}\n\nvoid DrClassMap::setSingleInstance(const std::shared_ptr<DrObjectBase> &ins)\n{\n    auto &mtx = internal::getMapMutex();\n    auto &singleInstanceMap = internal::getObjsMap();\n    std::lock_guard<std::mutex> lock(mtx);\n    singleInstanceMap[ins->className()] = ins;\n}\n\nstd::vector<std::string> DrClassMap::getAllClassName()\n{\n    std::vector<std::string> ret;\n    for (auto const &iter : getMap())\n    {\n        ret.push_back(iter.first);\n    }\n    return ret;\n}\n\nstd::unordered_map<std::string, std::pair<DrAllocFunc, DrSharedAllocFunc>> &\nDrClassMap::getMap()\n{\n    static std::unordered_map<std::string,\n                              std::pair<DrAllocFunc, DrSharedAllocFunc>>\n        map;\n    return map;\n}\n"
  },
  {
    "path": "lib/src/DrTemplateBase.cc",
    "content": "/**\n *\n *  @file DrTemplateBase.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/DrClassMap.h>\n#include <drogon/DrTemplateBase.h>\n#include <trantor/utils/Logger.h>\n#include <memory>\n#include <regex>\n\nusing namespace drogon;\n\nstd::shared_ptr<DrTemplateBase> DrTemplateBase::newTemplate(\n    const std::string &templateName)\n{\n    LOG_TRACE << \"http view name=\" << templateName;\n    auto l = templateName.length();\n    if (l >= 4 && templateName[l - 4] == '.' && templateName[l - 3] == 'c' &&\n        templateName[l - 2] == 's' && templateName[l - 1] == 'p')\n    {\n        std::string::size_type pos = 0;\n        std::string newName;\n        newName.reserve(templateName.size());\n        if (templateName[0] == '/' || templateName[0] == '\\\\')\n        {\n            pos = 1;\n        }\n        else if (templateName[0] == '.' &&\n                 (templateName[1] == '/' || templateName[1] == '\\\\'))\n        {\n            pos = 2;\n        }\n        while (pos < l - 4)\n        {\n            if (templateName[pos] == '/' || templateName[pos] == '\\\\')\n            {\n                newName.append(\"::\");\n            }\n            else\n            {\n                newName.append(1, templateName[pos]);\n            }\n            ++pos;\n        }\n        return std::shared_ptr<DrTemplateBase>(dynamic_cast<DrTemplateBase *>(\n            drogon::DrClassMap::newObject(newName)));\n    }\n    else\n    {\n        return std::shared_ptr<DrTemplateBase>(dynamic_cast<DrTemplateBase *>(\n            drogon::DrClassMap::newObject(templateName)));\n    }\n}\n"
  },
  {
    "path": "lib/src/FixedWindowRateLimiter.cc",
    "content": "#include \"FixedWindowRateLimiter.h\"\n\nusing namespace drogon;\n\nFixedWindowRateLimiter::FixedWindowRateLimiter(\n    size_t capacity,\n    std::chrono::duration<double> timeUnit)\n    : capacity_(capacity),\n      lastTime_(std::chrono::steady_clock::now()),\n      timeUnit_(timeUnit)\n{\n}\n\n// implementation of the fixed window algorithm\n\nbool FixedWindowRateLimiter::isAllowed()\n{\n    auto now = std::chrono::steady_clock::now();\n    auto duration = std::chrono::duration_cast<std::chrono::duration<double>>(\n        now - lastTime_);\n    if (duration >= timeUnit_)\n    {\n        currentRequests_ = 0;\n        lastTime_ = now;\n    }\n    if (currentRequests_ < capacity_)\n    {\n        currentRequests_++;\n        return true;\n    }\n    return false;\n}\n"
  },
  {
    "path": "lib/src/FixedWindowRateLimiter.h",
    "content": "#pragma once\n\n#include <drogon/RateLimiter.h>\n#include <chrono>\n\nnamespace drogon\n{\nclass FixedWindowRateLimiter : public RateLimiter\n{\n  public:\n    FixedWindowRateLimiter(size_t capacity,\n                           std::chrono::duration<double> timeUnit);\n    bool isAllowed() override;\n    ~FixedWindowRateLimiter() noexcept override = default;\n\n  private:\n    size_t capacity_;\n    size_t currentRequests_{0};\n    std::chrono::steady_clock::time_point lastTime_;\n    std::chrono::duration<double> timeUnit_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/GlobalFilters.cc",
    "content": "#include <drogon/plugins/GlobalFilters.h>\n#include <drogon/DrClassMap.h>\n#include <drogon/HttpAppFramework.h>\n#include \"MiddlewaresFunction.h\"\n#include \"HttpRequestImpl.h\"\n#include \"HttpAppFrameworkImpl.h\"\n\nusing namespace drogon::plugin;\n\nvoid GlobalFilters::initAndStart(const Json::Value &config)\n{\n    if (config.isMember(\"filters\") && config[\"filters\"].isArray())\n    {\n        auto &filters = config[\"filters\"];\n\n        for (auto const &filter : filters)\n        {\n            if (filter.isString())\n            {\n                auto filterPtr = std::dynamic_pointer_cast<HttpFilterBase>(\n                    drogon::DrClassMap::getSingleInstance(filter.asString()));\n                if (filterPtr)\n                {\n                    filters_.push_back(filterPtr);\n                }\n                else\n                {\n                    LOG_ERROR << \"Filter \" << filter.asString()\n                              << \" not found!\";\n                }\n            }\n        }\n    }\n    if (config.isMember(\"exempt\"))\n    {\n        auto exempt = config[\"exempt\"];\n        if (exempt.isArray())\n        {\n            std::string regexStr;\n            for (auto const &ex : exempt)\n            {\n                if (ex.isString())\n                {\n                    regexStr.append(\"(\").append(ex.asString()).append(\")|\");\n                }\n                else\n                {\n                    LOG_ERROR << \"exempt must be a string array!\";\n                }\n            }\n            if (!regexStr.empty())\n            {\n                regexStr.pop_back();\n                exemptPegex_ = std::regex(regexStr);\n                regexFlag_ = true;\n            }\n        }\n        else if (exempt.isString())\n        {\n            exemptPegex_ = std::regex(exempt.asString());\n            regexFlag_ = true;\n        }\n        else\n        {\n            LOG_ERROR << \"exempt must be a string or string array!\";\n        }\n    }\n    std::weak_ptr<GlobalFilters> weakPtr = shared_from_this();\n    drogon::app().registerPreRoutingAdvice(\n        [weakPtr](const drogon::HttpRequestPtr &req,\n                  drogon::AdviceCallback &&acb,\n                  drogon::AdviceChainCallback &&accb) {\n            auto thisPtr = weakPtr.lock();\n            if (!thisPtr)\n            {\n                accb();\n                return;\n            }\n            if (thisPtr->regexFlag_)\n            {\n                if (std::regex_match(req->path(), thisPtr->exemptPegex_))\n                {\n                    accb();\n                    return;\n                }\n            }\n\n            drogon::middlewares_function::doFilters(\n                thisPtr->filters_,\n                std::static_pointer_cast<HttpRequestImpl>(req),\n                [acb = std::move(acb),\n                 accb = std::move(accb)](const HttpResponsePtr &resp) {\n                    if (resp)\n                    {\n                        acb(resp);\n                    }\n                    else\n                    {\n                        accb();\n                    }\n                });\n        });\n}\n\nvoid GlobalFilters::shutdown()\n{\n    filters_.clear();\n}\n"
  },
  {
    "path": "lib/src/Histogram.cc",
    "content": "#include <drogon/utils/monitoring/Histogram.h>\nusing namespace drogon;\nusing namespace drogon::monitoring;\n\nvoid Histogram::observe(double value)\n{\n    std::lock_guard<std::mutex> lock(mutex_);\n    if (maxAge_ > std::chrono::seconds(0) &&\n        timerId_ == trantor::InvalidTimerId)\n    {\n        std::weak_ptr<Histogram> weakPtr =\n            std::dynamic_pointer_cast<Histogram>(shared_from_this());\n        timerId_ = loopPtr_->runEvery(maxAge_ / timeBucketCount_, [weakPtr]() {\n            auto thisPtr = weakPtr.lock();\n            if (!thisPtr)\n                return;\n            thisPtr->rotateTimeBuckets();\n        });\n    }\n    auto &currentBucket = timeBuckets_.back();\n    currentBucket.sum += value;\n    currentBucket.count += 1;\n    for (size_t i = 0; i < bucketBoundaries_.size(); i++)\n    {\n        if (value <= bucketBoundaries_[i])\n        {\n            currentBucket.buckets[i] += 1;\n            break;\n        }\n    }\n    if (value > bucketBoundaries_.back())\n    {\n        currentBucket.buckets.back() += 1;\n    }\n}\n\nstd::vector<Sample> Histogram::collect() const\n{\n    std::vector<Sample> samples;\n    std::lock_guard<std::mutex> guard(mutex_);\n    size_t count{0};\n    for (size_t i = 0; i < bucketBoundaries_.size(); i++)\n    {\n        Sample sample;\n        for (auto &bucket : timeBuckets_)\n        {\n            count += bucket.buckets[i];\n        }\n        sample.name = name_ + \"_bucket\";\n        sample.exLabels.emplace_back(\"le\",\n                                     std::to_string(bucketBoundaries_[i]));\n        sample.value = count;\n        samples.emplace_back(std::move(sample));\n    }\n    Sample sample;\n    for (auto &bucket : timeBuckets_)\n    {\n        count += bucket.buckets.back();\n    }\n    sample.name = name_ + \"_bucket\";\n    sample.exLabels.emplace_back(\"le\", \"+Inf\");\n    sample.value = count;\n    samples.emplace_back(std::move(sample));\n    double sum{0};\n    uint64_t totalCount{0};\n    for (auto &bucket : timeBuckets_)\n    {\n        sum += bucket.sum;\n        totalCount += bucket.count;\n    }\n    Sample sumSample;\n    sumSample.name = name_ + \"_sum\";\n    sumSample.value = sum;\n    samples.emplace_back(std::move(sumSample));\n    Sample countSample;\n    countSample.name = name_ + \"_count\";\n    countSample.value = totalCount;\n    samples.emplace_back(std::move(countSample));\n    return samples;\n}\n"
  },
  {
    "path": "lib/src/Hodor.cc",
    "content": "#include <drogon/plugins/Hodor.h>\n#include <drogon/plugins/RealIpResolver.h>\n\nusing namespace drogon::plugin;\n\nHodor::LimitStrategy Hodor::makeLimitStrategy(const Json::Value &config)\n{\n    LimitStrategy strategy;\n    strategy.capacity = config.get(\"capacity\", 0).asUInt();\n    if (config.isMember(\"urls\") && config[\"urls\"].isArray())\n    {\n        std::string regexString;\n        for (auto &str : config[\"urls\"])\n        {\n            assert(str.isString());\n            regexString.append(\"(\").append(str.asString()).append(\")|\");\n        }\n        if (!regexString.empty())\n        {\n            regexString.resize(regexString.length() - 1);\n            strategy.urlsRegex = std::regex(regexString);\n            strategy.regexFlag = true;\n        }\n    }\n\n    if (strategy.capacity > 0)\n    {\n        if (multiThreads_)\n        {\n            strategy.globalLimiterPtr = std::make_shared<SafeRateLimiter>(\n                RateLimiter::newRateLimiter(algorithm_,\n                                            strategy.capacity,\n                                            timeUnit_));\n        }\n        else\n        {\n            strategy.globalLimiterPtr =\n                RateLimiter::newRateLimiter(algorithm_,\n                                            strategy.capacity,\n                                            timeUnit_);\n        }\n    }\n    strategy.ipCapacity = config.get(\"ip_capacity\", 0).asUInt();\n    if (strategy.ipCapacity > 0)\n    {\n        strategy.ipLimiterMapPtr =\n            std::make_unique<CacheMap<std::string, RateLimiterPtr>>(\n                drogon::app().getLoop(),\n                float(timeUnit_.count() / 60 < 1 ? 1 : timeUnit_.count() / 60),\n                2,\n                100);\n    }\n\n    strategy.userCapacity = config.get(\"user_capacity\", 0).asUInt();\n    if (strategy.userCapacity > 0)\n    {\n        strategy.userLimiterMapPtr =\n            std::make_unique<CacheMap<std::string, RateLimiterPtr>>(\n                drogon::app().getLoop(),\n                float(timeUnit_.count() / 60 < 1 ? 1 : timeUnit_.count() / 60),\n                2,\n                100);\n    }\n    return strategy;\n}\n\nvoid Hodor::initAndStart(const Json::Value &config)\n{\n    algorithm_ = stringToRateLimiterType(\n        config.get(\"algorithm\", \"token_bucket\").asString());\n    timeUnit_ = std::chrono::seconds(config.get(\"time_unit\", 60).asUInt());\n\n    multiThreads_ = config.get(\"multi_threads\", true).asBool();\n\n    useRealIpResolver_ = config.get(\"use_real_ip_resolver\", false).asBool();\n    rejectResponse_ = HttpResponse::newHttpResponse();\n    rejectResponse_->setStatusCode(k429TooManyRequests);\n    rejectResponse_->setBody(\n        config.get(\"rejection_message\", \"Too many requests\").asString());\n    rejectResponse_->setCloseConnection(true);\n    limiterExpireTime_ =\n        (std::max)(static_cast<size_t>(\n                       config.get(\"limiter_expire_time\", 600).asUInt()),\n                   static_cast<size_t>(timeUnit_.count() * 3));\n    limitStrategies_.emplace_back(makeLimitStrategy(config));\n    if (config.isMember(\"sub_limits\") && config[\"sub_limits\"].isArray())\n    {\n        for (auto &subLimit : config[\"sub_limits\"])\n        {\n            assert(subLimit.isObject());\n            if (!subLimit[\"urls\"].isArray() || subLimit[\"urls\"].size() == 0)\n            {\n                LOG_ERROR\n                    << \"The urls of sub_limits must be an array and not empty!\";\n                continue;\n            }\n            if (subLimit[\"capacity\"].asUInt() == 0 &&\n                subLimit[\"ip_capacity\"].asUInt() == 0 &&\n                subLimit[\"user_capacity\"].asUInt() == 0)\n            {\n                LOG_ERROR << \"At least one capacity of sub_limits must be \"\n                             \"greater than 0!\";\n                continue;\n            }\n            limitStrategies_.emplace_back(makeLimitStrategy(subLimit));\n        }\n    }\n\n    const Json::Value &trustIps = config[\"trust_ips\"];\n    if (!trustIps.isNull() && !trustIps.isArray())\n    {\n        throw std::runtime_error(\"Invalid trusted_ips. Should be array.\");\n    }\n    for (const auto &ipOrCidr : trustIps)\n    {\n        trustCIDRs_.emplace_back(ipOrCidr.asString());\n    }\n\n    app().registerPreHandlingAdvice([this](const drogon::HttpRequestPtr &req,\n                                           AdviceCallback &&acb,\n                                           AdviceChainCallback &&accb) {\n        onHttpRequest(req, std::move(acb), std::move(accb));\n    });\n}\n\nvoid Hodor::shutdown()\n{\n    LOG_TRACE << \"Hodor plugin is shutdown!\";\n}\n\nbool Hodor::checkLimit(const drogon::HttpRequestPtr &req,\n                       const LimitStrategy &strategy,\n                       const trantor::InetAddress &ip,\n                       const std::optional<std::string> &userId)\n{\n    if (RealIpResolver::matchCidr(ip, trustCIDRs_))\n    {\n        return true;\n    }\n    if (strategy.regexFlag)\n    {\n        if (!std::regex_match(req->path(), strategy.urlsRegex))\n        {\n            return true;\n        }\n    }\n    if (strategy.globalLimiterPtr)\n    {\n        if (!strategy.globalLimiterPtr->isAllowed())\n        {\n            return false;\n        }\n    }\n    if (strategy.ipCapacity > 0)\n    {\n        RateLimiterPtr limiterPtr;\n        strategy.ipLimiterMapPtr->modify(\n            ip.toIpNetEndian(),\n            [this, &limiterPtr, &strategy](RateLimiterPtr &ptr) {\n                if (!ptr)\n                {\n                    if (multiThreads_)\n                    {\n                        ptr = std::make_shared<SafeRateLimiter>(\n                            RateLimiter::newRateLimiter(algorithm_,\n                                                        strategy.ipCapacity,\n                                                        timeUnit_));\n                    }\n                    else\n                    {\n                        ptr = RateLimiter::newRateLimiter(algorithm_,\n                                                          strategy.ipCapacity,\n                                                          timeUnit_);\n                    }\n                }\n                limiterPtr = ptr;\n            },\n            limiterExpireTime_);\n        if (!limiterPtr->isAllowed())\n        {\n            return false;\n        }\n    }\n    if (strategy.userCapacity > 0)\n    {\n        if (!userId.has_value())\n        {\n            return true;\n        }\n        RateLimiterPtr limiterPtr;\n        strategy.userLimiterMapPtr->modify(\n            *userId,\n            [this, &strategy, &limiterPtr](RateLimiterPtr &ptr) {\n                if (!ptr)\n                {\n                    if (multiThreads_)\n                    {\n                        ptr = std::make_shared<SafeRateLimiter>(\n                            RateLimiter::newRateLimiter(algorithm_,\n                                                        strategy.userCapacity,\n                                                        timeUnit_));\n                    }\n                    else\n                    {\n                        ptr = RateLimiter::newRateLimiter(algorithm_,\n                                                          strategy.userCapacity,\n                                                          timeUnit_);\n                    }\n                }\n                limiterPtr = ptr;\n            },\n            limiterExpireTime_);\n        if (!limiterPtr->isAllowed())\n        {\n            return false;\n        }\n    }\n    return true;\n}\n\nvoid Hodor::onHttpRequest(const drogon::HttpRequestPtr &req,\n                          drogon::AdviceCallback &&adviceCallback,\n                          drogon::AdviceChainCallback &&chainCallback)\n{\n    const trantor::InetAddress &ip =\n        useRealIpResolver_ ? drogon::plugin::RealIpResolver::GetRealAddr(req)\n                           : req->peerAddr();\n    std::optional<std::string> userId;\n    if (userIdGetter_)\n    {\n        userId = userIdGetter_(req);\n    }\n    for (auto &strategy : limitStrategies_)\n    {\n        if (!checkLimit(req, strategy, ip, userId))\n        {\n            if (rejectResponseFactory_)\n            {\n                adviceCallback(rejectResponseFactory_(req));\n            }\n            else\n            {\n                adviceCallback(rejectResponse_);\n            }\n            return;\n        }\n    }\n    chainCallback();\n}\n"
  },
  {
    "path": "lib/src/HttpAppFrameworkImpl.cc",
    "content": "/**\n *\n *  @file HttpAppFrameworkImpl.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpAppFrameworkImpl.h\"\n#include <drogon/DrClassMap.h>\n#include <drogon/HttpResponse.h>\n#include <drogon/HttpTypes.h>\n#include <drogon/utils/Utilities.h>\n#include <drogon/version.h>\n#include <json/json.h>\n#include <trantor/utils/AsyncFileLogger.h>\n#include <algorithm>\n#include \"AOPAdvice.h\"\n#include \"ConfigLoader.h\"\n#include \"DbClientManager.h\"\n#include \"HttpClientImpl.h\"\n#include \"HttpConnectionLimit.h\"\n#include \"HttpControllersRouter.h\"\n#include \"HttpRequestImpl.h\"\n#include \"HttpResponseImpl.h\"\n#include \"HttpServer.h\"\n#include \"HttpUtils.h\"\n#include \"ListenerManager.h\"\n#include \"PluginsManager.h\"\n#include \"RedisClientManager.h\"\n#include \"SessionManager.h\"\n#include \"SharedLibManager.h\"\n#include \"StaticFileRouter.h\"\n\n#include <iostream>\n#include <memory>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n#include <filesystem>\n\n#include <fcntl.h>\n#include <sys/stat.h>\n#ifndef _WIN32\n#include <sys/wait.h>\n#include <unistd.h>\n#define os_access access\n#elif !defined(_WIN32) || defined(__MINGW32__)\n#include <sys/file.h>\n#include <unistd.h>\n#define os_access access\n#else\n#include <io.h>\n#define os_access _waccess\n#define R_OK 04\n#define W_OK 02\n#endif\n\n#ifdef DROGON_SPDLOG_SUPPORT\n#include <spdlog/spdlog.h>\n#include <spdlog/logger.h>\n#include <spdlog/sinks/stdout_color_sinks.h>\n#include <spdlog/sinks/rotating_file_sink.h>\n#ifdef _WIN32\n#include <spdlog/sinks/msvc_sink.h>\n// Damn antedeluvian M$ macros\n#undef min\n#undef max\n#endif\n#endif  // DROGON_SPDLOG_SUPPORT\n\nusing namespace drogon;\nusing namespace std::placeholders;\n\nHttpAppFrameworkImpl::HttpAppFrameworkImpl()\n    : listenerManagerPtr_(new ListenerManager),\n      pluginsManagerPtr_(new PluginsManager),\n      dbClientManagerPtr_(new orm::DbClientManager),\n      redisClientManagerPtr_(new nosql::RedisClientManager),\n      uploadPath_(rootPath_ + \"uploads\")\n{\n}\n\nstatic std::function<void()> f = [] {\n    LOG_TRACE << \"Initialize the main event loop in the main thread\";\n};\n\n/// Make sure that the main event loop is initialized in the main thread.\ndrogon::InitBeforeMainFunction drogon::HttpAppFrameworkImpl::initFirst_([]() {\n    HttpAppFrameworkImpl::instance().getLoop()->runInLoop(f);\n});\n\nnamespace drogon\n{\nstd::string getVersion()\n{\n    return DROGON_VERSION;\n}\n\nstd::string getGitCommit()\n{\n    return DROGON_VERSION_SHA1;\n}\n\nHttpResponsePtr defaultErrorHandler(HttpStatusCode code, const HttpRequestPtr &)\n{\n    return std::make_shared<HttpResponseImpl>(code, CT_TEXT_HTML);\n}\n\nvoid defaultExceptionHandler(\n    const std::exception &e,\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    std::string pathWithQuery = req->path();\n    if (req->query().empty() == false)\n        pathWithQuery += \"?\" + req->query();\n    LOG_ERROR << \"Unhandled exception in \" << pathWithQuery\n              << \", what(): \" << e.what();\n    const auto &handler = app().getCustomErrorHandler();\n    callback(handler(k500InternalServerError, req));\n}\n\nstatic void godaemon()\n{\n    printf(\"Initializing daemon mode\\n\");\n#ifndef _WIN32\n    if (getppid() != 1)\n    {\n        pid_t pid;\n        pid = fork();\n        if (pid > 0)\n            exit(0);  // parent\n        if (pid < 0)\n        {\n            perror(\"fork\");\n            exit(1);\n        }\n        setsid();\n    }\n\n    // redirect stdin/stdout/stderr to /dev/null\n    close(0);\n    close(1);\n    close(2);\n\n    int ret = open(\"/dev/null\", O_RDWR);\n    (void)ret;\n    ret = dup(0);\n    (void)ret;\n    ret = dup(0);\n    (void)ret;\n    umask(0);\n#else\n    LOG_ERROR << \"Cannot run as daemon in Windows\";\n    exit(1);\n#endif\n\n    return;\n}\n\nstatic void TERMFunction(int sig)\n{\n    if (sig == SIGTERM)\n    {\n        LOG_WARN << \"SIGTERM signal received.\";\n        HttpAppFrameworkImpl::instance().getTermSignalHandler()();\n    }\n    else if (sig == SIGINT)\n    {\n        LOG_WARN << \"SIGINT signal received.\";\n        HttpAppFrameworkImpl::instance().getIntSignalHandler()();\n    }\n}\n\n}  // namespace drogon\n\nHttpAppFrameworkImpl::~HttpAppFrameworkImpl() noexcept\n{\n// Destroy the following objects before the loop destruction\n#if !defined(_WIN32) && !TARGET_OS_IOS\n    sharedLibManagerPtr_.reset();\n#endif\n    sessionManagerPtr_.reset();\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setStaticFilesCacheTime(int cacheTime)\n{\n    StaticFileRouter::instance().setStaticFilesCacheTime(cacheTime);\n    return *this;\n}\n\nint HttpAppFrameworkImpl::staticFilesCacheTime() const\n{\n    return StaticFileRouter::instance().staticFilesCacheTime();\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setGzipStatic(bool useGzipStatic)\n{\n    StaticFileRouter::instance().setGzipStatic(useGzipStatic);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setBrStatic(bool useGzipStatic)\n{\n    StaticFileRouter::instance().setBrStatic(useGzipStatic);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setImplicitPageEnable(\n    bool useImplicitPage)\n{\n    StaticFileRouter::instance().setImplicitPageEnable(useImplicitPage);\n    return *this;\n}\n\nbool HttpAppFrameworkImpl::isImplicitPageEnabled() const\n{\n    return StaticFileRouter::instance().isImplicitPageEnabled();\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setImplicitPage(\n    const std::string &implicitPageFile)\n{\n    StaticFileRouter::instance().setImplicitPage(implicitPageFile);\n    return *this;\n}\n\nconst std::string &HttpAppFrameworkImpl::getImplicitPage() const\n{\n    return StaticFileRouter::instance().getImplicitPage();\n}\n#if !defined(_WIN32) && !TARGET_OS_IOS\nHttpAppFramework &HttpAppFrameworkImpl::enableDynamicViewsLoading(\n    const std::vector<std::string> &libPaths,\n    const std::string &outputPath)\n{\n    assert(!running_);\n\n    for (auto const &libpath : libPaths)\n    {\n        if (libpath[0] == '/' ||\n            (libpath.length() >= 2 && libpath[0] == '.' && libpath[1] == '/') ||\n            (libpath.length() >= 3 && libpath[0] == '.' && libpath[1] == '.' &&\n             libpath[2] == '/') ||\n            libpath == \".\" || libpath == \"..\")\n        {\n            libFilePaths_.push_back(libpath);\n        }\n        else\n        {\n            if (rootPath_[rootPath_.length() - 1] == '/')\n                libFilePaths_.push_back(rootPath_ + libpath);\n            else\n                libFilePaths_.push_back(rootPath_ + \"/\" + libpath);\n        }\n    }\n    libFileOutputPath_ = outputPath;\n    if (!libFileOutputPath_.empty())\n    {\n        if (drogon::utils::createPath(libFileOutputPath_) == -1)\n        {\n            LOG_FATAL << \"Can't create \" << libFileOutputPath_\n                      << \" path for dynamic views\";\n            exit(-1);\n        }\n    }\n\n    return *this;\n}\n#endif\nHttpAppFramework &HttpAppFrameworkImpl::setFileTypes(\n    const std::vector<std::string> &types)\n{\n    StaticFileRouter::instance().setFileTypes(types);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::registerWebSocketController(\n    const std::string &pathName,\n    const std::string &ctrlName,\n    const std::vector<internal::HttpConstraint> &constraints)\n{\n    assert(!routersInit_);\n    HttpControllersRouter::instance().registerWebSocketController(pathName,\n                                                                  ctrlName,\n                                                                  constraints);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::registerWebSocketControllerRegex(\n    const std::string &regExp,\n    const std::string &ctrlName,\n    const std::vector<internal::HttpConstraint> &constraints)\n{\n    assert(!routersInit_);\n    HttpControllersRouter::instance().registerWebSocketControllerRegex(\n        regExp, ctrlName, constraints);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::registerHttpSimpleController(\n    const std::string &pathName,\n    const std::string &ctrlName,\n    const std::vector<internal::HttpConstraint> &constraints)\n{\n    assert(!routersInit_);\n    HttpControllersRouter::instance().registerHttpSimpleController(pathName,\n                                                                   ctrlName,\n                                                                   constraints);\n    return *this;\n}\n\nvoid HttpAppFrameworkImpl::registerHttpController(\n    const std::string &pathPattern,\n    const internal::HttpBinderBasePtr &binder,\n    const std::vector<HttpMethod> &validMethods,\n    const std::vector<std::string> &middlewareNames,\n    const std::string &handlerName)\n{\n    assert(!pathPattern.empty());\n    assert(binder);\n    assert(!routersInit_);\n    HttpControllersRouter::instance().addHttpPath(\n        pathPattern, binder, validMethods, middlewareNames, handlerName);\n}\n\nvoid HttpAppFrameworkImpl::registerHttpControllerViaRegex(\n    const std::string &regExp,\n    const internal::HttpBinderBasePtr &binder,\n    const std::vector<HttpMethod> &validMethods,\n    const std::vector<std::string> &middlewareNames,\n    const std::string &handlerName)\n{\n    assert(!regExp.empty());\n    assert(binder);\n    assert(!routersInit_);\n    HttpControllersRouter::instance().addHttpRegex(\n        regExp, binder, validMethods, middlewareNames, handlerName);\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setThreadNum(size_t threadNum)\n{\n    if (threadNum == 0)\n    {\n        threadNum_ = std::thread::hardware_concurrency();\n        return *this;\n    }\n    threadNum_ = threadNum;\n    return *this;\n}\n\nPluginBase *HttpAppFrameworkImpl::getPlugin(const std::string &name)\n{\n    return pluginsManagerPtr_->getPlugin(name);\n}\n\nstd::shared_ptr<PluginBase> HttpAppFrameworkImpl::getSharedPlugin(\n    const std::string &name)\n{\n    return pluginsManagerPtr_->getSharedPlugin(name);\n}\n\nvoid HttpAppFrameworkImpl::addPlugin(\n    const std::string &name,\n    const std::vector<std::string> &dependencies,\n    const Json::Value &config)\n{\n    assert(!isRunning());\n    Json::Value pluginConfig;\n    pluginConfig[\"name\"] = name;\n    Json::Value deps(Json::arrayValue);\n    for (const auto &dep : dependencies)\n    {\n        deps.append(dep);\n    }\n    pluginConfig[\"dependencies\"] = deps;\n    pluginConfig[\"config\"] = config;\n    auto &plugins = jsonRuntimeConfig_[\"plugins\"];\n    plugins.append(pluginConfig);\n}\n\nvoid HttpAppFrameworkImpl::addPlugins(const Json::Value &configs)\n{\n    assert(!isRunning());\n    assert(configs.isArray());\n    auto &plugins = jsonRuntimeConfig_[\"plugins\"];\n    for (const auto &config : configs)\n    {\n        plugins.append(config);\n    }\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::addListener(\n    const std::string &ip,\n    uint16_t port,\n    bool useSSL,\n    const std::string &certFile,\n    const std::string &keyFile,\n    bool useOldTLS,\n    const std::vector<std::pair<std::string, std::string>> &sslConfCmds)\n{\n    assert(!running_);\n    listenerManagerPtr_->addListener(\n        ip, port, useSSL, certFile, keyFile, useOldTLS, sslConfCmds);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setMaxConnectionNum(\n    size_t maxConnections)\n{\n    HttpConnectionLimit::instance().setMaxConnectionNum(maxConnections);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setMaxConnectionNumPerIP(\n    size_t maxConnectionsPerIP)\n{\n    HttpConnectionLimit::instance().setMaxConnectionNumPerIP(\n        maxConnectionsPerIP);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::loadConfigFile(\n    const std::string &fileName)\n{\n    ConfigLoader loader(fileName);\n    loader.load();\n    jsonConfig_ = loader.jsonValue();\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::loadConfigJson(const Json::Value &data)\n{\n    ConfigLoader loader(data);\n    loader.load();\n    jsonConfig_ = loader.jsonValue();\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::loadConfigJson(Json::Value &&data)\n{\n    ConfigLoader loader(std::move(data));\n    loader.load();\n    jsonConfig_ = loader.jsonValue();\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setLogPath(\n    const std::string &logPath,\n    const std::string &logfileBaseName,\n    size_t logfileSize,\n    size_t maxFiles,\n    bool useSpdlog)\n{\n#ifdef DROGON_SPDLOG_SUPPORT\n    logWithSpdlog_ = trantor::Logger::hasSpdLogSupport() && useSpdlog;\n#endif\n    if (logPath.empty())\n        return *this;\n    // std::filesystem does not provide a method to check access permissions, so\n    // keep existing code\n    if (os_access(utils::toNativePath(logPath).c_str(), 0) != 0)\n    {\n        std::cerr << \"Log path does not exist!\\n\";\n        exit(1);\n    }\n    if (os_access(utils::toNativePath(logPath).c_str(), R_OK | W_OK) != 0)\n    {\n        std::cerr << \"Unable to access log path!\\n\";\n        exit(1);\n    }\n    logPath_ = logPath;\n    logfileBaseName_ = logfileBaseName;\n    logfileSize_ = logfileSize;\n    logfileMaxNum_ = maxFiles;\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setLogLevel(\n    trantor::Logger::LogLevel level)\n{\n    trantor::Logger::setLogLevel(level);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setLogLocalTime(bool on)\n{\n    trantor::Logger::setDisplayLocalTime(on);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setSSLConfigCommands(\n    const std::vector<std::pair<std::string, std::string>> &sslConfCmds)\n{\n    sslConfCmds_ = sslConfCmds;\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setSSLFiles(const std::string &certPath,\n                                                    const std::string &keyPath)\n{\n    sslCertPath_ = certPath;\n    sslKeyPath_ = keyPath;\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::reloadSSLFiles()\n{\n    listenerManagerPtr_->reloadSSLFiles();\n    return *this;\n}\n\nvoid HttpAppFrameworkImpl::run()\n{\n    if (!getLoop()->isInLoopThread())\n    {\n        getLoop()->moveToCurrentThread();\n    }\n    LOG_TRACE << \"Start to run...\";\n    // Create dirs for cache files\n    for (int i = 0; i < 256; ++i)\n    {\n        char dirName[4];\n        snprintf(dirName, sizeof(dirName), \"%02x\", i);\n        std::transform(dirName, dirName + 2, dirName, [](unsigned char c) {\n            return toupper(c);\n        });\n        utils::createPath(getUploadPath() + \"/tmp/\" + dirName);\n    }\n    if (runAsDaemon_)\n    {\n        // go daemon!\n        godaemon();\n#ifdef __linux__\n        getLoop()->resetTimerQueue();\n#endif\n        getLoop()->resetAfterFork();\n    }\n    // set relaunching\n    if (relaunchOnError_)\n    {\n#ifndef _WIN32\n        while (true)\n        {\n            int child_status = 0;\n            auto child_pid = fork();\n            if (child_pid < 0)\n            {\n                LOG_ERROR << \"fork error\";\n                abort();\n            }\n            else if (child_pid == 0)\n            {\n                // child\n                break;\n            }\n            waitpid(child_pid, &child_status, 0);\n            sleep(1);\n            LOG_INFO << \"start new process\";\n        }\n#ifdef __linux__\n        getLoop()->resetTimerQueue();\n#endif\n        getLoop()->resetAfterFork();\n#endif\n    }\n    if (handleSigterm_)\n    {\n#ifdef WIN32\n        signal(SIGTERM, TERMFunction);\n        signal(SIGINT, TERMFunction);\n#else\n        struct sigaction sa;\n        sa.sa_handler = TERMFunction;\n        sigemptyset(&sa.sa_mask);\n        sa.sa_flags = 0;\n        if (sigaction(SIGINT, &sa, NULL) == -1)\n        {\n            LOG_ERROR << \"sigaction() failed, can't set SIGINT handler\";\n            abort();\n        }\n        if (sigaction(SIGTERM, &sa, NULL) == -1)\n        {\n            LOG_ERROR << \"sigaction() failed, can't set SIGTERM handler\";\n            abort();\n        }\n#endif\n    }\n    setupFileLogger();\n    if (relaunchOnError_)\n    {\n        LOG_INFO << \"Start child process\";\n    }\n\n#if !defined(_WIN32) && !TARGET_OS_IOS\n    if (!libFilePaths_.empty())\n    {\n        sharedLibManagerPtr_ =\n            std::make_unique<SharedLibManager>(libFilePaths_,\n                                               libFileOutputPath_);\n    }\n#endif\n\n    // Create IO threads\n    ioLoopThreadPool_ =\n        std::make_unique<trantor::EventLoopThreadPool>(threadNum_,\n                                                       \"DrogonIoLoop\");\n    std::vector<trantor::EventLoop *> ioLoops = ioLoopThreadPool_->getLoops();\n    for (size_t i = 0; i < threadNum_; ++i)\n    {\n        ioLoops[i]->setIndex(i);\n    }\n    getLoop()->setIndex(threadNum_);\n\n    // Create all listeners.\n    listenerManagerPtr_->createListeners(sslCertPath_,\n                                         sslKeyPath_,\n                                         sslConfCmds_,\n                                         ioLoops);\n\n    // A fast database client instance should be created in the main event\n    // loop, so put the main loop into ioLoops.\n    ioLoops.push_back(getLoop());\n    dbClientManagerPtr_->createDbClients(ioLoops);\n    redisClientManagerPtr_->createRedisClients(ioLoops);\n    if (useSession_)\n    {\n        sessionManagerPtr_ =\n            std::make_unique<SessionManager>(getLoop(),\n                                             sessionTimeout_,\n                                             sessionStartAdvices_,\n                                             sessionDestroyAdvices_,\n                                             sessionIdGeneratorCallback_);\n    }\n    // now start running!!\n    running_ = true;\n    // Initialize plugins\n    auto &pluginConfig = jsonConfig_[\"plugins\"];\n    const auto &runtimePluginConfig = jsonRuntimeConfig_[\"plugins\"];\n    if (!pluginConfig.isNull())\n    {\n        if (!runtimePluginConfig.isNull() && runtimePluginConfig.isArray())\n        {\n            for (const auto &plugin : runtimePluginConfig)\n            {\n                pluginConfig.append(plugin);\n            }\n        }\n    }\n    else\n    {\n        jsonConfig_[\"plugins\"] = runtimePluginConfig;\n    }\n    if (!pluginConfig.isNull())\n    {\n        pluginsManagerPtr_->initializeAllPlugins(pluginConfig,\n                                                 [](PluginBase *plugin) {\n                                                     LOG_TRACE\n                                                         << \"new plugin:\"\n                                                         << plugin->className();\n                                                     // TODO: new plugin\n                                                 });\n    }\n    routersInit_ = true;\n    HttpControllersRouter::instance().init(ioLoops);\n    StaticFileRouter::instance().init(ioLoops);\n    getLoop()->queueInLoop([this]() {\n        for (auto &adv : beginningAdvices_)\n        {\n            adv();\n        }\n        beginningAdvices_.clear();\n        // Let listener event loops run when everything is ready.\n        listenerManagerPtr_->startListening();\n    });\n    // start all loops\n    // TODO: when should IOLoops start?\n    // In before, IOLoops are started in `listenerManagerPtr_->startListening()`\n    // It should be fine for them to start anywhere before `startListening()`.\n    // However, we should consider other components.\n    ioLoopThreadPool_->start();\n    getLoop()->loop();\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setUploadPath(\n    const std::string &uploadPath)\n{\n    assert(!uploadPath.empty());\n\n    std::filesystem::path fsUploadPath(utils::toNativePath(uploadPath));\n    if (!fsUploadPath.is_absolute())\n    {\n        std::filesystem::path fsRoot(utils::toNativePath(rootPath_));\n        fsUploadPath = fsRoot / fsUploadPath;\n    }\n    uploadPath_ = utils::fromNativePath(fsUploadPath.native());\n    return *this;\n}\n\nvoid HttpAppFrameworkImpl::findSessionForRequest(const HttpRequestImplPtr &req)\n{\n    if (useSession_)\n    {\n        std::string sessionId = req->getCookie(sessionCookieKey_);\n        bool needSetSessionid = false;\n        if (sessionId.empty())\n        {\n            sessionId = sessionIdGeneratorCallback_();\n            needSetSessionid = true;\n        }\n        req->setSession(\n            sessionManagerPtr_->getSession(sessionId, needSetSessionid));\n    }\n}\n\nstd::vector<HttpHandlerInfo> HttpAppFrameworkImpl::getHandlersInfo() const\n{\n    return HttpControllersRouter::instance().getHandlersInfo();\n}\n\nHttpResponsePtr HttpAppFrameworkImpl::handleSessionForResponse(\n    const HttpRequestImplPtr &req,\n    const HttpResponsePtr &resp)\n{\n    if (useSession_)\n    {\n        auto &sessionPtr = req->getSession();\n        if (!sessionPtr)\n        {\n            return resp;\n        }\n        if (sessionPtr->needToChangeSessionId())\n        {\n            sessionManagerPtr_->changeSessionId(sessionPtr);\n        }\n        if (sessionPtr->needSetToClient())\n        {\n            if (resp->expiredTime() >= 0)\n            {\n                auto newResp = std::make_shared<HttpResponseImpl>(\n                    *static_cast<HttpResponseImpl *>(resp.get()));\n                newResp->setExpiredTime(-1);  // make it temporary\n                auto sessionid =\n                    Cookie(sessionCookieKey_, sessionPtr->sessionId());\n                sessionid.setPath(\"/\");\n                sessionid.setSameSite(sessionSameSite_);\n                if (sessionMaxAge_ >= 0)\n                    sessionid.setMaxAge(sessionMaxAge_);\n                newResp->addCookie(std::move(sessionid));\n                sessionPtr->hasSet();\n\n                return newResp;\n            }\n            else\n            {\n                auto sessionid =\n                    Cookie(sessionCookieKey_, sessionPtr->sessionId());\n                sessionid.setPath(\"/\");\n                sessionid.setSameSite(sessionSameSite_);\n                if (sessionMaxAge_ >= 0)\n                    sessionid.setMaxAge(sessionMaxAge_);\n                resp->addCookie(std::move(sessionid));\n                sessionPtr->hasSet();\n\n                return resp;\n            }\n        }\n        else if (resp->version() != req->version())\n        {\n            auto newResp = std::make_shared<HttpResponseImpl>(\n                *static_cast<HttpResponseImpl *>(resp.get()));\n            newResp->setVersion(req->version());\n            newResp->setExpiredTime(-1);  // make it temporary\n\n            return newResp;\n        }\n        else\n        {\n            return resp;\n        }\n    }\n    else\n    {\n        if (resp->expiredTime() >= 0 && resp->version() != req->version())\n        {\n            auto newResp = std::make_shared<HttpResponseImpl>(\n                *static_cast<HttpResponseImpl *>(resp.get()));\n            newResp->setVersion(req->version());\n            newResp->setExpiredTime(-1);  // make it temporary\n\n            return newResp;\n        }\n        else\n        {\n            return resp;\n        }\n    }\n}\n\ntrantor::EventLoop *HttpAppFrameworkImpl::getLoop() const\n{\n    static trantor::EventLoop loop;\n    return &loop;\n}\n\ntrantor::EventLoop *HttpAppFrameworkImpl::getIOLoop(size_t id) const\n{\n    if (!ioLoopThreadPool_)\n    {\n        LOG_WARN << \"Please call getIOLoop() after drogon::app().run()\";\n        return nullptr;\n    }\n    auto n = ioLoopThreadPool_->size();\n    if (id >= n)\n    {\n        LOG_TRACE << \"Loop id (\" << id << \") out of range [0-\" << n << \").\";\n        id %= n;\n        LOG_TRACE << \"Rounded to : \" << id;\n    }\n    return ioLoopThreadPool_->getLoop(id);\n}\n\nHttpAppFramework &HttpAppFramework::instance()\n{\n    return HttpAppFrameworkImpl::instance();\n}\n\nvoid HttpAppFrameworkImpl::forward(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback,\n    const std::string &hostString,\n    double timeout)\n{\n    forward(std::dynamic_pointer_cast<HttpRequestImpl>(req),\n            std::move(callback),\n            hostString,\n            timeout);\n}\n\nvoid HttpAppFrameworkImpl::forward(\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback,\n    const std::string &hostString,\n    double timeout)\n{\n    if (hostString.empty())\n    {\n        HttpInternalForwardHelper::forward(req, std::move(callback));\n    }\n    else\n    {\n        /// A tiny implementation of a reverse proxy.\n        static std::unordered_map<std::string, HttpClientImplPtr> clientsMap;\n        HttpClientImplPtr clientPtr;\n        static std::mutex mtx;\n        {\n            std::lock_guard<std::mutex> lock(mtx);\n            auto iter = clientsMap.find(hostString);\n            if (iter != clientsMap.end())\n            {\n                clientPtr = iter->second;\n            }\n            else\n            {\n                clientPtr = std::make_shared<HttpClientImpl>(\n                    trantor::EventLoop::getEventLoopOfCurrentThread()\n                        ? trantor::EventLoop::getEventLoopOfCurrentThread()\n                        : getLoop(),\n                    hostString);\n                clientsMap[hostString] = clientPtr;\n            }\n        }\n        req->setPassThrough(true);\n        clientPtr->sendRequest(\n            req,\n            [callback = std::move(callback), req](ReqResult result,\n                                                  const HttpResponsePtr &resp) {\n                if (result == ReqResult::Ok)\n                {\n                    resp->setPassThrough(true);\n                    callback(resp);\n                }\n                else\n                {\n                    callback(HttpResponse::newNotFoundResponse(req));\n                }\n            },\n            timeout);\n    }\n}\n\norm::DbClientPtr HttpAppFrameworkImpl::getDbClient(const std::string &name)\n{\n    return dbClientManagerPtr_->getDbClient(name);\n}\n\norm::DbClientPtr HttpAppFrameworkImpl::getFastDbClient(const std::string &name)\n{\n    return dbClientManagerPtr_->getFastDbClient(name);\n}\n\nnosql::RedisClientPtr HttpAppFrameworkImpl::getRedisClient(\n    const std::string &name)\n{\n    return redisClientManagerPtr_->getRedisClient(name);\n}\n\nnosql::RedisClientPtr HttpAppFrameworkImpl::getFastRedisClient(\n    const std::string &name)\n{\n    return redisClientManagerPtr_->getFastRedisClient(name);\n}\n\n// deprecated\nHttpAppFramework &HttpAppFrameworkImpl::createDbClient(\n    const std::string &dbType,\n    const std::string &host,\n    unsigned short port,\n    const std::string &databaseName,\n    const std::string &userName,\n    const std::string &password,\n    size_t connectionNum,\n    const std::string &filename,\n    const std::string &name,\n    bool isFast,\n    const std::string &characterSet,\n    double timeout,\n    bool autoBatch)\n{\n    assert(!running_);\n    addDbClient(dbType,\n                host,\n                port,\n                databaseName,\n                userName,\n                password,\n                connectionNum,\n                filename,\n                name,\n                isFast,\n                characterSet,\n                timeout,\n                autoBatch,\n                {});\n    return *this;\n}\n\nvoid HttpAppFrameworkImpl::addDbClient(\n    const std::string &dbType,\n    const std::string &host,\n    unsigned short port,\n    const std::string &databaseName,\n    const std::string &userName,\n    const std::string &password,\n    size_t connectionNum,\n    const std::string &filename,\n    const std::string &name,\n    bool isFast,\n    const std::string &characterSet,\n    double timeout,\n    bool autoBatch,\n    std::unordered_map<std::string, std::string> options)\n{\n    if (dbType == \"postgresql\" || dbType == \"postgres\")\n    {\n        addDbClient(orm::PostgresConfig{host,\n                                        port,\n                                        databaseName,\n                                        userName,\n                                        password,\n                                        connectionNum,\n                                        name,\n                                        isFast,\n                                        characterSet,\n                                        timeout,\n                                        autoBatch,\n                                        std::move(options)});\n    }\n    else if (dbType == \"mysql\")\n    {\n        addDbClient(orm::MysqlConfig{host,\n                                     port,\n                                     databaseName,\n                                     userName,\n                                     password,\n                                     connectionNum,\n                                     name,\n                                     isFast,\n                                     characterSet,\n                                     timeout});\n    }\n    else if (dbType == \"sqlite3\")\n    {\n        addDbClient(orm::Sqlite3Config{connectionNum, filename, name, timeout});\n    }\n    else\n    {\n        LOG_ERROR << \"Unsupported database type: \" << dbType\n                  << \", should be one of (postgresql, mysql, sqlite3)\";\n    }\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::addDbClient(const orm::DbConfig &config)\n{\n    assert(!running_);\n    dbClientManagerPtr_->addDbClient(config);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::createRedisClient(\n    const std::string &ip,\n    unsigned short port,\n    const std::string &name,\n    const std::string &password,\n    size_t connectionNum,\n    bool isFast,\n    double timeout,\n    unsigned int db,\n    const std::string &username)\n{\n    assert(!running_);\n    redisClientManagerPtr_->createRedisClient(\n        name, ip, port, username, password, connectionNum, isFast, timeout, db);\n    return *this;\n}\n\nvoid HttpAppFrameworkImpl::quit()\n{\n    if (getLoop()->isRunning() && running_.exchange(false))\n    {\n        getLoop()->queueInLoop([this]() {\n            // Release members in the reverse order of initialization\n            listenerManagerPtr_->stopListening();\n            listenerManagerPtr_.reset();\n            StaticFileRouter::instance().reset();\n            HttpControllersRouter::instance().reset();\n            pluginsManagerPtr_.reset();\n            redisClientManagerPtr_.reset();\n            dbClientManagerPtr_.reset();\n            getLoop()->quit();\n            for (trantor::EventLoop *loop : ioLoopThreadPool_->getLoops())\n            {\n                loop->quit();\n            }\n            ioLoopThreadPool_->wait();\n        });\n    }\n}\n\nconst HttpResponsePtr &HttpAppFrameworkImpl::getCustom404Page()\n{\n    if (!custom404_)\n    {\n        return custom404_;\n    }\n    auto loop = trantor::EventLoop::getEventLoopOfCurrentThread();\n    if (loop && loop->index() < app().getThreadNum())\n    {\n        // If the current thread is an IO thread\n        static IOThreadStorage<HttpResponsePtr> thread404Pages;\n        static std::once_flag once;\n        std::call_once(once, [this] {\n            thread404Pages.init(\n                [this](HttpResponsePtr &resp, size_t /*index*/) {\n                    resp = std::make_shared<HttpResponseImpl>(\n                        *static_cast<HttpResponseImpl *>(custom404_.get()));\n                });\n        });\n        return thread404Pages.getThreadData();\n    }\n    else\n    {\n        return custom404_;\n    }\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setStaticFileHeaders(\n    const std::vector<std::pair<std::string, std::string>> &headers)\n{\n    StaticFileRouter::instance().setStaticFileHeaders(headers);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::addALocation(\n    const std::string &uriPrefix,\n    const std::string &defaultContentType,\n    const std::string &alias,\n    bool isCaseSensitive,\n    bool allowAll,\n    bool isRecursive,\n    const std::vector<std::string> &middlewareNames)\n{\n    StaticFileRouter::instance().addALocation(uriPrefix,\n                                              defaultContentType,\n                                              alias,\n                                              isCaseSensitive,\n                                              allowAll,\n                                              isRecursive,\n                                              middlewareNames);\n    return *this;\n}\n\nbool HttpAppFrameworkImpl::areAllDbClientsAvailable() const noexcept\n{\n    return dbClientManagerPtr_->areAllDbClientsAvailable();\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setCustomErrorHandler(\n    std::function<HttpResponsePtr(HttpStatusCode, const HttpRequestPtr &req)>\n        &&resp_generator)\n{\n    customErrorHandler_ = std::move(resp_generator);\n    usingCustomErrorHandler_ = true;\n    return *this;\n}\n\nconst std::function<HttpResponsePtr(HttpStatusCode,\n                                    const HttpRequestPtr &req)> &\nHttpAppFrameworkImpl::getCustomErrorHandler() const\n{\n    return customErrorHandler_;\n}\n\nstd::vector<trantor::InetAddress> HttpAppFrameworkImpl::getListeners() const\n{\n    return listenerManagerPtr_->getListeners();\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setDefaultHandler(\n    DefaultHandler handler)\n{\n    StaticFileRouter::instance().setDefaultHandler(std::move(handler));\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setupFileLogger()\n{\n#ifdef DROGON_SPDLOG_SUPPORT\n    if (logWithSpdlog_)\n    {\n        // Do nothing if already initialized...\n        if (!trantor::Logger::getSpdLogger())\n        {\n            trantor::Logger::enableSpdLog();\n            // Get the new logger & replace its sinks with the ones of the\n            // config\n            auto logger = trantor::Logger::getSpdLogger();\n            std::vector<spdlog::sink_ptr> sinks;\n            if (!logPath_.empty())\n            {\n                // 1. check existence of folder or try to create it\n                auto fsLogPath =\n                    std::filesystem::path(utils::toNativePath(logPath_));\n                std::error_code fsErr;\n                if (!std::filesystem::create_directories(fsLogPath, fsErr) &&\n                    fsErr)\n                {\n                    LOG_ERROR << \"could not create log file path\";\n                    abort();\n                }\n                // 2. check if we have rights to create files in the folder\n                if (os_access(fsLogPath.native().c_str(), W_OK) != 0)\n                {\n                    LOG_ERROR << \"cannot create files in log folder\";\n                    abort();\n                }\n                std::filesystem::path baseName(logfileBaseName_);\n                if (baseName.empty())\n                    baseName = \"drogon.log\";\n                else\n                    baseName.replace_extension(\".log\");\n                auto sizeLimit = logfileSize_;\n                if (sizeLimit == 0)  // 0 is not allowed by this sink\n                    sizeLimit = std::numeric_limits<std::size_t>::max();\n                sinks.push_back(\n                    std::make_shared<spdlog::sinks::rotating_file_sink_mt>(\n                        (fsLogPath / baseName).string(),\n                        sizeLimit,\n                        // spdlog limitation\n                        std::min(logfileMaxNum_, std::size_t(20000)),\n                        false));\n            }\n            else\n                sinks.push_back(\n                    std::make_shared<spdlog::sinks::stderr_color_sink_mt>());\n#if defined(_WIN32) && defined(_DEBUG)\n            // On Windows with debug, it may be interesting to have the logs\n            // directly in the Visual Studio / WinDbg console\n            sinks.push_back(std::make_shared<spdlog::sinks::msvc_sink_mt>());\n#endif\n            // Note: the new sinks won't use the format pattern set on the\n            // logger, and there is currently not way to retrieve it.\n            // So, set the same pattern as the one set on the logger in\n            // trantor::Logger::getDefaultSpdLogger()\n            for (auto &sink : sinks)\n                sink->set_pattern(\"%Y%m%d %T.%f %6t %^%=8l%$ [%!] %v - %s:%#\");\n            logger->sinks() = sinks;\n        }\n        return *this;\n    }\n#endif  // DROGON_SPDLOG_SUPPORT\n    if (!logPath_.empty() && !asyncFileLoggerPtr_)\n    {\n        // std::filesystem does not provide a method to check access\n        // permissions, so keep existing code\n        if (os_access(utils::toNativePath(logPath_).c_str(), R_OK | W_OK) != 0)\n        {\n            LOG_ERROR << \"log file path not exist\";\n            abort();\n        }\n        else\n        {\n            std::string baseName = logfileBaseName_;\n            if (baseName.empty())\n            {\n                baseName = \"drogon\";\n            }\n            asyncFileLoggerPtr_ = std::make_shared<trantor::AsyncFileLogger>();\n            asyncFileLoggerPtr_->setFileName(baseName, \".log\", logPath_);\n            asyncFileLoggerPtr_->startLogging();\n            asyncFileLoggerPtr_->setFileSizeLimit(logfileSize_);\n            asyncFileLoggerPtr_->setMaxFiles(logfileMaxNum_);\n            trantor::Logger::setOutputFunction(\n                [loggerPtr = asyncFileLoggerPtr_](const char *msg,\n                                                  const uint64_t len) {\n                    loggerPtr->output(msg, len);\n                },\n                [loggerPtr = asyncFileLoggerPtr_]() { loggerPtr->flush(); });\n        }\n    }\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::registerCustomExtensionMime(\n    const std::string &ext,\n    const std::string &mime)\n{\n    drogon::registerCustomExtensionMime(ext, mime);\n    return *this;\n}\n\nint64_t HttpAppFrameworkImpl::getConnectionCount() const\n{\n    return HttpConnectionLimit::instance().getConnectionNum();\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::enableRequestStream(bool enable)\n{\n    enableRequestStream_ = enable;\n    return *this;\n}\n\nbool HttpAppFrameworkImpl::isRequestStreamEnabled() const\n{\n    return enableRequestStream_;\n}\n\n// AOP registration methods\n\nHttpAppFramework &HttpAppFrameworkImpl::registerNewConnectionAdvice(\n    const std::function<bool(const trantor::InetAddress &,\n                             const trantor::InetAddress &)> &advice)\n{\n    AopAdvice::instance().registerNewConnectionAdvice(advice);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::registerHttpResponseCreationAdvice(\n    const std::function<void(const HttpResponsePtr &)> &advice)\n{\n    // Is this callback really an AOP?\n    // Maybe we should store them in HttpResponseImpl class as static member\n    AopAdvice::instance().registerHttpResponseCreationAdvice(advice);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::registerSyncAdvice(\n    const std::function<HttpResponsePtr(const HttpRequestPtr &)> &advice)\n\n{\n    AopAdvice::instance().registerSyncAdvice(advice);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::registerPreRoutingAdvice(\n    const std::function<void(const HttpRequestPtr &)> &advice)\n{\n    AopAdvice::instance().registerPreRoutingObserver(advice);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::registerPreRoutingAdvice(\n    const std::function<void(const HttpRequestPtr &,\n                             AdviceCallback &&,\n                             AdviceChainCallback &&)> &advice)\n{\n    AopAdvice::instance().registerPreRoutingAdvice(advice);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::registerPostRoutingAdvice(\n    const std::function<void(const HttpRequestPtr &)> &advice)\n{\n    AopAdvice::instance().registerPostRoutingObserver(advice);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::registerPostRoutingAdvice(\n    const std::function<void(const HttpRequestPtr &,\n                             AdviceCallback &&,\n                             AdviceChainCallback &&)> &advice)\n{\n    AopAdvice::instance().registerPostRoutingAdvice(advice);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::registerPreHandlingAdvice(\n    const std::function<void(const HttpRequestPtr &)> &advice)\n{\n    AopAdvice::instance().registerPreHandlingObserver(advice);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::registerPreHandlingAdvice(\n    const std::function<void(const HttpRequestPtr &,\n                             AdviceCallback &&,\n                             AdviceChainCallback &&)> &advice)\n{\n    AopAdvice::instance().registerPreHandlingAdvice(advice);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::registerPostHandlingAdvice(\n    const std::function<void(const HttpRequestPtr &, const HttpResponsePtr &)>\n        &advice)\n{\n    AopAdvice::instance().registerPostHandlingAdvice(advice);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::registerPreSendingAdvice(\n    const std::function<void(const HttpRequestPtr &, const HttpResponsePtr &)>\n        &advice)\n{\n    AopAdvice::instance().registerPreSendingAdvice(advice);\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setBeforeListenSockOptCallback(\n    std::function<void(int)> cb)\n{\n    listenerManagerPtr_->setBeforeListenSockOptCallback(std::move(cb));\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setAfterAcceptSockOptCallback(\n    std::function<void(int)> cb)\n{\n    listenerManagerPtr_->setAfterAcceptSockOptCallback(std::move(cb));\n    return *this;\n}\n\nHttpAppFramework &HttpAppFrameworkImpl::setConnectionCallback(\n    std::function<void(const trantor::TcpConnectionPtr &)> cb)\n{\n    listenerManagerPtr_->setConnectionCallback(std::move(cb));\n    return *this;\n}\n"
  },
  {
    "path": "lib/src/HttpAppFrameworkImpl.h",
    "content": "/**\n *\n *  @file HttpAppFrameworkImpl.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/HttpAppFramework.h>\n#include <drogon/config.h>\n#include <json/json.h>\n#include <functional>\n#include <memory>\n#include <string>\n#include <vector>\n#include \"SessionManager.h\"\n#include \"drogon/utils/Utilities.h\"\n#include \"impl_forwards.h\"\n\nnamespace trantor\n{\nclass EventLoopThreadPool;\n}\n\nnamespace drogon\n{\nHttpResponsePtr defaultErrorHandler(HttpStatusCode code,\n                                    const HttpRequestPtr &req);\nvoid defaultExceptionHandler(const std::exception &,\n                             const HttpRequestPtr &,\n                             std::function<void(const HttpResponsePtr &)> &&);\n\nstruct InitBeforeMainFunction\n{\n    explicit InitBeforeMainFunction(const std::function<void()> &func)\n    {\n        func();\n    }\n};\n\nclass HttpAppFrameworkImpl final : public HttpAppFramework\n{\n  public:\n    HttpAppFrameworkImpl();\n\n    const Json::Value &getCustomConfig() const override\n    {\n        return jsonConfig_[\"custom_config\"];\n    }\n\n    PluginBase *getPlugin(const std::string &name) override;\n    std::shared_ptr<PluginBase> getSharedPlugin(\n        const std::string &name) override;\n    void addPlugins(const Json::Value &configs) override;\n    void addPlugin(const std::string &name,\n                   const std::vector<std::string> &dependencies,\n                   const Json::Value &config) override;\n    HttpAppFramework &addListener(\n        const std::string &ip,\n        uint16_t port,\n        bool useSSL,\n        const std::string &certFile,\n        const std::string &keyFile,\n        bool useOldTLS,\n        const std::vector<std::pair<std::string, std::string>> &sslConfCmds)\n        override;\n    HttpAppFramework &setThreadNum(size_t threadNum) override;\n\n    size_t getThreadNum() const override\n    {\n        return threadNum_;\n    }\n\n    HttpAppFramework &setSSLConfigCommands(\n        const std::vector<std::pair<std::string, std::string>> &sslConfCmds)\n        override;\n    HttpAppFramework &setSSLFiles(const std::string &certPath,\n                                  const std::string &keyPath) override;\n\n    HttpAppFramework &reloadSSLFiles() override;\n\n    void run() override;\n    HttpAppFramework &registerWebSocketController(\n        const std::string &pathName,\n        const std::string &ctrlName,\n        const std::vector<internal::HttpConstraint> &constraints) override;\n    HttpAppFramework &registerWebSocketControllerRegex(\n        const std::string &regExp,\n        const std::string &ctrlName,\n        const std::vector<internal::HttpConstraint> &constraints) override;\n    HttpAppFramework &registerHttpSimpleController(\n        const std::string &pathName,\n        const std::string &ctrlName,\n        const std::vector<internal::HttpConstraint> &constraints) override;\n\n    HttpAppFramework &setCustom404Page(const HttpResponsePtr &resp,\n                                       bool set404) override\n    {\n        if (set404)\n        {\n            resp->setStatusCode(k404NotFound);\n        }\n        custom404_ = resp;\n        return *this;\n    }\n\n    HttpAppFramework &setCustomErrorHandler(\n        std::function<HttpResponsePtr(HttpStatusCode,\n                                      const HttpRequestPtr &req)>\n            &&resp_generator) override;\n\n    const HttpResponsePtr &getCustom404Page();\n\n    void forward(const HttpRequestPtr &req,\n                 std::function<void(const HttpResponsePtr &)> &&callback,\n                 const std::string &hostString,\n                 double timeout) override;\n\n    void forward(const HttpRequestImplPtr &req,\n                 std::function<void(const HttpResponsePtr &)> &&callback,\n                 const std::string &hostString,\n                 double timeout = 0);\n\n    HttpAppFramework &registerBeginningAdvice(\n        const std::function<void()> &advice) override\n    {\n        beginningAdvices_.emplace_back(advice);\n        return *this;\n    }\n\n    HttpAppFramework &registerNewConnectionAdvice(\n        const std::function<bool(const trantor::InetAddress &,\n                                 const trantor::InetAddress &)> &advice)\n        override;\n\n    HttpAppFramework &registerHttpResponseCreationAdvice(\n        const std::function<void(const HttpResponsePtr &)> &advice) override;\n\n    HttpAppFramework &registerSyncAdvice(\n        const std::function<HttpResponsePtr(const HttpRequestPtr &)> &advice)\n        override;\n\n    HttpAppFramework &registerPreRoutingAdvice(\n        const std::function<void(const HttpRequestPtr &,\n                                 AdviceCallback &&,\n                                 AdviceChainCallback &&)> &advice) override;\n\n    HttpAppFramework &registerPostRoutingAdvice(\n        const std::function<void(const HttpRequestPtr &,\n                                 AdviceCallback &&,\n                                 AdviceChainCallback &&)> &advice) override;\n\n    HttpAppFramework &registerPreHandlingAdvice(\n        const std::function<void(const HttpRequestPtr &,\n                                 AdviceCallback &&,\n                                 AdviceChainCallback &&)> &advice) override;\n\n    HttpAppFramework &registerPreRoutingAdvice(\n        const std::function<void(const HttpRequestPtr &)> &advice) override;\n\n    HttpAppFramework &registerPostRoutingAdvice(\n        const std::function<void(const HttpRequestPtr &)> &advice) override;\n\n    HttpAppFramework &registerPreHandlingAdvice(\n        const std::function<void(const HttpRequestPtr &)> &advice) override;\n\n    HttpAppFramework &registerPostHandlingAdvice(\n        const std::function<void(const HttpRequestPtr &,\n                                 const HttpResponsePtr &)> &advice) override;\n\n    HttpAppFramework &registerPreSendingAdvice(\n        const std::function<void(const HttpRequestPtr &,\n                                 const HttpResponsePtr &)> &advice) override;\n\n    HttpAppFramework &setDefaultHandler(DefaultHandler handler) override;\n\n    HttpAppFramework &setupFileLogger() override;\n\n    HttpAppFramework &enableSession(\n        const size_t timeout,\n        Cookie::SameSite sameSite = Cookie::SameSite::kNull,\n        const std::string &cookieKey = \"JSESSIONID\",\n        int maxAge = -1,\n        SessionManager::IdGeneratorCallback idGeneratorCallback =\n            nullptr) override\n    {\n        useSession_ = true;\n        sessionTimeout_ = timeout;\n        sessionSameSite_ = sameSite;\n        sessionCookieKey_ = cookieKey;\n        sessionMaxAge_ = maxAge;\n        return setSessionIdGenerator(idGeneratorCallback);\n    }\n\n    HttpAppFramework &setSessionIdGenerator(\n        SessionManager::IdGeneratorCallback idGeneratorCallback = nullptr)\n    {\n        sessionIdGeneratorCallback_ =\n            idGeneratorCallback ? idGeneratorCallback\n                                : []() { return utils::getUuid(true); };\n        return *this;\n    }\n\n    HttpAppFramework &disableSession() override\n    {\n        useSession_ = false;\n        return *this;\n    }\n\n    HttpAppFramework &registerSessionStartAdvice(\n        const AdviceStartSessionCallback &advice) override\n    {\n        sessionStartAdvices_.emplace_back(advice);\n        return *this;\n    }\n\n    HttpAppFramework &registerSessionDestroyAdvice(\n        const AdviceDestroySessionCallback &advice) override\n    {\n        sessionDestroyAdvices_.emplace_back(advice);\n        return *this;\n    }\n\n    const std::string &getDocumentRoot() const override\n    {\n        return rootPath_;\n    }\n\n    HttpAppFramework &setDocumentRoot(const std::string &rootPath) override\n    {\n        rootPath_ = rootPath;\n        return *this;\n    }\n\n    HttpAppFramework &setStaticFileHeaders(\n        const std::vector<std::pair<std::string, std::string>> &headers)\n        override;\n\n    HttpAppFramework &addALocation(\n        const std::string &uriPrefix,\n        const std::string &defaultContentType,\n        const std::string &alias,\n        bool isCaseSensitive,\n        bool allowAll,\n        bool isRecursive,\n        const std::vector<std::string> &middlewareNames) override;\n\n    const std::string &getUploadPath() const override\n    {\n        return uploadPath_;\n    }\n\n    const std::shared_ptr<trantor::Resolver> &getResolver() const override\n    {\n        static auto resolver = trantor::Resolver::newResolver(getLoop());\n        return resolver;\n    }\n\n    HttpAppFramework &setUploadPath(const std::string &uploadPath) override;\n    HttpAppFramework &setFileTypes(\n        const std::vector<std::string> &types) override;\n#if !defined(_WIN32) && !TARGET_OS_IOS\n    HttpAppFramework &enableDynamicViewsLoading(\n        const std::vector<std::string> &libPaths,\n        const std::string &outputPath) override;\n#endif\n    HttpAppFramework &setMaxConnectionNum(size_t maxConnections) override;\n    HttpAppFramework &setMaxConnectionNumPerIP(\n        size_t maxConnectionsPerIP) override;\n    HttpAppFramework &loadConfigFile(const std::string &fileName) noexcept(\n        false) override;\n    HttpAppFramework &loadConfigJson(const Json::Value &data) noexcept(\n        false) override;\n    HttpAppFramework &loadConfigJson(Json::Value &&data) noexcept(\n        false) override;\n\n    HttpAppFramework &enableRunAsDaemon() override\n    {\n        runAsDaemon_ = true;\n        return *this;\n    }\n\n    HttpAppFramework &disableSigtermHandling() override\n    {\n        handleSigterm_ = false;\n        return *this;\n    }\n\n    HttpAppFramework &enableRelaunchOnError() override\n    {\n        relaunchOnError_ = true;\n        return *this;\n    }\n\n    HttpAppFramework &setLogPath(const std::string &logPath,\n                                 const std::string &logfileBaseName,\n                                 size_t logfileSize,\n                                 size_t maxFiles,\n                                 bool useSpdlog) override;\n    HttpAppFramework &setLogLevel(trantor::Logger::LogLevel level) override;\n    HttpAppFramework &setLogLocalTime(bool on) override;\n\n    HttpAppFramework &enableSendfile(bool sendFile) override\n    {\n        useSendfile_ = sendFile;\n        return *this;\n    }\n\n    HttpAppFramework &enableGzip(bool useGzip) override\n    {\n        useGzip_ = useGzip;\n        return *this;\n    }\n\n    bool isGzipEnabled() const override\n    {\n        return useGzip_;\n    }\n\n    HttpAppFramework &enableBrotli(bool useBrotli) override\n    {\n        useBrotli_ = useBrotli;\n        return *this;\n    }\n\n    bool isBrotliEnabled() const override\n    {\n        return useBrotli_;\n    }\n\n    HttpAppFramework &setStaticFilesCacheTime(int cacheTime) override;\n    int staticFilesCacheTime() const override;\n\n    HttpAppFramework &setIdleConnectionTimeout(size_t timeout) override\n    {\n        idleConnectionTimeout_ = timeout;\n        return *this;\n    }\n\n    size_t getIdleConnectionTimeout() const  // could expose in base class\n    {\n        return idleConnectionTimeout_;\n    }\n\n    HttpAppFramework &setKeepaliveRequestsNumber(const size_t number) override\n    {\n        keepaliveRequestsNumber_ = number;\n        return *this;\n    }\n\n    HttpAppFramework &setPipeliningRequestsNumber(const size_t number) override\n    {\n        pipeliningRequestsNumber_ = number;\n        return *this;\n    }\n\n    HttpAppFramework &setGzipStatic(bool useGzipStatic) override;\n    HttpAppFramework &setBrStatic(bool useGzipStatic) override;\n\n    HttpAppFramework &setClientMaxBodySize(size_t maxSize) override\n    {\n        clientMaxBodySize_ = maxSize;\n        return *this;\n    }\n\n    HttpAppFramework &setClientMaxMemoryBodySize(size_t maxSize) override\n    {\n        clientMaxMemoryBodySize_ = maxSize;\n        return *this;\n    }\n\n    HttpAppFramework &setClientMaxWebSocketMessageSize(size_t maxSize) override\n    {\n        clientMaxWebSocketMessageSize_ = maxSize;\n        return *this;\n    }\n\n    HttpAppFramework &setHomePage(const std::string &homePageFile) override\n    {\n        homePageFile_ = homePageFile;\n        return *this;\n    }\n\n    const std::string &getHomePage() const override\n    {\n        return homePageFile_;\n    }\n\n    HttpAppFramework &setTermSignalHandler(\n        const std::function<void()> &handler) override\n    {\n        termSignalHandler_ = handler;\n        return *this;\n    }\n\n    const std::function<void()> &getTermSignalHandler() const\n    {\n        return termSignalHandler_;\n    }\n\n    HttpAppFramework &setIntSignalHandler(\n        const std::function<void()> &handler) override\n    {\n        intSignalHandler_ = handler;\n        return *this;\n    }\n\n    const std::function<void()> &getIntSignalHandler() const\n    {\n        return intSignalHandler_;\n    }\n\n    HttpAppFramework &setImplicitPageEnable(bool useImplicitPage) override;\n    bool isImplicitPageEnabled() const override;\n    HttpAppFramework &setImplicitPage(\n        const std::string &implicitPageFile) override;\n    const std::string &getImplicitPage() const override;\n\n    size_t getClientMaxBodySize() const\n    {\n        return clientMaxBodySize_;\n    }\n\n    size_t getClientMaxMemoryBodySize() const\n    {\n        return clientMaxMemoryBodySize_;\n    }\n\n    size_t getClientMaxWebSocketMessageSize() const\n    {\n        return clientMaxWebSocketMessageSize_;\n    }\n\n    std::vector<HttpHandlerInfo> getHandlersInfo() const override;\n\n    size_t keepaliveRequestsNumber() const\n    {\n        return keepaliveRequestsNumber_;\n    }\n\n    size_t pipeliningRequestsNumber() const\n    {\n        return pipeliningRequestsNumber_;\n    }\n\n    ~HttpAppFrameworkImpl() noexcept override;\n\n    bool isRunning() override\n    {\n        return running_;\n    }\n\n    HttpAppFramework &setJsonParserStackLimit(size_t limit) noexcept override\n    {\n        jsonStackLimit_ = limit;\n        return *this;\n    }\n\n    size_t getJsonParserStackLimit() const noexcept override\n    {\n        return jsonStackLimit_;\n    }\n\n    HttpAppFramework &setUnicodeEscapingInJson(bool enable) noexcept override\n    {\n        usingUnicodeEscaping_ = enable;\n        return *this;\n    }\n\n    bool isUnicodeEscapingUsedInJson() const noexcept override\n    {\n        return usingUnicodeEscaping_;\n    }\n\n    HttpAppFramework &setFloatPrecisionInJson(\n        unsigned int precision,\n        const std::string &precisionType) noexcept override\n    {\n        floatPrecisionInJson_ = std::make_pair(precision, precisionType);\n        return *this;\n    }\n\n    const std::pair<unsigned int, std::string> &getFloatPrecisionInJson()\n        const noexcept override\n    {\n        return floatPrecisionInJson_;\n    }\n\n    trantor::EventLoop *getLoop() const override;\n\n    trantor::EventLoop *getIOLoop(size_t id) const override;\n\n    void quit() override;\n\n    HttpAppFramework &setServerHeaderField(const std::string &server) override\n    {\n        assert(!running_);\n        assert(server.find(\"\\r\\n\") == std::string::npos);\n        serverHeader_ = \"server: \" + server + \"\\r\\n\";\n        return *this;\n    }\n\n    HttpAppFramework &enableServerHeader(bool flag) override\n    {\n        enableServerHeader_ = flag;\n        return *this;\n    }\n\n    HttpAppFramework &enableDateHeader(bool flag) override\n    {\n        enableDateHeader_ = flag;\n        return *this;\n    }\n\n    bool sendServerHeader() const\n    {\n        return enableServerHeader_;\n    }\n\n    bool sendDateHeader() const\n    {\n        return enableDateHeader_;\n    }\n\n    const std::string &getServerHeaderString() const\n    {\n        return serverHeader_;\n    }\n\n    orm::DbClientPtr getDbClient(const std::string &name) override;\n    orm::DbClientPtr getFastDbClient(const std::string &name) override;\n\n    HttpAppFramework &createDbClient(const std::string &dbType,\n                                     const std::string &host,\n                                     unsigned short port,\n                                     const std::string &databaseName,\n                                     const std::string &userName,\n                                     const std::string &password,\n                                     size_t connectionNum,\n                                     const std::string &filename,\n                                     const std::string &name,\n                                     bool isFast,\n                                     const std::string &characterSet,\n                                     double timeout,\n                                     bool autoBatch) override;\n    // a helper method\n    void addDbClient(const std::string &dbType,\n                     const std::string &host,\n                     unsigned short port,\n                     const std::string &databaseName,\n                     const std::string &userName,\n                     const std::string &password,\n                     size_t connectionNum,\n                     const std::string &filename,\n                     const std::string &name,\n                     bool isFast,\n                     const std::string &characterSet,\n                     double timeout,\n                     bool autoBatch,\n                     std::unordered_map<std::string, std::string> options);\n    HttpAppFramework &addDbClient(const orm::DbConfig &config) override;\n\n    HttpAppFramework &createRedisClient(const std::string &ip,\n                                        unsigned short port,\n                                        const std::string &name,\n                                        const std::string &password,\n                                        size_t connectionNum,\n                                        bool isFast,\n                                        double timeout,\n                                        unsigned int db,\n                                        const std::string &username) override;\n    nosql::RedisClientPtr getRedisClient(const std::string &name) override;\n    nosql::RedisClientPtr getFastRedisClient(const std::string &name) override;\n    std::vector<trantor::InetAddress> getListeners() const override;\n\n    inline static HttpAppFrameworkImpl &instance()\n    {\n        static HttpAppFrameworkImpl instance;\n        return instance;\n    }\n\n    bool useSendfile() const\n    {\n        return useSendfile_;\n    }\n\n    bool supportSSL() const override\n    {\n        return trantor::utils::tlsBackend() != \"None\";\n    }\n\n    size_t getCurrentThreadIndex() const override\n    {\n        auto *loop = trantor::EventLoop::getEventLoopOfCurrentThread();\n        if (loop)\n        {\n            return loop->index();\n        }\n        return (std::numeric_limits<size_t>::max)();\n    }\n\n    bool areAllDbClientsAvailable() const noexcept override;\n    const std::function<HttpResponsePtr(HttpStatusCode,\n                                        const HttpRequestPtr &req)> &\n    getCustomErrorHandler() const override;\n\n    bool isUsingCustomErrorHandler() const\n    {\n        return usingCustomErrorHandler_;\n    }\n\n    void enableReusePort(bool enable) override\n    {\n        reusePort_ = enable;\n    }\n\n    bool reusePort() const override\n    {\n        return reusePort_;\n    }\n\n    HttpAppFramework &setExceptionHandler(ExceptionHandler handler) override\n    {\n        exceptionHandler_ = std::move(handler);\n        return *this;\n    }\n\n    const ExceptionHandler &getExceptionHandler() const override\n    {\n        return exceptionHandler_;\n    }\n\n    HttpAppFramework &enableCompressedRequest(bool enable) override\n    {\n        enableCompressedRequest_ = enable;\n        return *this;\n    }\n\n    bool isCompressedRequestEnabled() const override\n    {\n        return enableCompressedRequest_;\n    }\n\n    HttpAppFramework &registerCustomExtensionMime(\n        const std::string &ext,\n        const std::string &mime) override;\n\n    // should return unsigned type!\n    int64_t getConnectionCount() const override;\n\n    // TODO: move session related codes to its own singleton class\n    void findSessionForRequest(const HttpRequestImplPtr &req);\n    HttpResponsePtr handleSessionForResponse(const HttpRequestImplPtr &req,\n                                             const HttpResponsePtr &resp);\n\n    HttpAppFramework &setBeforeListenSockOptCallback(\n        std::function<void(int)> cb) override;\n    HttpAppFramework &setAfterAcceptSockOptCallback(\n        std::function<void(int)> cb) override;\n    HttpAppFramework &setConnectionCallback(\n        std::function<void(const trantor::TcpConnectionPtr &)> cb) override;\n\n    HttpAppFramework &enableRequestStream(bool enable) override;\n    bool isRequestStreamEnabled() const override;\n\n  private:\n    void registerHttpController(const std::string &pathPattern,\n                                const internal::HttpBinderBasePtr &binder,\n                                const std::vector<HttpMethod> &validMethods,\n                                const std::vector<std::string> &middlewareNames,\n                                const std::string &handlerName) override;\n    void registerHttpControllerViaRegex(\n        const std::string &regExp,\n        const internal::HttpBinderBasePtr &binder,\n        const std::vector<HttpMethod> &validMethods,\n        const std::vector<std::string> &middlewareNames,\n        const std::string &handlerName) override;\n\n    // We use an uuid string as session id;\n    // set sessionTimeout_=0 to make location session valid forever based on\n    // cookies;\n    size_t sessionTimeout_{0};\n    Cookie::SameSite sessionSameSite_{Cookie::SameSite::kNull};\n    std::string sessionCookieKey_{\"JSESSIONID\"};\n    int sessionMaxAge_{-1};\n    size_t idleConnectionTimeout_{60};\n    bool useSession_{false};\n    std::string serverHeader_{\"server: drogon/\" + drogon::getVersion() +\n                              \"\\r\\n\"};\n\n    std::unique_ptr<ListenerManager> listenerManagerPtr_;\n    std::unique_ptr<PluginsManager> pluginsManagerPtr_;\n    std::unique_ptr<orm::DbClientManager> dbClientManagerPtr_;\n    std::unique_ptr<nosql::RedisClientManager> redisClientManagerPtr_;\n\n    std::string rootPath_{\"./\"};\n    std::string uploadPath_;\n    std::atomic_bool running_{false};\n    std::atomic_bool routersInit_{false};\n\n    size_t threadNum_{1};\n    std::unique_ptr<trantor::EventLoopThreadPool> ioLoopThreadPool_;\n\n#if !defined(_WIN32) && !TARGET_OS_IOS\n    std::vector<std::string> libFilePaths_;\n    std::string libFileOutputPath_;\n    std::unique_ptr<SharedLibManager> sharedLibManagerPtr_;\n#endif\n\n    std::vector<std::pair<std::string, std::string>> sslConfCmds_;\n    std::string sslCertPath_;\n    std::string sslKeyPath_;\n\n    bool runAsDaemon_{false};\n    bool handleSigterm_{true};\n    bool relaunchOnError_{false};\n    bool logWithSpdlog_{false};\n    std::string logPath_;\n    std::string logfileBaseName_;\n    size_t logfileSize_{100000000};\n    size_t logfileMaxNum_{0};\n    size_t keepaliveRequestsNumber_{0};\n    size_t pipeliningRequestsNumber_{0};\n    size_t jsonStackLimit_{1000};\n    bool useSendfile_{true};\n    bool useGzip_{true};\n    bool useBrotli_{false};\n    bool usingUnicodeEscaping_{true};\n    std::pair<unsigned int, std::string> floatPrecisionInJson_{0,\n                                                               \"significant\"};\n    bool usingCustomErrorHandler_{false};\n    size_t clientMaxBodySize_{1024 * 1024};\n    size_t clientMaxMemoryBodySize_{64 * 1024};\n    size_t clientMaxWebSocketMessageSize_{128 * 1024};\n    std::string homePageFile_{\"index.html\"};\n    std::function<void()> termSignalHandler_{[]() { app().quit(); }};\n    std::function<void()> intSignalHandler_{[]() { app().quit(); }};\n    std::unique_ptr<SessionManager> sessionManagerPtr_;\n    std::vector<AdviceStartSessionCallback> sessionStartAdvices_;\n    std::vector<AdviceDestroySessionCallback> sessionDestroyAdvices_;\n    SessionManager::IdGeneratorCallback sessionIdGeneratorCallback_;\n    std::shared_ptr<trantor::AsyncFileLogger> asyncFileLoggerPtr_;\n    Json::Value jsonConfig_;\n    Json::Value jsonRuntimeConfig_;\n    HttpResponsePtr custom404_;\n    std::function<HttpResponsePtr(HttpStatusCode, const HttpRequestPtr &req)>\n        customErrorHandler_ = &defaultErrorHandler;\n    static InitBeforeMainFunction initFirst_;\n    bool enableServerHeader_{true};\n    bool enableDateHeader_{true};\n    bool reusePort_{false};\n    std::vector<std::function<void()>> beginningAdvices_;\n\n    ExceptionHandler exceptionHandler_{defaultExceptionHandler};\n    bool enableCompressedRequest_{false};\n\n    bool enableRequestStream_{false};\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpBinder.cc",
    "content": "/**\n *\n *  HttpBinder.h\n *  Martin Chang\n *\n *  Copyright 2021, Martin Chang.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/HttpBinder.h>\n#include <drogon/HttpAppFramework.h>\n\nnamespace drogon\n{\nnamespace internal\n{\nvoid handleException(const std::exception &e,\n                     const HttpRequestPtr &req,\n                     std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    app().getExceptionHandler()(e, req, std::move(callback));\n}\n}  // namespace internal\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpClientImpl.cc",
    "content": "/**\n *\n *  @file HttpClientImpl.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpClientImpl.h\"\n#include \"HttpAppFrameworkImpl.h\"\n#include \"HttpRequestImpl.h\"\n#include \"HttpResponseImpl.h\"\n#include \"HttpResponseParser.h\"\n\n#include <drogon/config.h>\n#include <stdlib.h>\n#include <algorithm>\n\nusing namespace trantor;\nusing namespace drogon;\nusing namespace std::placeholders;\n\nnamespace trantor\n{\nstatic const size_t kDefaultDNSTimeout{600};\n}\n\nvoid HttpClientImpl::createTcpClient()\n{\n    LOG_TRACE << \"New TcpClient,\" << serverAddr_.toIpPort();\n    tcpClientPtr_ =\n        std::make_shared<trantor::TcpClient>(loop_, serverAddr_, \"httpClient\");\n\n    if (useSSL_ && utils::supportsTls())\n    {\n        LOG_TRACE << \"useOldTLS=\" << useOldTLS_;\n        LOG_TRACE << \"domain=\" << domain_;\n        auto policy = trantor::TLSPolicy::defaultClientPolicy();\n        policy->setUseOldTLS(useOldTLS_)\n            .setValidate(validateCert_)\n            .setHostname(domain_)\n            .setConfCmds(sslConfCmds_)\n            .setCertPath(clientCertPath_)\n            .setKeyPath(clientKeyPath_);\n        tcpClientPtr_->enableSSL(std::move(policy));\n    }\n\n    auto thisPtr = shared_from_this();\n    std::weak_ptr<HttpClientImpl> weakPtr = thisPtr;\n    tcpClientPtr_->setSockOptCallback([weakPtr](int fd) {\n        auto thisPtr = weakPtr.lock();\n        if (!thisPtr)\n            return;\n        if (thisPtr->sockOptCallback_)\n            thisPtr->sockOptCallback_(fd);\n    });\n    tcpClientPtr_->setConnectionCallback(\n        [weakPtr](const trantor::TcpConnectionPtr &connPtr) {\n            auto thisPtr = weakPtr.lock();\n            if (!thisPtr)\n                return;\n            if (connPtr->connected())\n            {\n                connPtr->setContext(\n                    std::make_shared<HttpResponseParser>(connPtr));\n                // send request;\n                LOG_TRACE << \"Connection established!\";\n                while (thisPtr->pipeliningCallbacks_.size() <=\n                           thisPtr->pipeliningDepth_ &&\n                       !thisPtr->requestsBuffer_.empty())\n                {\n                    thisPtr->sendReq(connPtr,\n                                     thisPtr->requestsBuffer_.front().first);\n                    thisPtr->pipeliningCallbacks_.push(\n                        std::move(thisPtr->requestsBuffer_.front()));\n                    thisPtr->requestsBuffer_.pop_front();\n                }\n            }\n            else\n            {\n                LOG_TRACE << \"connection disconnect\";\n                auto responseParser = connPtr->getContext<HttpResponseParser>();\n                if (responseParser && responseParser->parseResponseOnClose() &&\n                    responseParser->gotAll())\n                {\n                    auto &firstReq = thisPtr->pipeliningCallbacks_.front();\n                    if (firstReq.first->method() == Head)\n                    {\n                        responseParser->setForHeadMethod();\n                    }\n                    auto resp = responseParser->responseImpl();\n                    responseParser->reset();\n                    // temporary fix of dead tcpClientPtr_\n                    // TODO: fix HttpResponseParser when content-length absence\n                    thisPtr->tcpClientPtr_.reset();\n                    thisPtr->handleResponse(resp, std::move(firstReq), connPtr);\n                    if (!thisPtr->requestsBuffer_.empty())\n                    {\n                        thisPtr->createTcpClient();\n                    }\n                    return;\n                }\n                thisPtr->onError(ReqResult::NetworkFailure);\n            }\n        });\n    tcpClientPtr_->setConnectionErrorCallback([weakPtr]() {\n        auto thisPtr = weakPtr.lock();\n        if (!thisPtr)\n            return;\n        // can't connect to server\n        thisPtr->onError(ReqResult::BadServerAddress);\n    });\n    tcpClientPtr_->setMessageCallback(\n        [weakPtr](const trantor::TcpConnectionPtr &connPtr,\n                  trantor::MsgBuffer *msg) {\n            auto thisPtr = weakPtr.lock();\n            if (thisPtr)\n            {\n                thisPtr->onRecvMessage(connPtr, msg);\n            }\n        });\n    tcpClientPtr_->setSSLErrorCallback([weakPtr](SSLError err) {\n        auto thisPtr = weakPtr.lock();\n        if (!thisPtr)\n            return;\n        if (err == trantor::SSLError::kSSLHandshakeError)\n            thisPtr->onError(ReqResult::HandshakeError);\n        else if (err == trantor::SSLError::kSSLInvalidCertificate)\n            thisPtr->onError(ReqResult::InvalidCertificate);\n        else if (err == trantor::SSLError::kSSLProtocolError)\n            thisPtr->onError(ReqResult::EncryptionFailure);\n        else\n        {\n            LOG_FATAL << \"Invalid value for SSLError\";\n            abort();\n        }\n    });\n    tcpClientPtr_->connect();\n}\n\nHttpClientImpl::HttpClientImpl(trantor::EventLoop *loop,\n                               const trantor::InetAddress &addr,\n                               bool useSSL,\n                               bool useOldTLS,\n                               bool validateCert)\n    : loop_(loop),\n      serverAddr_(addr),\n      useSSL_(useSSL),\n      validateCert_(validateCert),\n      useOldTLS_(useOldTLS)\n{\n}\n\nHttpClientImpl::HttpClientImpl(trantor::EventLoop *loop,\n                               const std::string &hostString,\n                               bool useOldTLS,\n                               bool validateCert)\n    : loop_(loop), validateCert_(validateCert), useOldTLS_(useOldTLS)\n{\n    auto lowerHost = hostString;\n    std::transform(lowerHost.begin(),\n                   lowerHost.end(),\n                   lowerHost.begin(),\n                   [](unsigned char c) { return tolower(c); });\n    if (lowerHost.find(\"https://\") == 0)\n    {\n        useSSL_ = true;\n        lowerHost = lowerHost.substr(8);\n    }\n    else if (lowerHost.find(\"http://\") == 0)\n    {\n        useSSL_ = false;\n        lowerHost = lowerHost.substr(7);\n    }\n    else\n    {\n        return;\n    }\n    auto pos = lowerHost.find(']');\n    if (lowerHost[0] == '[' && pos != std::string::npos)\n    {\n        // ipv6\n        domain_ = lowerHost.substr(1, pos - 1);\n        if (lowerHost[pos + 1] == ':')\n        {\n            auto portStr = lowerHost.substr(pos + 2);\n            pos = portStr.find('/');\n            if (pos != std::string::npos)\n            {\n                portStr = portStr.substr(0, pos);\n            }\n            auto port = atoi(portStr.c_str());\n            if (port > 0 && port < 65536)\n            {\n                serverAddr_ = InetAddress(domain_, port, true);\n            }\n        }\n        else\n        {\n            if (useSSL_)\n            {\n                serverAddr_ = InetAddress(domain_, 443, true);\n            }\n            else\n            {\n                serverAddr_ = InetAddress(domain_, 80, true);\n            }\n        }\n    }\n    else\n    {\n        auto pos = lowerHost.find(':');\n        if (pos != std::string::npos)\n        {\n            domain_ = lowerHost.substr(0, pos);\n            auto portStr = lowerHost.substr(pos + 1);\n            pos = portStr.find('/');\n            if (pos != std::string::npos)\n            {\n                portStr = portStr.substr(0, pos);\n            }\n            auto port = atoi(portStr.c_str());\n            if (port > 0 && port < 65536)\n            {\n                serverAddr_ = InetAddress(domain_, port);\n            }\n        }\n        else\n        {\n            domain_ = lowerHost;\n            pos = domain_.find('/');\n            if (pos != std::string::npos)\n            {\n                domain_ = domain_.substr(0, pos);\n            }\n            if (useSSL_)\n            {\n                serverAddr_ = InetAddress(domain_, 443);\n            }\n            else\n            {\n                serverAddr_ = InetAddress(domain_, 80);\n            }\n        }\n    }\n    if (serverAddr_.isUnspecified())\n    {\n        isDomainName_ = true;\n    }\n    LOG_TRACE << \"userSSL=\" << useSSL_ << \" domain=\" << domain_;\n}\n\nHttpClientImpl::~HttpClientImpl()\n{\n    LOG_TRACE << \"Deconstruction HttpClient\";\n    if (resolverPtr_ && !(loop_->isInLoopThread()))\n    {\n        // Make sure the resolverPtr_ is destroyed in the correct thread.\n        loop_->queueInLoop([resolverPtr = std::move(resolverPtr_)]() {});\n    }\n}\n\nvoid HttpClientImpl::sendRequest(const drogon::HttpRequestPtr &req,\n                                 const drogon::HttpReqCallback &callback,\n                                 double timeout)\n{\n    auto thisPtr = shared_from_this();\n    loop_->runInLoop([thisPtr, req, callback = callback, timeout]() mutable {\n        thisPtr->sendRequestInLoop(req, std::move(callback), timeout);\n    });\n}\n\nvoid HttpClientImpl::sendRequest(const drogon::HttpRequestPtr &req,\n                                 drogon::HttpReqCallback &&callback,\n                                 double timeout)\n{\n    auto thisPtr = shared_from_this();\n    loop_->runInLoop(\n        [thisPtr, req, callback = std::move(callback), timeout]() mutable {\n            thisPtr->sendRequestInLoop(req, std::move(callback), timeout);\n        });\n}\n\nstruct RequestCallbackParams\n{\n    RequestCallbackParams(HttpReqCallback &&cb,\n                          HttpClientImplPtr client,\n                          HttpRequestPtr req)\n        : callback(std::move(cb)),\n          clientPtr(std::move(client)),\n          requestPtr(std::move(req))\n    {\n    }\n\n    const drogon::HttpReqCallback callback;\n    const HttpClientImplPtr clientPtr;\n    const HttpRequestPtr requestPtr;\n    bool timeoutFlag{false};\n};\n\nvoid HttpClientImpl::sendRequestInLoop(const HttpRequestPtr &req,\n                                       HttpReqCallback &&callback,\n                                       double timeout)\n{\n    if (timeout <= 0)\n    {\n        sendRequestInLoop(req, std::move(callback));\n        return;\n    }\n\n    auto callbackParamsPtr =\n        std::make_shared<RequestCallbackParams>(std::move(callback),\n                                                shared_from_this(),\n                                                req);\n\n    loop_->runAfter(\n        timeout,\n        [weakCallbackBackPtr =\n             std::weak_ptr<RequestCallbackParams>(callbackParamsPtr)] {\n            auto callbackParamsPtr = weakCallbackBackPtr.lock();\n            if (callbackParamsPtr != nullptr)\n            {\n                auto &thisPtr = callbackParamsPtr->clientPtr;\n                if (callbackParamsPtr->timeoutFlag)\n                {\n                    return;\n                }\n\n                callbackParamsPtr->timeoutFlag = true;\n\n                for (auto iter = thisPtr->requestsBuffer_.begin();\n                     iter != thisPtr->requestsBuffer_.end();\n                     ++iter)\n                {\n                    if (iter->first == callbackParamsPtr->requestPtr)\n                    {\n                        thisPtr->requestsBuffer_.erase(iter);\n                        break;\n                    }\n                }\n\n                (callbackParamsPtr->callback)(ReqResult::Timeout, nullptr);\n            }\n        });\n    sendRequestInLoop(req,\n                      [callbackParamsPtr](ReqResult r,\n                                          const HttpResponsePtr &resp) {\n                          if (callbackParamsPtr->timeoutFlag)\n                          {\n                              return;\n                          }\n                          callbackParamsPtr->timeoutFlag = true;\n                          (callbackParamsPtr->callback)(r, resp);\n                      });\n}\n\nstatic bool isValidIpAddr(const trantor::InetAddress &addr)\n{\n    if (addr.portNetEndian() == 0)\n    {\n        return false;\n    }\n    if (!addr.isIpV6())\n    {\n        return addr.ipNetEndian() != 0;\n    }\n    // Is ipv6\n    auto ipaddr = addr.ip6NetEndian();\n    for (int i = 0; i < 4; ++i)\n    {\n        if (ipaddr[i] != 0)\n        {\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid HttpClientImpl::sendRequestInLoop(const drogon::HttpRequestPtr &req,\n                                       drogon::HttpReqCallback &&callback)\n{\n    loop_->assertInLoopThread();\n    if (!static_cast<drogon::HttpRequestImpl *>(req.get())->passThrough())\n    {\n        req->addHeader(\"connection\", \"Keep-Alive\");\n        if (!userAgent_.empty())\n            req->addHeader(\"user-agent\", userAgent_);\n    }\n    // Set the host header if not already set\n    if (req->getHeader(\"host\").empty())\n    {\n        if (onDefaultPort())\n        {\n            req->addHeader(\"host\", host());\n        }\n        else\n        {\n            req->addHeader(\"host\", host() + \":\" + std::to_string(port()));\n        }\n    }\n\n    for (auto &cookie : validCookies_)\n    {\n        if ((cookie.expiresDate().microSecondsSinceEpoch() == 0 ||\n             cookie.expiresDate() > trantor::Date::now()) &&\n            (cookie.path().empty() || req->path().find(cookie.path()) == 0))\n        {\n            req->addCookie(cookie.key(), cookie.value());\n        }\n    }\n\n    if (!tcpClientPtr_)\n    {\n        auto callbackPtr =\n            std::make_shared<drogon::HttpReqCallback>(std::move(callback));\n        requestsBuffer_.push_back(\n            {req,\n             [thisPtr = shared_from_this(),\n              callbackPtr](ReqResult result, const HttpResponsePtr &response) {\n                 (*callbackPtr)(result, response);\n             }});\n\n        if (domain_.empty() || !isDomainName_)\n        {\n            // Valid ip address, no domain, connect directly\n            if (isValidIpAddr(serverAddr_))\n            {\n                createTcpClient();\n            }\n            // No ip address and no domain, respond with BadServerAddress\n            else\n            {\n                requestsBuffer_.pop_front();\n                (*callbackPtr)(ReqResult::BadServerAddress, nullptr);\n                assert(requestsBuffer_.empty());\n            }\n            return;\n        }\n\n        // A dns query is on going.\n        if (dns_)\n        {\n            return;\n        }\n\n        // Always do dns query when (re)connects a domain.\n        dns_ = true;\n        if (!resolverPtr_)\n        {\n            resolverPtr_ =\n                trantor::Resolver::newResolver(loop_, kDefaultDNSTimeout);\n        }\n        auto thisPtr = shared_from_this();\n        resolverPtr_->resolve(\n            domain_, [thisPtr](const trantor::InetAddress &addr) {\n                thisPtr->loop_->runInLoop([thisPtr, addr]() {\n                    // Retrieve port from old serverAddr_\n                    auto port = thisPtr->serverAddr_.portNetEndian();\n                    thisPtr->serverAddr_ = addr;\n                    thisPtr->serverAddr_.setPortNetEndian(port);\n                    LOG_TRACE << \"dns:domain=\" << thisPtr->domain_\n                              << \";ip=\" << thisPtr->serverAddr_.toIp();\n                    thisPtr->dns_ = false;\n\n                    if (isValidIpAddr(thisPtr->serverAddr_))\n                    {\n                        thisPtr->createTcpClient();\n                        return;\n                    }\n\n                    // DNS fail to get valid ip address,\n                    // respond all requests with BadServerAddress\n                    while (!(thisPtr->requestsBuffer_).empty())\n                    {\n                        auto &reqAndCb = (thisPtr->requestsBuffer_).front();\n                        reqAndCb.second(ReqResult::BadServerAddress, nullptr);\n                        (thisPtr->requestsBuffer_).pop_front();\n                    }\n                });\n            });\n\n        return;\n    }\n\n    // send request;\n    auto connPtr = tcpClientPtr_->connection();\n    auto thisPtr = shared_from_this();\n\n    // Not connected, push request to buffer and wait for connection\n    if (!connPtr || connPtr->disconnected())\n    {\n        requestsBuffer_.push_back(\n            {req,\n             [thisPtr,\n              callback = std::move(callback)](ReqResult result,\n                                              const HttpResponsePtr &response) {\n                 callback(result, response);\n             }});\n        return;\n    }\n\n    // Connected, send request now\n    if (pipeliningCallbacks_.size() <= pipeliningDepth_ &&\n        requestsBuffer_.empty())\n    {\n        sendReq(connPtr, req);\n        pipeliningCallbacks_.push(\n            {req,\n             [thisPtr,\n              callback = std::move(callback)](ReqResult result,\n                                              const HttpResponsePtr &response) {\n                 callback(result, response);\n             }});\n    }\n    else\n    {\n        requestsBuffer_.push_back(\n            {req,\n             [thisPtr,\n              callback = std::move(callback)](ReqResult result,\n                                              const HttpResponsePtr &response) {\n                 callback(result, response);\n             }});\n    }\n}\n\nvoid HttpClientImpl::sendReq(const trantor::TcpConnectionPtr &connPtr,\n                             const HttpRequestPtr &req)\n{\n    trantor::MsgBuffer buffer;\n    assert(req);\n    auto implPtr = static_cast<HttpRequestImpl *>(req.get());\n    implPtr->appendToBuffer(&buffer);\n    LOG_TRACE << \"Send request:\"\n              << std::string(buffer.peek(), buffer.readableBytes());\n    bytesSent_ += buffer.readableBytes();\n    connPtr->send(std::move(buffer));\n}\n\nvoid HttpClientImpl::handleResponse(\n    const HttpResponseImplPtr &resp,\n    std::pair<HttpRequestPtr, HttpReqCallback> &&reqAndCb,\n    const trantor::TcpConnectionPtr &connPtr)\n{\n    assert(!pipeliningCallbacks_.empty());\n    auto &coding = resp->getHeaderBy(\"content-encoding\");\n    if (coding == \"gzip\")\n    {\n        resp->gunzip();\n    }\n#ifdef USE_BROTLI\n    else if (coding == \"br\")\n    {\n        resp->brDecompress();\n    }\n#endif\n    auto cb = std::move(reqAndCb);\n    pipeliningCallbacks_.pop();\n    handleCookies(resp);\n    cb.second(ReqResult::Ok, resp);\n\n    // LOG_TRACE << \"pipelining buffer size=\" <<\n    // pipeliningCallbacks_.size(); LOG_TRACE << \"requests buffer size=\"\n    // << requestsBuffer_.size();\n\n    if (connPtr->connected())\n    {\n        if (!requestsBuffer_.empty())\n        {\n            auto &reqAndCallback = requestsBuffer_.front();\n            sendReq(connPtr, reqAndCallback.first);\n            pipeliningCallbacks_.push(std::move(reqAndCallback));\n            requestsBuffer_.pop_front();\n        }\n        else\n        {\n            if (resp->ifCloseConnection() && pipeliningCallbacks_.empty())\n            {\n                tcpClientPtr_.reset();\n            }\n        }\n    }\n    else\n    {\n        while (!pipeliningCallbacks_.empty())\n        {\n            auto cb = std::move(pipeliningCallbacks_.front());\n            pipeliningCallbacks_.pop();\n            cb.second(ReqResult::NetworkFailure, nullptr);\n        }\n    }\n}\n\nvoid HttpClientImpl::onRecvMessage(const trantor::TcpConnectionPtr &connPtr,\n                                   trantor::MsgBuffer *msg)\n{\n    auto responseParser = connPtr->getContext<HttpResponseParser>();\n\n    // LOG_TRACE << \"###:\" << msg->readableBytes();\n    auto msgSize = msg->readableBytes();\n    while (msg->readableBytes() > 0)\n    {\n        if (pipeliningCallbacks_.empty())\n        {\n            LOG_ERROR << \"More responses than expected!\";\n            connPtr->shutdown();\n            return;\n        }\n        auto &firstReq = pipeliningCallbacks_.front();\n        if (firstReq.first->method() == Head)\n        {\n            responseParser->setForHeadMethod();\n        }\n        if (!responseParser->parseResponse(msg))\n        {\n            onError(ReqResult::BadResponse);\n            bytesReceived_ += (msgSize - msg->readableBytes());\n            return;\n        }\n        if (responseParser->gotAll())\n        {\n            auto resp = responseParser->responseImpl();\n            resp->setPeerCertificate(connPtr->peerCertificate());\n            responseParser->reset();\n            bytesReceived_ += (msgSize - msg->readableBytes());\n            msgSize = msg->readableBytes();\n            handleResponse(resp, std::move(firstReq), connPtr);\n        }\n        else\n        {\n            bytesReceived_ += (msgSize - msg->readableBytes());\n            break;\n        }\n    }\n}\n\nHttpClientPtr HttpClient::newHttpClient(const std::string &ip,\n                                        uint16_t port,\n                                        bool useSSL,\n                                        trantor::EventLoop *loop,\n                                        bool useOldTLS,\n                                        bool validateCert)\n{\n    bool isIpv6 = ip.find(':') == std::string::npos ? false : true;\n    return std::make_shared<HttpClientImpl>(\n        loop == nullptr ? HttpAppFrameworkImpl::instance().getLoop() : loop,\n        trantor::InetAddress(ip, port, isIpv6),\n        useSSL,\n        useOldTLS,\n        validateCert);\n}\n\nHttpClientPtr HttpClient::newHttpClient(const std::string &hostString,\n                                        trantor::EventLoop *loop,\n                                        bool useOldTLS,\n                                        bool validateCert)\n{\n    return std::make_shared<HttpClientImpl>(\n        loop == nullptr ? HttpAppFrameworkImpl::instance().getLoop() : loop,\n        hostString,\n        useOldTLS,\n        validateCert);\n}\n\nvoid HttpClientImpl::onError(ReqResult result)\n{\n    while (!pipeliningCallbacks_.empty())\n    {\n        auto cb = std::move(pipeliningCallbacks_.front());\n        pipeliningCallbacks_.pop();\n        cb.second(result, nullptr);\n    }\n    while (!requestsBuffer_.empty())\n    {\n        auto cb = std::move(requestsBuffer_.front().second);\n        requestsBuffer_.pop_front();\n        cb(result, nullptr);\n    }\n    tcpClientPtr_.reset();\n}\n\nvoid HttpClientImpl::handleCookies(const HttpResponseImplPtr &resp)\n{\n    loop_->assertInLoopThread();\n    if (!enableCookies_)\n        return;\n    for (auto &iter : resp->getCookies())\n    {\n        auto &cookie = iter.second;\n        if (!cookie.domain().empty() && cookie.domain() != domain_)\n        {\n            continue;\n        }\n        if (cookie.isSecure())\n        {\n            if (useSSL_)\n            {\n                validCookies_.emplace_back(cookie);\n            }\n        }\n        else\n        {\n            validCookies_.emplace_back(cookie);\n        }\n    }\n}\n\nvoid HttpClientImpl::setCertPath(const std::string &cert,\n                                 const std::string &key)\n{\n    clientCertPath_ = cert;\n    clientKeyPath_ = key;\n}\n\nvoid HttpClientImpl::addSSLConfigs(\n    const std::vector<std::pair<std::string, std::string>> &sslConfCmds)\n{\n    for (const auto &cmd : sslConfCmds)\n    {\n        sslConfCmds_.push_back(cmd);\n    }\n}\n"
  },
  {
    "path": "lib/src/HttpClientImpl.h",
    "content": "/**\n *\n *  @file HttpClientImpl.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by the MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/Cookie.h>\n#include <drogon/HttpClient.h>\n#include <trantor/net/EventLoop.h>\n#include <trantor/net/Resolver.h>\n#include <trantor/net/TcpClient.h>\n#include <cstddef>\n#include <functional>\n#include <future>\n#include <list>\n#include <mutex>\n#include <queue>\n#include <vector>\n#include \"impl_forwards.h\"\n\nnamespace drogon\n{\nclass HttpClientImpl final : public HttpClient,\n                             public std::enable_shared_from_this<HttpClientImpl>\n{\n  public:\n    HttpClientImpl(trantor::EventLoop *loop,\n                   const trantor::InetAddress &addr,\n                   bool useSSL = false,\n                   bool useOldTLS = false,\n                   bool validateCert = true);\n    HttpClientImpl(trantor::EventLoop *loop,\n                   const std::string &hostString,\n                   bool useOldTLS = false,\n                   bool validateCert = true);\n    void sendRequest(const HttpRequestPtr &req,\n                     const HttpReqCallback &callback,\n                     double timeout = 0) override;\n    void sendRequest(const HttpRequestPtr &req,\n                     HttpReqCallback &&callback,\n                     double timeout = 0) override;\n\n    trantor::EventLoop *getLoop() override\n    {\n        return loop_;\n    }\n\n    void setPipeliningDepth(size_t depth) override\n    {\n        pipeliningDepth_ = depth;\n    }\n\n    ~HttpClientImpl();\n\n    void enableCookies(bool flag = true) override\n    {\n        enableCookies_ = flag;\n    }\n\n    void addCookie(const std::string &key, const std::string &value) override\n    {\n        validCookies_.emplace_back(Cookie(key, value));\n    }\n\n    void addCookie(const Cookie &cookie) override\n    {\n        validCookies_.emplace_back(cookie);\n    }\n\n    size_t bytesSent() const override\n    {\n        return bytesSent_;\n    }\n\n    size_t bytesReceived() const override\n    {\n        return bytesReceived_;\n    }\n\n    void setUserAgent(const std::string &userAgent) override\n    {\n        userAgent_ = userAgent;\n    }\n\n    uint16_t port() const override\n    {\n        return serverAddr_.toPort();\n    }\n\n    std::string host() const override\n    {\n        if (domain_.empty())\n            return serverAddr_.toIp();\n        return domain_;\n    }\n\n    bool secure() const override\n    {\n        return useSSL_;\n    }\n\n    void setCertPath(const std::string &cert, const std::string &key) override;\n    void addSSLConfigs(const std::vector<std::pair<std::string, std::string>>\n                           &sslConfCmds) override;\n\n    void setSockOptCallback(std::function<void(int)> cb) override\n    {\n        sockOptCallback_ = std::move(cb);\n    }\n\n    std::size_t requestsBufferSize() override\n    {\n        if (loop_->isInLoopThread())\n        {\n            return requestsBuffer_.size();\n        }\n        else\n        {\n            std::promise<std::size_t> bufferSize;\n            loop_->queueInLoop(\n                [&] { bufferSize.set_value(requestsBuffer_.size()); });\n            return bufferSize.get_future().get();\n        }\n    }\n\n  private:\n    std::shared_ptr<trantor::TcpClient> tcpClientPtr_;\n    trantor::EventLoop *loop_;\n    trantor::InetAddress serverAddr_;\n    bool useSSL_;\n    bool validateCert_;\n    void sendReq(const trantor::TcpConnectionPtr &connPtr,\n                 const HttpRequestPtr &req);\n    void sendRequestInLoop(const HttpRequestPtr &req,\n                           HttpReqCallback &&callback);\n    void sendRequestInLoop(const HttpRequestPtr &req,\n                           HttpReqCallback &&callback,\n                           double timeout);\n    void handleCookies(const HttpResponseImplPtr &resp);\n    void handleResponse(const HttpResponseImplPtr &resp,\n                        std::pair<HttpRequestPtr, HttpReqCallback> &&reqAndCb,\n                        const trantor::TcpConnectionPtr &connPtr);\n    void createTcpClient();\n    std::queue<std::pair<HttpRequestPtr, HttpReqCallback>> pipeliningCallbacks_;\n    std::list<std::pair<HttpRequestPtr, HttpReqCallback>> requestsBuffer_;\n    void onRecvMessage(const trantor::TcpConnectionPtr &, trantor::MsgBuffer *);\n    void onError(ReqResult result);\n    std::string domain_;\n    bool isDomainName_{true};  // true if domain_ is name\n    size_t pipeliningDepth_{0};\n    bool enableCookies_{false};\n    std::vector<Cookie> validCookies_;\n    size_t bytesSent_{0};\n    size_t bytesReceived_{0};\n    bool dns_{false};\n    std::shared_ptr<trantor::Resolver> resolverPtr_;\n    bool useOldTLS_{false};\n    std::string userAgent_{\"DrogonClient\"};\n    std::vector<std::pair<std::string, std::string>> sslConfCmds_;\n    std::string clientCertPath_;\n    std::string clientKeyPath_;\n    std::function<void(int)> sockOptCallback_;\n};\n\nusing HttpClientImplPtr = std::shared_ptr<HttpClientImpl>;\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpConnectionLimit.cc",
    "content": "/**\n *\n *  @file HttpConnectionLimit.cc\n *  @author Nitromelon\n *\n *  Copyright 2023, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpConnectionLimit.h\"\n#include <trantor/utils/Logger.h>\n\nusing namespace drogon;\n\nvoid HttpConnectionLimit::setMaxConnectionNum(size_t num)\n{\n    maxConnectionNum_ = num;\n}\n\nvoid HttpConnectionLimit::setMaxConnectionNumPerIP(size_t num)\n{\n    maxConnectionNumPerIP_ = num;\n}\n\nbool HttpConnectionLimit::tryAddConnection(\n    const trantor::TcpConnectionPtr &conn)\n{\n    assert(conn->connected());\n    if (connectionNum_.fetch_add(1, std::memory_order_relaxed) >\n        maxConnectionNum_)\n    {\n        return false;\n    }\n    if (maxConnectionNumPerIP_ > 0)\n    {\n        std::string ip = conn->peerAddr().toIp();\n        size_t numOnThisIp;\n        {\n            std::lock_guard<std::mutex> lock(mutex_);\n            numOnThisIp = (++ipConnectionsMap_[ip]);\n        }\n        if (numOnThisIp > maxConnectionNumPerIP_)\n        {\n            return false;\n        }\n    }\n\n    return true;\n}\n\nvoid HttpConnectionLimit::releaseConnection(\n    const trantor::TcpConnectionPtr &conn)\n{\n    assert(!conn->connected());\n    if (!conn->hasContext())\n    {\n        // If the connection is connected to the SSL port and then\n        // disconnected before the SSL handshake.\n        return;\n    }\n    connectionNum_.fetch_sub(1, std::memory_order_relaxed);\n    if (maxConnectionNumPerIP_ > 0)\n    {\n        std::string ip = conn->peerAddr().toIp();\n        std::lock_guard<std::mutex> lock(mutex_);\n        auto iter = ipConnectionsMap_.find(ip);\n        if (iter != ipConnectionsMap_.end())\n        {\n            if (--iter->second <= 0)\n            {\n                ipConnectionsMap_.erase(iter);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/HttpConnectionLimit.h",
    "content": "/**\n *\n *  @file HttpConnectionLimit.h\n *  @author Nitromelon\n *\n *  Copyright 2023, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <string>\n#include <unordered_map>\n#include <atomic>\n#include <cstddef>\n#include <mutex>\n#include <trantor/net/TcpConnection.h>\n\nnamespace drogon\n{\nclass HttpConnectionLimit\n{\n  public:\n    static HttpConnectionLimit &instance()\n    {\n        static HttpConnectionLimit inst;\n        return inst;\n    }\n\n    size_t getConnectionNum() const\n    {\n        return connectionNum_.load(std::memory_order_relaxed);\n    }\n\n    // don't set after start\n    void setMaxConnectionNum(size_t num);\n    void setMaxConnectionNumPerIP(size_t num);\n\n    bool tryAddConnection(const trantor::TcpConnectionPtr &conn);\n    void releaseConnection(const trantor::TcpConnectionPtr &conn);\n\n  private:\n    std::mutex mutex_;\n\n    size_t maxConnectionNum_{100000};\n    std::atomic<size_t> connectionNum_{0};\n\n    size_t maxConnectionNumPerIP_{0};\n    std::unordered_map<std::string, size_t> ipConnectionsMap_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpControllerBinder.cc",
    "content": "/**\n *\n *  @file HttpControllerBinder.cc\n *  @author Nitromelon\n *\n *  Copyright 2023, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpControllerBinder.h\"\n#include \"HttpResponseImpl.h\"\n#include <drogon/HttpSimpleController.h>\n#include <drogon/WebSocketController.h>\n\nnamespace drogon\n{\n\nvoid HttpSimpleControllerBinder::handleRequest(\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback) const\n{\n    // Binders without controller should be removed after run()\n    assert(controller_);\n\n    try\n    {\n        auto cb = callback;  // copy\n        controller_->asyncHandleHttpRequest(req, std::move(cb));\n    }\n    catch (const std::exception &e)\n    {\n        app().getExceptionHandler()(e, req, std::move(callback));\n        return;\n    }\n    catch (...)\n    {\n        LOG_ERROR << \"Exception not derived from std::exception\";\n        return;\n    }\n}\n\nvoid HttpControllerBinder::handleRequest(\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback) const\n{\n    auto &paramsVector = req->getRoutingParameters();\n    std::deque<std::string> params(paramsVector.begin(), paramsVector.end());\n    binderPtr_->handleHttpRequest(params, req, std::move(callback));\n}\n\nvoid WebsocketControllerBinder::handleRequest(\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback) const\n{\n    std::string wsKey = req->getHeaderBy(\"sec-websocket-key\");\n    wsKey.append(\"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\");\n    unsigned char accKey[20];\n    auto sha1 = trantor::utils::sha1(wsKey.c_str(), wsKey.length());\n    memcpy(accKey, &sha1, sizeof(sha1));\n    auto base64Key = utils::base64Encode(accKey, sizeof(accKey));\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setStatusCode(k101SwitchingProtocols);\n    resp->addHeader(\"Upgrade\", \"websocket\");\n    resp->addHeader(\"Connection\", \"Upgrade\");\n    resp->addHeader(\"Sec-WebSocket-Accept\", base64Key);\n    callback(resp);\n}\n\nvoid WebsocketControllerBinder::handleNewConnection(\n    const HttpRequestImplPtr &req,\n    const WebSocketConnectionImplPtr &wsConnPtr) const\n{\n    auto ctrlPtr = controller_;\n    assert(ctrlPtr);\n    wsConnPtr->setMessageCallback(\n        [ctrlPtr](std::string &&message,\n                  const WebSocketConnectionImplPtr &connPtr,\n                  const WebSocketMessageType &type) {\n            ctrlPtr->handleNewMessage(connPtr, std::move(message), type);\n        });\n    wsConnPtr->setCloseCallback(\n        [ctrlPtr](const WebSocketConnectionImplPtr &connPtr) {\n            ctrlPtr->handleConnectionClosed(connPtr);\n        });\n    ctrlPtr->handleNewConnection(req, wsConnPtr);\n}\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpControllerBinder.h",
    "content": "/**\n *\n *  @file HttpControllerBinder.h\n *  @author Nitromelon\n *\n *  Copyright 2023, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"ControllerBinderBase.h\"\n#include \"HttpRequestImpl.h\"\n#include \"WebSocketConnectionImpl.h\"\n#include <drogon/HttpBinder.h>\n\nnamespace drogon\n{\n\nclass HttpSimpleControllerBinder : public ControllerBinderBase\n{\n  public:\n    void handleRequest(\n        const HttpRequestImplPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) const override;\n\n    std::shared_ptr<HttpSimpleControllerBase> controller_;\n};\n\nclass HttpControllerBinder : public ControllerBinderBase\n{\n  public:\n    void handleRequest(\n        const HttpRequestImplPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) const override;\n\n    bool isStreamHandler() const override\n    {\n        assert(binderPtr_);\n        return binderPtr_->isStreamHandler();\n    }\n\n    internal::HttpBinderBasePtr binderPtr_;\n    std::vector<size_t> parameterPlaces_;\n    std::vector<std::pair<std::string, size_t>> queryParametersPlaces_;\n};\n\nstruct WebsocketControllerBinder : public ControllerBinderBase\n{\n    std::shared_ptr<WebSocketControllerBase> controller_;\n\n    void handleRequest(\n        const HttpRequestImplPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) const override;\n\n    void handleNewConnection(const HttpRequestImplPtr &req,\n                             const WebSocketConnectionImplPtr &wsConnPtr) const;\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpControllersRouter.cc",
    "content": "/**\n *\n *  @file HttpControllersRouter.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpControllersRouter.h\"\n#include \"HttpControllerBinder.h\"\n#include \"HttpRequestImpl.h\"\n#include \"HttpAppFrameworkImpl.h\"\n#include \"MiddlewaresFunction.h\"\n#include <drogon/HttpSimpleController.h>\n#include <drogon/WebSocketController.h>\n#include <algorithm>\n\nusing namespace drogon;\n\nvoid HttpControllersRouter::init(\n    const std::vector<trantor::EventLoop *> & /*ioLoops*/)\n{\n    auto initMiddlewaresAndCorsMethods = [](const auto &item) {\n        auto corsMethods = std::make_shared<std::string>(\"OPTIONS,\");\n        for (size_t i = 0; i < Invalid; ++i)\n        {\n            auto &binder = item.binders_[i];\n            if (binder)\n            {\n                binder->middlewares_ = middlewares_function::createMiddlewares(\n                    binder->middlewareNames_);\n                binder->corsMethods_ = corsMethods;\n                if (binder->isCORS_)\n                {\n                    if (i == Get)\n                    {\n                        corsMethods->append(\"GET,HEAD,\");\n                    }\n                    else if (i != Options)\n                    {\n                        corsMethods->append(to_string_view((HttpMethod)i));\n                        corsMethods->append(\",\");\n                    }\n                }\n            }\n        }\n        corsMethods->pop_back();  // remove last comma\n    };\n\n    for (auto &iter : simpleCtrlMap_)\n    {\n        initMiddlewaresAndCorsMethods(iter.second);\n    }\n\n    for (auto &iter : wsCtrlMap_)\n    {\n        initMiddlewaresAndCorsMethods(iter.second);\n    }\n\n    for (auto &router : wsCtrlVector_)\n    {\n        initMiddlewaresAndCorsMethods(router);\n    }\n\n    for (auto &router : ctrlVector_)\n    {\n        router.regex_ = std::regex(router.pathParameterPattern_,\n                                   std::regex_constants::icase);\n        initMiddlewaresAndCorsMethods(router);\n    }\n\n    for (auto &p : ctrlMap_)\n    {\n        auto &router = p.second;\n        router.regex_ = std::regex(router.pathParameterPattern_,\n                                   std::regex_constants::icase);\n        initMiddlewaresAndCorsMethods(router);\n    }\n}\n\nvoid HttpControllersRouter::reset()\n{\n    simpleCtrlMap_.clear();\n    ctrlMap_.clear();\n    ctrlVector_.clear();\n    wsCtrlMap_.clear();\n    wsCtrlVector_.clear();\n}\n\nstd::vector<HttpHandlerInfo> HttpControllersRouter::getHandlersInfo() const\n{\n    std::vector<HttpHandlerInfo> ret;\n    auto gatherInfo = [&ret](const std::string &path, const auto &item) {\n        for (size_t i = 0; i < Invalid; ++i)\n        {\n            if (item.binders_[i])\n            {\n                std::string description;\n                if constexpr (std::is_same_v<std::decay_t<decltype(item)>,\n                                             SimpleControllerRouterItem>)\n\n                {\n                    description = std::string(\"HttpSimpleController: \") +\n                                  item.binders_[i]->handlerName_;\n                }\n                else if constexpr (std::is_same_v<\n                                       std::decay_t<decltype(item)>,\n                                       WebSocketControllerRouterItem> ||\n                                   std::is_same_v<\n                                       std::decay_t<decltype(item)>,\n                                       RegExWebSocketControllerRouterItem>)\n                {\n                    description = std::string(\"WebsocketController: \") +\n                                  item.binders_[i]->handlerName_;\n                }\n                else\n                {\n                    description =\n                        item.binders_[i]->handlerName_.empty()\n                            ? std::string(\"Handler: \") +\n                                  item.binders_[i]->binderPtr_->handlerName()\n                            : std::string(\"HttpController: \") +\n                                  item.binders_[i]->handlerName_;\n                }\n                ret.emplace_back(path, (HttpMethod)i, std::move(description));\n            }\n        }\n    };\n\n    for (auto &[path, item] : simpleCtrlMap_)\n    {\n        gatherInfo(path, item);\n    }\n    for (auto &item : ctrlVector_)\n    {\n        gatherInfo(item.pathPattern_, item);\n    }\n    for (auto &[key, item] : ctrlMap_)\n    {\n        gatherInfo(item.pathPattern_, item);\n    }\n    for (auto &[path, item] : wsCtrlMap_)\n    {\n        gatherInfo(path, item);\n    }\n    for (auto &item : wsCtrlVector_)\n    {\n        gatherInfo(item.pathPattern_, item);\n    }\n    return ret;\n}\n\ntemplate <typename Binder, typename RouterItem>\nstatic void addCtrlBinderToRouterItem(const std::shared_ptr<Binder> &binderPtr,\n                                      RouterItem &router,\n                                      const std::vector<HttpMethod> &methods)\n{\n    if (!methods.empty())\n    {\n        for (const auto &method : methods)\n        {\n            router.binders_[method] = binderPtr;\n            if (method == Options)\n            {\n                binderPtr->isCORS_ = true;\n            }\n        }\n    }\n    else\n    {\n        // All HTTP methods are valid\n        binderPtr->isCORS_ = true;\n        for (int i = 0; i < Invalid; ++i)\n        {\n            router.binders_[i] = binderPtr;\n        }\n    }\n}\n\nstruct SimpleControllerProcessResult\n{\n    std::string lowerPath;\n    std::vector<HttpMethod> validMethods;\n    std::vector<std::string> middlewares;\n};\n\nstatic SimpleControllerProcessResult processSimpleControllerParams(\n    const std::string &pathName,\n    const std::vector<internal::HttpConstraint> &constraints)\n{\n    std::string path(pathName);\n    std::transform(pathName.begin(),\n                   pathName.end(),\n                   path.begin(),\n                   [](unsigned char c) { return tolower(c); });\n    std::vector<HttpMethod> validMethods;\n    std::vector<std::string> middlewareNames;\n    for (const auto &constraint : constraints)\n    {\n        if (constraint.type() == internal::ConstraintType::HttpMiddleware)\n        {\n            middlewareNames.push_back(constraint.getMiddlewareName());\n        }\n        else if (constraint.type() == internal::ConstraintType::HttpMethod)\n        {\n            validMethods.push_back(constraint.getHttpMethod());\n        }\n        else\n        {\n            LOG_ERROR << \"Invalid controller constraint type\";\n            // Used to call exit() here, but that's not very nice.\n        }\n    }\n    return {\n        std::move(path),\n        std::move(validMethods),\n        std::move(middlewareNames),\n    };\n}\n\nvoid HttpControllersRouter::registerHttpSimpleController(\n    const std::string &pathName,\n    const std::string &ctrlName,\n    const std::vector<internal::HttpConstraint> &constraints)\n{\n    assert(!pathName.empty());\n    assert(!ctrlName.empty());\n    // Note: some compiler version failed to handle structural bindings with\n    // lambda capture\n    auto result = processSimpleControllerParams(pathName, constraints);\n    std::string path = std::move(result.lowerPath);\n\n    auto &item = simpleCtrlMap_[path];\n    auto binder = std::make_shared<HttpSimpleControllerBinder>();\n    binder->handlerName_ = ctrlName;\n    binder->middlewareNames_ = result.middlewares;\n    drogon::app().getLoop()->queueInLoop([this, binder, ctrlName, path]() {\n        auto &object_ = DrClassMap::getSingleInstance(ctrlName);\n        auto controller =\n            std::dynamic_pointer_cast<HttpSimpleControllerBase>(object_);\n        if (!controller)\n        {\n            LOG_ERROR << \"Controller class not found: \" << ctrlName;\n            simpleCtrlMap_.erase(path);\n            return;\n        }\n        binder->controller_ = controller;\n        // Recreate this with the correct number of threads.\n        binder->responseCache_ = IOThreadStorage<HttpResponsePtr>();\n    });\n\n    addCtrlBinderToRouterItem(binder, item, result.validMethods);\n}\n\nvoid HttpControllersRouter::registerWebSocketController(\n    const std::string &pathName,\n    const std::string &ctrlName,\n    const std::vector<internal::HttpConstraint> &constraints)\n{\n    assert(!pathName.empty());\n    assert(!ctrlName.empty());\n    auto result = processSimpleControllerParams(pathName, constraints);\n    std::string path = std::move(result.lowerPath);\n\n    auto &item = wsCtrlMap_[path];\n    auto binder = std::make_shared<WebsocketControllerBinder>();\n    binder->handlerName_ = ctrlName;\n    binder->middlewareNames_ = result.middlewares;\n    drogon::app().getLoop()->queueInLoop([this, binder, ctrlName, path]() {\n        auto &object_ = DrClassMap::getSingleInstance(ctrlName);\n        auto controller =\n            std::dynamic_pointer_cast<WebSocketControllerBase>(object_);\n        if (!controller)\n        {\n            LOG_ERROR << \"Websocket controller class not found: \" << ctrlName;\n            wsCtrlMap_.erase(path);\n            return;\n        }\n\n        binder->controller_ = controller;\n    });\n\n    addCtrlBinderToRouterItem(binder, item, result.validMethods);\n}\n\nvoid HttpControllersRouter::registerWebSocketControllerRegex(\n    const std::string &regExp,\n    const std::string &ctrlName,\n    const std::vector<internal::HttpConstraint> &constraints)\n{\n    assert(!regExp.empty());\n    assert(!ctrlName.empty());\n    auto result = processSimpleControllerParams(regExp, constraints);\n    auto binder = std::make_shared<WebsocketControllerBinder>();\n    binder->handlerName_ = ctrlName;\n    binder->middlewareNames_ = result.middlewares;\n    drogon::app().getLoop()->queueInLoop([binder, ctrlName]() {\n        auto &object_ = DrClassMap::getSingleInstance(ctrlName);\n        auto controller =\n            std::dynamic_pointer_cast<WebSocketControllerBase>(object_);\n        binder->controller_ = controller;\n    });\n    struct RegExWebSocketControllerRouterItem router;\n    router.pathPattern_ = regExp;\n    router.regex_ = regExp;\n    addCtrlBinderToRouterItem(binder, router, result.validMethods);\n    wsCtrlVector_.push_back(std::move(router));\n}\n\nvoid HttpControllersRouter::addHttpRegex(\n    const std::string &regExp,\n    const internal::HttpBinderBasePtr &binder,\n    const std::vector<HttpMethod> &validMethods,\n    const std::vector<std::string> &middlewareNames,\n    const std::string &handlerName)\n{\n    auto binderInfo = std::make_shared<HttpControllerBinder>();\n    binderInfo->middlewareNames_ = middlewareNames;\n    binderInfo->handlerName_ = handlerName;\n    binderInfo->binderPtr_ = binder;\n    drogon::app().getLoop()->queueInLoop([binderInfo]() {\n        // Recreate this with the correct number of threads.\n        binderInfo->responseCache_ = IOThreadStorage<HttpResponsePtr>();\n    });\n\n    addRegexCtrlBinder(binderInfo, regExp, regExp, validMethods);\n}\n\nvoid HttpControllersRouter::addHttpPath(\n    const std::string &path,\n    const internal::HttpBinderBasePtr &binder,\n    const std::vector<HttpMethod> &validMethods,\n    const std::vector<std::string> &middlewareNames,\n    const std::string &handlerName)\n{\n    // Path is like /api/v1/service/method/{1}/{2}/xxx...\n    std::vector<size_t> places;\n    std::string tmpPath = path;\n    std::string paras;\n    static const std::regex regex(\"\\\\{([^/]*)\\\\}\");\n    std::smatch results;\n    auto pos = tmpPath.find('?');\n    if (pos != std::string::npos)\n    {\n        paras = tmpPath.substr(pos + 1);\n        tmpPath = tmpPath.substr(0, pos);\n    }\n    std::string originPath = tmpPath;\n    size_t placeIndex = 1;\n    // Process path parameter placeholders\n    while (std::regex_search(tmpPath, results, regex))\n    {\n        if (results.size() > 1)\n        {\n            auto result = results[1].str();\n            if (!result.empty() &&\n                std::all_of(result.begin(), result.end(), [](const char c) {\n                    return std::isdigit(c);\n                }))\n            {\n                auto place = (size_t)std::stoi(result);\n                if (place > binder->paramCount() || place == 0)\n                {\n                    LOG_ERROR << \"Parameter placeholder(value=\" << place\n                              << \") out of range (1 to \" << binder->paramCount()\n                              << \")\";\n                    LOG_ERROR << \"Path pattern: \" << path;\n                    exit(1);\n                }\n                if (!std::all_of(places.begin(),\n                                 places.end(),\n                                 [place](size_t i) { return i != place; }))\n                {\n                    LOG_ERROR << \"Parameter placeholders are duplicated: index=\"\n                              << place;\n                    LOG_ERROR << \"Path pattern: \" << path;\n                    exit(1);\n                }\n                places.push_back(place);\n            }\n            else\n            {\n                static const std::regex regNumberAndName(\"([0-9]+):.*\");\n                std::smatch regexResult;\n                if (std::regex_match(result, regexResult, regNumberAndName))\n                {\n                    assert(regexResult.size() == 2 && regexResult[1].matched);\n                    auto num = regexResult[1].str();\n                    auto place = (size_t)std::stoi(num);\n                    if (place > binder->paramCount() || place == 0)\n                    {\n                        LOG_ERROR << \"Parameter placeholder(value=\" << place\n                                  << \") out of range (1 to \"\n                                  << binder->paramCount() << \")\";\n                        LOG_ERROR << \"Path pattern: \" << path;\n                        exit(1);\n                    }\n                    if (!std::all_of(places.begin(),\n                                     places.end(),\n                                     [place](size_t i) { return i != place; }))\n                    {\n                        LOG_ERROR\n                            << \"Parameter placeholders are duplicated: index=\"\n                            << place;\n                        LOG_ERROR << \"Path pattern: \" << path;\n                        exit(1);\n                    }\n                    places.push_back(place);\n                }\n                else\n                {\n                    if (!std::all_of(places.begin(),\n                                     places.end(),\n                                     [placeIndex](size_t i) {\n                                         return i != placeIndex;\n                                     }))\n                    {\n                        LOG_ERROR\n                            << \"Parameter placeholders are duplicated: index=\"\n                            << placeIndex;\n                        LOG_ERROR << \"Path pattern: \" << path;\n                        exit(1);\n                    }\n                    places.push_back(placeIndex);\n                }\n            }\n            ++placeIndex;\n        }\n        tmpPath = results.suffix();\n    }\n    // Process query parameter placeholders\n    std::vector<std::pair<std::string, size_t>> parametersPlaces;\n    if (!paras.empty())\n    {\n        static const std::regex pregex(\"([^&]*)=\\\\{([^&]*)\\\\}&*\");\n        while (std::regex_search(paras, results, pregex))\n        {\n            if (results.size() > 2)\n            {\n                auto result = results[2].str();\n                if (!result.empty() &&\n                    std::all_of(result.begin(), result.end(), [](const char c) {\n                        return std::isdigit(c);\n                    }))\n                {\n                    auto place = (size_t)std::stoi(result);\n                    if (place > binder->paramCount() || place == 0)\n                    {\n                        LOG_ERROR << \"Parameter placeholder(value=\" << place\n                                  << \") out of range (1 to \"\n                                  << binder->paramCount() << \")\";\n                        LOG_ERROR << \"Path pattern: \" << path;\n                        exit(1);\n                    }\n                    if (!std::all_of(places.begin(),\n                                     places.end(),\n                                     [place](size_t i) {\n                                         return i != place;\n                                     }) ||\n                        !all_of(parametersPlaces.begin(),\n                                parametersPlaces.end(),\n                                [place](const std::pair<std::string, size_t>\n                                            &item) {\n                                    return item.second != place;\n                                }))\n                    {\n                        LOG_ERROR << \"Parameter placeholders are \"\n                                     \"duplicated: index=\"\n                                  << place;\n                        LOG_ERROR << \"Path pattern: \" << path;\n                        exit(1);\n                    }\n                    parametersPlaces.emplace_back(results[1].str(), place);\n                }\n                else\n                {\n                    std::regex regNumberAndName(\"([0-9]+):.*\");\n                    std::smatch regexResult;\n                    if (std::regex_match(result, regexResult, regNumberAndName))\n                    {\n                        assert(regexResult.size() == 2 &&\n                               regexResult[1].matched);\n                        auto num = regexResult[1].str();\n                        auto place = (size_t)std::stoi(num);\n                        if (place > binder->paramCount() || place == 0)\n                        {\n                            LOG_ERROR << \"Parameter placeholder(value=\" << place\n                                      << \") out of range (1 to \"\n                                      << binder->paramCount() << \")\";\n                            LOG_ERROR << \"Path pattern: \" << path;\n                            exit(1);\n                        }\n                        if (!std::all_of(places.begin(),\n                                         places.end(),\n                                         [place](size_t i) {\n                                             return i != place;\n                                         }) ||\n                            !all_of(parametersPlaces.begin(),\n                                    parametersPlaces.end(),\n                                    [place](const std::pair<std::string, size_t>\n                                                &item) {\n                                        return item.second != place;\n                                    }))\n                        {\n                            LOG_ERROR << \"Parameter placeholders are \"\n                                         \"duplicated: index=\"\n                                      << place;\n                            LOG_ERROR << \"Path pattern: \" << path;\n                            exit(1);\n                        }\n                        parametersPlaces.emplace_back(results[1].str(), place);\n                    }\n                    else\n                    {\n                        if (!std::all_of(places.begin(),\n                                         places.end(),\n                                         [placeIndex](size_t i) {\n                                             return i != placeIndex;\n                                         }) ||\n                            !all_of(parametersPlaces.begin(),\n                                    parametersPlaces.end(),\n                                    [placeIndex](\n                                        const std::pair<std::string, size_t>\n                                            &item) {\n                                        return item.second != placeIndex;\n                                    }))\n                        {\n                            LOG_ERROR << \"Parameter placeholders are \"\n                                         \"duplicated: index=\"\n                                      << placeIndex;\n                            LOG_ERROR << \"Path pattern: \" << path;\n                            exit(1);\n                        }\n                        parametersPlaces.emplace_back(results[1].str(),\n                                                      placeIndex);\n                    }\n                }\n                ++placeIndex;\n            }\n            paras = results.suffix();\n        }\n    }\n\n    // Create new ControllerBinder\n    auto binderInfo = std::make_shared<HttpControllerBinder>();\n    binderInfo->middlewareNames_ = middlewareNames;\n    binderInfo->handlerName_ = handlerName;\n    binderInfo->binderPtr_ = binder;\n    binderInfo->parameterPlaces_ = std::move(places);\n    binderInfo->queryParametersPlaces_ = std::move(parametersPlaces);\n    drogon::app().getLoop()->queueInLoop([binderInfo]() {\n        // Recreate this with the correct number of threads.\n        binderInfo->responseCache_ = IOThreadStorage<HttpResponsePtr>();\n    });\n\n    // Create or update RouterItem\n    auto pathParameterPattern =\n        std::regex_replace(originPath, regex, \"([^/]*)\");\n    if (originPath != pathParameterPattern)  // require regex\n    {\n        addRegexCtrlBinder(binderInfo,\n                           path,\n                           pathParameterPattern,\n                           validMethods);\n        return;\n    }\n\n    std::string loweredPath;\n    std::transform(originPath.begin(),\n                   originPath.end(),\n                   std::back_inserter(loweredPath),\n                   [](unsigned char c) { return tolower(c); });\n\n    HttpControllerRouterItem *routerItemPtr;\n    // If exists another controllers on the same route, update them\n    auto it = ctrlMap_.find(loweredPath);\n    if (it != ctrlMap_.end())\n    {\n        routerItemPtr = &it->second;\n    }\n    // Create new router item if not exists\n    else\n    {\n        struct HttpControllerRouterItem router;\n        router.pathParameterPattern_ = pathParameterPattern;\n        router.pathPattern_ = path;\n        routerItemPtr =\n            &ctrlMap_.emplace(loweredPath, std::move(router)).first->second;\n    }\n\n    addCtrlBinderToRouterItem(binderInfo, *routerItemPtr, validMethods);\n}\n\nRouteResult HttpControllersRouter::route(const HttpRequestImplPtr &req)\n{\n    // Find simple controller\n    std::string loweredPath(req->path().length(), 0);\n    std::transform(req->path().begin(),\n                   req->path().end(),\n                   loweredPath.begin(),\n                   [](unsigned char c) { return tolower(c); });\n    {\n        auto it = simpleCtrlMap_.find(loweredPath);\n        if (it != simpleCtrlMap_.end())\n        {\n            auto &ctrlInfo = it->second;\n            req->setMatchedPathPattern(it->first);\n            auto &binder = ctrlInfo.binders_[req->method()];\n            if (!binder)\n            {\n                return {RouteResult::MethodNotAllowed, nullptr};\n            }\n            return {RouteResult::Success, binder};\n        }\n    }\n\n    // Find http controller\n    HttpControllerRouterItem *routerItemPtr = nullptr;\n    std::smatch result;\n    auto it = ctrlMap_.find(loweredPath);\n    // Try to find a controller in the hash map. If can't linear search\n    // with regex.\n    if (it != ctrlMap_.end())\n    {\n        routerItemPtr = &it->second;\n    }\n    else\n    {\n        for (auto &item : ctrlVector_)\n        {\n            const auto &ctrlRegex = item.regex_;\n            if (item.binders_[req->method()] &&\n                std::regex_match(req->path(), result, ctrlRegex))\n            {\n                routerItemPtr = &item;\n                break;\n            }\n        }\n    }\n\n    // No handler found\n    if (!routerItemPtr)\n    {\n        return {RouteResult::NotFound, nullptr};\n    }\n    HttpControllerRouterItem &routerItem = *routerItemPtr;\n    assert(Invalid > req->method());\n    req->setMatchedPathPattern(routerItem.pathPattern_);\n    auto &binder = routerItem.binders_[req->method()];\n    if (!binder)\n    {\n        return {RouteResult::MethodNotAllowed, nullptr};\n    }\n    std::vector<std::string> params;\n    for (size_t j = 1; j < result.size(); ++j)\n    {\n        if (!result[j].matched)\n            continue;\n        size_t place = j;\n        if (j <= binder->parameterPlaces_.size())\n        {\n            place = binder->parameterPlaces_[j - 1];\n        }\n        if (place > params.size())\n            params.resize(place);\n        params[place - 1] = result[j].str();\n        LOG_TRACE << \"place=\" << place << \" para:\" << params[place - 1];\n    }\n\n    if (!binder->queryParametersPlaces_.empty())\n    {\n        auto &queryPara = req->getParameters();\n        for (const auto &paraPlace : binder->queryParametersPlaces_)\n        {\n            auto place = paraPlace.second;\n            if (place > params.size())\n                params.resize(place);\n            auto iter = queryPara.find(paraPlace.first);\n            if (iter != queryPara.end())\n            {\n                params[place - 1] = iter->second;\n            }\n            else\n            {\n                params[place - 1] = std::string{};\n            }\n        }\n    }\n    req->setRoutingParameters(std::move(params));\n    return {RouteResult::Success, binder};\n}\n\nRouteResult HttpControllersRouter::routeWs(const HttpRequestImplPtr &req)\n{\n    std::string wsKey = req->getHeaderBy(\"sec-websocket-key\");\n    if (!wsKey.empty())\n    {\n        std::string pathLower(req->path().length(), 0);\n        std::transform(req->path().begin(),\n                       req->path().end(),\n                       pathLower.begin(),\n                       [](unsigned char c) { return tolower(c); });\n        auto iter = wsCtrlMap_.find(pathLower);\n        if (iter != wsCtrlMap_.end())\n        {\n            auto &ctrlInfo = iter->second;\n            req->setMatchedPathPattern(iter->first);\n            auto &binder = ctrlInfo.binders_[req->method()];\n            if (!binder)\n            {\n                return {RouteResult::MethodNotAllowed, nullptr};\n            }\n            return {RouteResult::Success, binder};\n        }\n        else\n        {\n            for (auto &ctrlInfo : wsCtrlVector_)\n            {\n                auto const &wsCtrlRegex = ctrlInfo.regex_;\n                std::smatch result;\n                if (std::regex_match(req->path(), result, wsCtrlRegex))\n                {\n                    req->setMatchedPathPattern(ctrlInfo.pathPattern_);\n                    auto &binder = ctrlInfo.binders_[req->method()];\n                    if (!binder)\n                    {\n                        return {RouteResult::MethodNotAllowed, nullptr};\n                    }\n                    return {RouteResult::Success, binder};\n                }\n            }\n        }\n    }\n    return {RouteResult::NotFound, nullptr};\n}\n\nvoid HttpControllersRouter::addRegexCtrlBinder(\n    const std::shared_ptr<HttpControllerBinder> &binderPtr,\n    const std::string &pathPattern,\n    const std::string &pathParameterPattern,\n    const std::vector<HttpMethod> &methods)\n{\n    HttpControllerRouterItem *routerItemPtr;\n    auto existRouter = std::find_if(ctrlVector_.begin(),\n                                    ctrlVector_.end(),\n                                    [&pathParameterPattern](const auto &item) {\n                                        return item.pathParameterPattern_ ==\n                                               pathParameterPattern;\n                                    });\n    if (existRouter == ctrlVector_.end())\n    {\n        struct HttpControllerRouterItem router;\n        router.pathParameterPattern_ = pathParameterPattern;\n        router.pathPattern_ = pathPattern;\n        ctrlVector_.push_back(std::move(router));\n        routerItemPtr = &ctrlVector_.back();\n    }\n    else\n    {\n        routerItemPtr = &(*existRouter);\n    }\n\n    addCtrlBinderToRouterItem(binderPtr, *routerItemPtr, methods);\n}\n"
  },
  {
    "path": "lib/src/HttpControllersRouter.h",
    "content": "/**\n *\n *  HttpControllersRouter.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"impl_forwards.h\"\n#include \"ControllerBinderBase.h\"\n#include <trantor/utils/NonCopyable.h>\n#include <memory>\n#include <regex>\n#include <string>\n#include <vector>\n#include <unordered_map>\n\nnamespace drogon\n{\nclass HttpControllerBinder;\nclass HttpSimpleControllerBinder;\nstruct WebsocketControllerBinder;\n\nclass HttpControllersRouter : public trantor::NonCopyable\n{\n  public:\n    static HttpControllersRouter &instance()\n    {\n        static HttpControllersRouter inst;\n        return inst;\n    }\n\n    void init(const std::vector<trantor::EventLoop *> &ioLoops);\n    // clean all resources\n    void reset();\n\n    void registerHttpSimpleController(\n        const std::string &pathName,\n        const std::string &ctrlName,\n        const std::vector<internal::HttpConstraint> &constraints);\n    void registerWebSocketController(\n        const std::string &pathName,\n        const std::string &ctrlName,\n        const std::vector<internal::HttpConstraint> &constraints);\n    void registerWebSocketControllerRegex(\n        const std::string &regExp,\n        const std::string &ctrlName,\n        const std::vector<internal::HttpConstraint> &constraints);\n    void addHttpPath(const std::string &path,\n                     const internal::HttpBinderBasePtr &binder,\n                     const std::vector<HttpMethod> &validMethods,\n                     const std::vector<std::string> &middlewareNames,\n                     const std::string &handlerName = \"\");\n    void addHttpRegex(const std::string &regExp,\n                      const internal::HttpBinderBasePtr &binder,\n                      const std::vector<HttpMethod> &validMethods,\n                      const std::vector<std::string> &middlewareNames,\n                      const std::string &handlerName = \"\");\n    RouteResult route(const HttpRequestImplPtr &req);\n    RouteResult routeWs(const HttpRequestImplPtr &req);\n    std::vector<HttpHandlerInfo> getHandlersInfo() const;\n\n  private:\n    void addRegexCtrlBinder(\n        const std::shared_ptr<HttpControllerBinder> &binderPtr,\n        const std::string &pathPattern,\n        const std::string &pathParameterPattern,\n        const std::vector<HttpMethod> &methods);\n\n    struct SimpleControllerRouterItem\n    {\n        std::shared_ptr<HttpSimpleControllerBinder> binders_[Invalid]{nullptr};\n    };\n\n    struct HttpControllerRouterItem\n    {\n        std::string pathParameterPattern_;\n        std::string pathPattern_;\n        std::regex regex_;\n        std::shared_ptr<HttpControllerBinder> binders_[Invalid]{nullptr};\n    };\n\n    struct WebSocketControllerRouterItem\n    {\n        std::shared_ptr<WebsocketControllerBinder> binders_[Invalid]{nullptr};\n    };\n\n    struct RegExWebSocketControllerRouterItem\n    {\n        std::string pathPattern_;\n        std::regex regex_;\n        std::shared_ptr<WebsocketControllerBinder> binders_[Invalid]{nullptr};\n    };\n\n    std::unordered_map<std::string, SimpleControllerRouterItem> simpleCtrlMap_;\n    std::unordered_map<std::string, HttpControllerRouterItem> ctrlMap_;\n    std::vector<HttpControllerRouterItem> ctrlVector_;  // for regexp path\n    std::unordered_map<std::string, WebSocketControllerRouterItem> wsCtrlMap_;\n    std::vector<RegExWebSocketControllerRouterItem> wsCtrlVector_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpFileImpl.cc",
    "content": "/**\n *\n *  @file HttpFileImpl.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpFileImpl.h\"\n#include \"HttpAppFrameworkImpl.h\"\n#include <drogon/MultiPart.h>\n#include <drogon/utils/Utilities.h>\n#include <fstream>\n#include <iostream>\n#include <algorithm>\n#include <filesystem>\n\nusing namespace drogon;\n\nint HttpFileImpl::save() const noexcept\n{\n    return save(HttpAppFrameworkImpl::instance().getUploadPath());\n}\n\nint HttpFileImpl::save(const std::string &path) const noexcept\n{\n    assert(!path.empty());\n    if (fileName_.empty())\n        return -1;\n    std::filesystem::path fsUploadDir(utils::toNativePath(path));\n\n    if (fsUploadDir.is_absolute())\n    {  // do nothing\n    }\n    else if ((!fsUploadDir.has_parent_path() ||\n              (fsUploadDir.begin()->string() != \".\" &&\n               fsUploadDir.begin()->string() != \"..\")))\n    {\n        fsUploadDir = utils::toNativePath(\n                          HttpAppFrameworkImpl::instance().getUploadPath()) /\n                      fsUploadDir;\n    }\n    else\n    {\n        fsUploadDir = std::filesystem::current_path() / fsUploadDir;\n    }\n\n    fsUploadDir = std::filesystem::weakly_canonical(fsUploadDir);\n\n    if (!std::filesystem::exists(fsUploadDir))\n    {\n        LOG_TRACE << \"create path:\" << fsUploadDir;\n        std::error_code err;\n        std::filesystem::create_directories(fsUploadDir, err);\n        if (err)\n        {\n            LOG_SYSERR;\n            return -1;\n        }\n    }\n\n    std::filesystem::path fsSaveToPath(std::filesystem::weakly_canonical(\n        fsUploadDir / utils::toNativePath(fileName_)));\n    LOG_TRACE << \"save to path:\" << fsSaveToPath;\n    if (!std::equal(fsUploadDir.begin(),\n                    fsUploadDir.end(),\n                    fsSaveToPath.begin()))\n    {\n        LOG_ERROR\n            << \"Attempt writing outside of upload directory detected. Path: \"\n            << fileName_;\n        return -1;\n    }\n\n    return saveTo(fsSaveToPath);\n}\n\nint HttpFileImpl::saveAs(const std::string &fileName) const noexcept\n{\n    assert(!fileName.empty());\n    std::filesystem::path fsFileName(utils::toNativePath(fileName));\n    if (!fsFileName.is_absolute() && (!fsFileName.has_parent_path() ||\n                                      (fsFileName.begin()->string() != \".\" &&\n                                       fsFileName.begin()->string() != \"..\")))\n    {\n        std::filesystem::path fsUploadPath(utils::toNativePath(\n            HttpAppFrameworkImpl::instance().getUploadPath()));\n        fsFileName = fsUploadPath / fsFileName;\n    }\n    if (fsFileName.has_parent_path() &&\n        !std::filesystem::exists(fsFileName.parent_path()))\n    {\n        LOG_TRACE << \"create path:\" << fsFileName.parent_path();\n        std::error_code err;\n        std::filesystem::create_directories(fsFileName.parent_path(), err);\n        if (err)\n        {\n            LOG_SYSERR;\n            return -1;\n        }\n    }\n    return saveTo(fsFileName);\n}\n\nint HttpFileImpl::saveTo(\n    const std::filesystem::path &pathAndFileName) const noexcept\n{\n    LOG_TRACE << \"save uploaded file:\" << pathAndFileName;\n    auto wPath = utils::toNativePath(pathAndFileName.native());\n    std::ofstream file(wPath, std::ios::binary);\n    if (file.is_open())\n    {\n        file.write(fileContent_.data(), fileContent_.size());\n        file.close();\n        return 0;\n    }\n    else\n    {\n        LOG_ERROR << \"save failed!\";\n        return -1;\n    }\n}\n\nstd::string HttpFileImpl::getMd5() const noexcept\n{\n    return utils::getMd5(fileContent_.data(), fileContent_.size());\n}\n\nstd::string HttpFileImpl::getSha256() const noexcept\n{\n    return utils::getSha256(fileContent_.data(), fileContent_.size());\n}\n\nstd::string HttpFileImpl::getSha3() const noexcept\n{\n    return utils::getSha3(fileContent_.data(), fileContent_.size());\n}\n\nconst std::string &HttpFile::getFileName() const noexcept\n{\n    return implPtr_->getFileName();\n}\n\nvoid HttpFile::setFileName(const std::string &fileName) noexcept\n{\n    implPtr_->setFileName(fileName);\n}\n\nstd::string_view HttpFile::getFileExtension() const noexcept\n{\n    return implPtr_->getFileExtension();\n}\n\nFileType HttpFile::getFileType() const noexcept\n{\n    return implPtr_->getFileType();\n}\n\nvoid HttpFile::setFile(const char *data, size_t length) noexcept\n{\n    implPtr_->setFile(data, length);\n}\n\nint HttpFile::save() const noexcept\n{\n    return implPtr_->save();\n}\n\nint HttpFile::save(const std::string &path) const noexcept\n{\n    return implPtr_->save(path);\n}\n\nint HttpFile::saveAs(const std::string &fileName) const noexcept\n{\n    return implPtr_->saveAs(fileName);\n}\n\nsize_t HttpFile::fileLength() const noexcept\n{\n    return implPtr_->fileLength();\n}\n\ndrogon::ContentType HttpFile::getContentType() const noexcept\n{\n    return implPtr_->getContentType();\n}\n\nconst char *HttpFile::fileData() const noexcept\n{\n    return implPtr_->fileData();\n}\n\nstd::string HttpFile::getMd5() const noexcept\n{\n    return implPtr_->getMd5();\n}\n\nconst std::string &HttpFile::getContentTransferEncoding() const noexcept\n{\n    return implPtr_->getContentTransferEncoding();\n}\n\nHttpFile::HttpFile(std::shared_ptr<HttpFileImpl> &&implPtr) noexcept\n    : implPtr_(std::move(implPtr))\n{\n}\n\nconst std::string &HttpFile::getItemName() const noexcept\n{\n    return implPtr_->getItemName();\n}\n"
  },
  {
    "path": "lib/src/HttpFileImpl.h",
    "content": "/**\n *\n *  @file HttpFileImpl.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include \"HttpUtils.h\"\n#include <drogon/HttpRequest.h>\n\n#include <map>\n#include <string>\n#include <vector>\n#include <memory>\n#include <filesystem>\n#include <string_view>\n\nnamespace drogon\n{\nclass HttpFileImpl\n{\n  public:\n    /// Return the file name;\n    const std::string &getFileName() const noexcept\n    {\n        return fileName_;\n    }\n\n    /// Set the file name, usually called by the MultiPartParser parser.\n    void setFileName(const std::string &fileName) noexcept\n    {\n        fileName_ = fileName;\n    }\n\n    void setFileName(std::string &&fileName) noexcept\n    {\n        fileName_ = std::move(fileName);\n    }\n\n    /// Return the file extension;\n    std::string_view getFileExtension() const noexcept\n    {\n        return drogon::getFileExtension(fileName_);\n    }\n\n    /// Set the contents of the file, usually called by the MultiPartParser\n    /// parser.\n    void setFile(const char *data, size_t length) noexcept\n    {\n        fileContent_ = std::string_view{data, length};\n    }\n\n    /// Save the file to the file system.\n    /**\n     * The folder saving the file is app().getUploadPath().\n     * The full path is app().getUploadPath()+\"/\"+this->getFileName()\n     */\n    int save() const noexcept;\n\n    /// Save the file to @param path\n    /**\n     * @param path if the parameter is prefixed with \"/\", \"./\" or \"../\", or is\n     * \".\" or \"..\", the full path is path+\"/\"+this->getFileName(),\n     * otherwise the file is saved as\n     * app().getUploadPath()+\"/\"+path+\"/\"+this->getFileName()\n     */\n    int save(const std::string &path) const noexcept;\n\n    /// Save the file to file system with a new name\n    /**\n     * @param fileName if the parameter isn't prefixed with \"/\", \"./\" or \"../\",\n     * the full path is app().getUploadPath()+\"/\"+filename, otherwise the file\n     * is saved as the filename\n     */\n    int saveAs(const std::string &fileName) const noexcept;\n\n    /// Return the file length.\n    size_t fileLength() const noexcept\n    {\n        return fileContent_.length();\n    }\n\n    const char *fileData() const noexcept\n    {\n        return fileContent_.data();\n    }\n\n    const std::string_view &fileContent() const noexcept\n    {\n        return fileContent_;\n    }\n\n    /// Return the name of the item in multiple parts.\n    const std::string &getItemName() const noexcept\n    {\n        return itemName_;\n    }\n\n    void setItemName(const std::string &itemName) noexcept\n    {\n        itemName_ = itemName;\n    }\n\n    void setItemName(std::string &&itemName) noexcept\n    {\n        itemName_ = std::move(itemName);\n    }\n\n    /// Return the type of file.\n    FileType getFileType() const noexcept\n    {\n        auto ft = drogon::getFileType(contentType_);\n        if ((ft != FT_UNKNOWN) && (ft != FT_CUSTOM))\n            return ft;\n        return parseFileType(getFileExtension());\n    }\n\n    /// Return md5 hash of the file\n    std::string getMd5() const noexcept;\n    // Return sha1 hash of the file\n    std::string getSha256() const noexcept;\n    // Return sha512 hash of the file\n    std::string getSha3() const noexcept;\n    //    int saveTo(const std::string &pathAndFileName) const;\n    int saveTo(const std::filesystem::path &pathAndFileName) const noexcept;\n\n    void setRequest(const HttpRequestPtr &req) noexcept\n    {\n        requestPtr_ = req;\n    }\n\n    drogon::ContentType getContentType() const noexcept\n    {\n        return contentType_;\n    }\n\n    void setContentType(drogon::ContentType contentType) noexcept\n    {\n        contentType_ = contentType;\n    }\n\n    void setContentTransferEncoding(\n        const std::string &contentTransferEncoding) noexcept\n    {\n        transferEncoding_ = contentTransferEncoding;\n    }\n\n    void setContentTransferEncoding(\n        std::string &&contentTransferEncoding) noexcept\n    {\n        transferEncoding_ = std::move(contentTransferEncoding);\n    }\n\n    const std::string &getContentTransferEncoding() const noexcept\n    {\n        return transferEncoding_;\n    }\n\n  private:\n    std::string fileName_;\n    std::string itemName_;\n    std::string transferEncoding_;\n    std::string_view fileContent_;\n    HttpRequestPtr requestPtr_;\n    drogon::ContentType contentType_{drogon::CT_NONE};\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpFileUploadRequest.cc",
    "content": "/**\n *\n *  HttpFileUploadRequest.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpFileUploadRequest.h\"\n#include <drogon/UploadFile.h>\n#include <drogon/utils/Utilities.h>\n\nusing namespace drogon;\n\nHttpFileUploadRequest::HttpFileUploadRequest(\n    const std::vector<UploadFile> &files)\n    : HttpRequestImpl(nullptr),\n      boundary_(utils::genRandomString(32)),\n      files_(files)\n{\n    setMethod(drogon::Post);\n    setVersion(drogon::Version::kHttp11);\n    setContentType(\"multipart/form-data; boundary=\" + boundary_);\n    contentType_ = CT_MULTIPART_FORM_DATA;\n}\n"
  },
  {
    "path": "lib/src/HttpFileUploadRequest.h",
    "content": "/**\n *\n *  HttpFileUploadRequest.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include \"HttpRequestImpl.h\"\n#include <string>\n#include <vector>\n\nnamespace drogon\n{\nclass HttpFileUploadRequest : public HttpRequestImpl\n{\n  public:\n    const std::string &boundary() const\n    {\n        return boundary_;\n    }\n\n    const std::vector<UploadFile> &files() const\n    {\n        return files_;\n    }\n\n    explicit HttpFileUploadRequest(const std::vector<UploadFile> &files);\n\n  private:\n    std::string boundary_;\n    std::vector<UploadFile> files_;\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpMessageBody.h",
    "content": "/**\n *\n *  HttpMessageBody.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <string_view>\n#include <memory>\n#include <string>\n\nnamespace drogon\n{\nclass HttpMessageBody\n{\n  public:\n    enum class BodyType\n    {\n        kNone = 0,\n        kString,\n        kStringView\n    };\n\n    BodyType bodyType()\n    {\n        return type_;\n    }\n\n    virtual const char *data() const\n    {\n        return nullptr;\n    }\n\n    virtual char *data()\n    {\n        return nullptr;\n    }\n\n    virtual size_t length() const\n    {\n        return 0;\n    }\n\n    virtual std::string_view getString() const = 0;\n\n    virtual void append(const char * /*buf*/, size_t /*len*/)\n    {\n    }\n\n    virtual ~HttpMessageBody()\n    {\n    }\n\n  protected:\n    BodyType type_{BodyType::kNone};\n};\n\nclass HttpMessageStringBody : public HttpMessageBody\n{\n  public:\n    HttpMessageStringBody()\n    {\n        type_ = BodyType::kString;\n    }\n\n    HttpMessageStringBody(const std::string &body) : body_(body)\n    {\n        type_ = BodyType::kString;\n    }\n\n    HttpMessageStringBody(std::string &&body) : body_(std::move(body))\n    {\n        type_ = BodyType::kString;\n    }\n\n    const char *data() const override\n    {\n        return body_.data();\n    }\n\n    char *data() override\n    {\n        return const_cast<char *>(body_.data());\n    }\n\n    size_t length() const override\n    {\n        return body_.length();\n    }\n\n    std::string_view getString() const override\n    {\n        return std::string_view{body_.data(), body_.length()};\n    }\n\n    void append(const char *buf, size_t len) override\n    {\n        body_.append(buf, len);\n    }\n\n  private:\n    std::string body_;\n};\n\nclass HttpMessageStringViewBody : public HttpMessageBody\n{\n  public:\n    HttpMessageStringViewBody(const char *buf, size_t len) : body_(buf, len)\n    {\n        type_ = BodyType::kStringView;\n    }\n\n    const char *data() const override\n    {\n        return body_.data();\n    }\n\n    char *data() override\n    {\n        return const_cast<char *>(body_.data());\n    }\n\n    size_t length() const override\n    {\n        return body_.length();\n    }\n\n    std::string_view getString() const override\n    {\n        return body_;\n    }\n\n  private:\n    std::string_view body_;\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpRequestImpl.cc",
    "content": "/**\n *\n *  @file HttpRequestImpl.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpRequestImpl.h\"\n#include \"HttpFileUploadRequest.h\"\n#include \"HttpAppFrameworkImpl.h\"\n\n#include <drogon/utils/Utilities.h>\n#include <fstream>\n#include <iostream>\n#ifndef _WIN32\n#include <unistd.h>\n#endif\n\n#include <zlib.h>\n#ifdef USE_BROTLI\n#include <brotli/decode.h>\n#endif\n\nusing namespace drogon;\n\nvoid HttpRequestImpl::parseJson() const\n{\n    auto input = contentView();\n    if (input.empty())\n        return;\n    if (contentType_ == CT_APPLICATION_JSON ||\n        getHeaderBy(\"content-type\").find(\"application/json\") !=\n            std::string::npos)\n    {\n        static std::once_flag once;\n        static Json::CharReaderBuilder builder;\n        std::call_once(once, []() {\n            builder[\"collectComments\"] = false;\n            builder[\"stackLimit\"] = static_cast<Json::UInt>(\n                drogon::app().getJsonParserStackLimit());\n        });\n        jsonPtr_ = std::make_shared<Json::Value>();\n        JSONCPP_STRING errs;\n        std::unique_ptr<Json::CharReader> reader(builder.newCharReader());\n        if (!reader->parse(input.data(),\n                           input.data() + input.size(),\n                           jsonPtr_.get(),\n                           &errs))\n        {\n            LOG_DEBUG << errs;\n            jsonPtr_.reset();\n            jsonParsingErrorPtr_ =\n                std::make_unique<std::string>(std::move(errs));\n        }\n        else\n        {\n            jsonParsingErrorPtr_.reset();\n        }\n    }\n    else\n    {\n        jsonPtr_.reset();\n        jsonParsingErrorPtr_ =\n            std::make_unique<std::string>(\"content type error\");\n    }\n}\n\nvoid HttpRequestImpl::parseParameters() const\n{\n    auto input = queryView();\n    if (!input.empty())\n    {\n        std::string_view::size_type pos = 0;\n        while ((input[pos] == '?' ||\n                isspace(static_cast<unsigned char>(input[pos]))) &&\n               pos < input.length())\n        {\n            ++pos;\n        }\n        auto value = input.substr(pos);\n        while ((pos = value.find('&')) != std::string_view::npos)\n        {\n            auto coo = value.substr(0, pos);\n            auto epos = coo.find('=');\n            if (epos != std::string_view::npos)\n            {\n                auto key = coo.substr(0, epos);\n                std::string_view::size_type cpos = 0;\n                while (cpos < key.length() &&\n                       isspace(static_cast<unsigned char>(key[cpos])))\n                    ++cpos;\n                key.remove_prefix(cpos);\n                auto pvalue = coo.substr(epos + 1);\n                parameters_[utils::urlDecode(key)] = utils::urlDecode(pvalue);\n            }\n            else\n            {\n                parameters_[utils::urlDecode(coo)];\n            }\n            value.remove_prefix(pos + 1);\n        }\n        if (value.length() > 0)\n        {\n            auto &coo = value;\n            auto epos = coo.find('=');\n            if (epos != std::string_view::npos)\n            {\n                auto key = coo.substr(0, epos);\n                std::string_view::size_type cpos = 0;\n                while (cpos < key.length() &&\n                       isspace(static_cast<unsigned char>(key[cpos])))\n                    ++cpos;\n                key.remove_prefix(cpos);\n                auto pvalue = coo.substr(epos + 1);\n                parameters_[utils::urlDecode(key)] = utils::urlDecode(pvalue);\n            }\n            else\n            {\n                parameters_[utils::urlDecode(coo)];\n            }\n        }\n    }\n\n    input = contentView();\n    if (input.empty())\n        return;\n    std::string type = getHeaderBy(\"content-type\");\n    std::transform(type.begin(), type.end(), type.begin(), [](unsigned char c) {\n        return tolower(c);\n    });\n    if (type.empty() ||\n        type.find(\"application/x-www-form-urlencoded\") != std::string::npos)\n    {\n        std::string_view::size_type pos = 0;\n        while ((input[pos] == '?' ||\n                isspace(static_cast<unsigned char>(input[pos]))) &&\n               pos < input.length())\n        {\n            ++pos;\n        }\n        auto value = input.substr(pos);\n        while ((pos = value.find('&')) != std::string_view::npos)\n        {\n            auto coo = value.substr(0, pos);\n            auto epos = coo.find('=');\n            if (epos != std::string_view::npos)\n            {\n                auto key = coo.substr(0, epos);\n                std::string_view::size_type cpos = 0;\n                while (cpos < key.length() &&\n                       isspace(static_cast<unsigned char>(key[cpos])))\n                    ++cpos;\n                key.remove_prefix(cpos);\n                auto pvalue = coo.substr(epos + 1);\n                parameters_[utils::urlDecode(key)] = utils::urlDecode(pvalue);\n            }\n            else\n            {\n                parameters_[utils::urlDecode(coo)];\n            }\n            value.remove_prefix(pos + 1);\n        }\n        if (value.length() > 0)\n        {\n            auto &coo = value;\n            auto epos = coo.find('=');\n            if (epos != std::string_view::npos)\n            {\n                auto key = coo.substr(0, epos);\n                std::string_view::size_type cpos = 0;\n                while (cpos < key.length() &&\n                       isspace(static_cast<unsigned char>(key[cpos])))\n                    ++cpos;\n                key.remove_prefix(cpos);\n                auto pvalue = coo.substr(epos + 1);\n                parameters_[utils::urlDecode(key)] = utils::urlDecode(pvalue);\n            }\n            else\n            {\n                parameters_[utils::urlDecode(coo)];\n            }\n        }\n    }\n}\n\nvoid HttpRequestImpl::appendToBuffer(trantor::MsgBuffer *output) const\n{\n    switch (method_)\n    {\n        case Get:\n            output->append(\"GET \");\n            break;\n        case Post:\n            output->append(\"POST \");\n            break;\n        case Head:\n            output->append(\"HEAD \");\n            break;\n        case Put:\n            output->append(\"PUT \");\n            break;\n        case Delete:\n            output->append(\"DELETE \");\n            break;\n        case Options:\n            output->append(\"OPTIONS \");\n            break;\n        case Patch:\n            output->append(\"PATCH \");\n            break;\n        default:\n            return;\n    }\n\n    if (!path_.empty())\n    {\n        if (pathEncode_)\n        {\n            output->append(utils::urlEncode(path_));\n        }\n        else\n        {\n            output->append(path_);\n        }\n    }\n    else\n    {\n        output->append(\"/\");\n    }\n\n    std::string content;\n    if (passThrough_ && !query_.empty())\n    {\n        output->append(\"?\");\n        output->append(query_);\n    }\n    if (!passThrough_ && !parameters_.empty() &&\n        contentType_ != CT_MULTIPART_FORM_DATA)\n    {\n        for (auto const &p : parameters_)\n        {\n            content.append(utils::urlEncodeComponent(p.first));\n            content.append(\"=\");\n            content.append(utils::urlEncodeComponent(p.second));\n            content.append(\"&\");\n        }\n        content.resize(content.length() - 1);\n        if (contentType_ != CT_APPLICATION_X_FORM)\n        {\n            auto ret = std::find(output->peek(),\n                                 (const char *)output->beginWrite(),\n                                 '?');\n            if (ret != output->beginWrite())\n            {\n                if (ret != output->beginWrite() - 1)\n                {\n                    output->append(\"&\");\n                }\n            }\n            else\n            {\n                output->append(\"?\");\n            }\n            output->append(content);\n            content.clear();\n        }\n    }\n\n    output->append(\" \");\n    if (version_ == Version::kHttp11)\n    {\n        output->append(\"HTTP/1.1\");\n    }\n    else if (version_ == Version::kHttp10)\n    {\n        output->append(\"HTTP/1.0\");\n    }\n    else\n    {\n        return;\n    }\n    output->append(\"\\r\\n\");\n\n    if (!passThrough_ && contentType_ == CT_MULTIPART_FORM_DATA)\n    {\n        auto mReq = dynamic_cast<const HttpFileUploadRequest *>(this);\n        if (mReq)\n        {\n            for (auto &param : mReq->getParameters())\n            {\n                content.append(\"--\");\n                content.append(mReq->boundary());\n                content.append(\"\\r\\n\");\n                content.append(\"content-disposition: form-data; name=\\\"\");\n                content.append(param.first);\n                content.append(\"\\\"\\r\\n\\r\\n\");\n                content.append(param.second);\n                content.append(\"\\r\\n\");\n            }\n            for (auto &file : mReq->files())\n            {\n                content.append(\"--\");\n                content.append(mReq->boundary());\n                content.append(\"\\r\\n\");\n                content.append(\"content-disposition: form-data; name=\\\"\");\n                content.append(file.itemName());\n                content.append(\"\\\"; filename=\\\"\");\n                content.append(file.fileName());\n                content.append(\"\\\"\");\n                if (file.contentType() != CT_NONE)\n                {\n                    content.append(\"\\r\\n\");\n\n                    auto &type = contentTypeToMime(file.contentType());\n                    content.append(\"content-type: \");\n                    content.append(type.data(), type.length());\n                }\n                content.append(\"\\r\\n\\r\\n\");\n                std::ifstream infile(utils::toNativePath(file.path()),\n                                     std::ifstream::binary);\n                if (!infile)\n                {\n                    LOG_ERROR << file.path() << \" not found\";\n                }\n                else\n                {\n                    std::streambuf *pbuf = infile.rdbuf();\n                    std::streamsize filesize = pbuf->pubseekoff(0, infile.end);\n                    pbuf->pubseekoff(0, infile.beg);  // rewind\n                    std::string str;\n                    str.resize(filesize);\n                    pbuf->sgetn(&str[0], filesize);\n                    content.append(std::move(str));\n                }\n                content.append(\"\\r\\n\");\n            }\n            content.append(\"--\");\n            content.append(mReq->boundary());\n            content.append(\"--\");\n        }\n    }\n    assert(!(!content.empty() && !content_.empty()));\n    if (!passThrough_)\n    {\n        if (!content.empty() || !content_.empty())\n        {\n            char buf[64];\n            auto len = snprintf(\n                buf,\n                sizeof(buf),\n                contentLengthFormatString<decltype(content.length())>(),\n                content.length() + content_.length());\n            output->append(buf, len);\n            if (contentTypeString_.empty())\n            {\n                auto &type = contentTypeToMime(contentType_);\n                output->append(\"content-type: \");\n                output->append(type.data(), type.length());\n                output->append(\"\\r\\n\");\n            }\n        }\n        else if (method_ == Post || method_ == Put || method_ == Options ||\n                 method_ == Patch)\n        {\n            output->append(\"content-length: 0\\r\\n\", 19);\n        }\n        if (!contentTypeString_.empty())\n        {\n            output->append(\"content-type: \");\n            output->append(contentTypeString_);\n            output->append(\"\\r\\n\");\n        }\n    }\n    for (auto it = headers_.begin(); it != headers_.end(); ++it)\n    {\n        output->append(it->first);\n        output->append(\": \");\n        output->append(it->second);\n        output->append(\"\\r\\n\");\n    }\n    if (cookies_.size() > 0)\n    {\n        output->append(\"cookie: \");\n        for (auto it = cookies_.begin(); it != cookies_.end(); ++it)\n        {\n            output->append(it->first);\n            output->append(\"=\");\n            output->append(it->second);\n            output->append(\";\");\n        }\n        output->unwrite(1);  // delete last ';'\n        output->append(\"\\r\\n\");\n    }\n\n    output->append(\"\\r\\n\");\n    if (!content.empty())\n        output->append(content);\n    if (!content_.empty())\n        output->append(content_);\n}\n\nvoid HttpRequestImpl::addHeader(const char *start,\n                                const char *colon,\n                                const char *end)\n{\n    std::string field(start, colon);\n    // Field name is case-insensitive.so we transform it to lower;(rfc2616-4.2)\n    std::transform(field.begin(),\n                   field.end(),\n                   field.begin(),\n                   [](unsigned char c) { return tolower(c); });\n    ++colon;\n    while (colon < end && isspace(static_cast<unsigned char>(*colon)))\n    {\n        ++colon;\n    }\n    std::string value(colon, end);\n    while (!value.empty() &&\n           isspace(static_cast<unsigned char>(value[value.size() - 1])))\n    {\n        value.resize(value.size() - 1);\n    }\n    if (field.length() == 6 && field == \"cookie\")\n    {\n        LOG_TRACE << \"cookies!!!:\" << value;\n        std::string::size_type pos;\n        while ((pos = value.find(';')) != std::string::npos)\n        {\n            std::string coo = value.substr(0, pos);\n            auto epos = coo.find('=');\n            if (epos != std::string::npos)\n            {\n                std::string cookie_name = coo.substr(0, epos);\n                std::string::size_type cpos = 0;\n                while (cpos < cookie_name.length() &&\n                       isspace(static_cast<unsigned char>(cookie_name[cpos])))\n                    ++cpos;\n                cookie_name = cookie_name.substr(cpos);\n                std::string cookie_value = coo.substr(epos + 1);\n                cpos = 0;\n                while (cpos < cookie_value.length() &&\n                       isspace(static_cast<unsigned char>(cookie_value[cpos])))\n                    ++cpos;\n                cookie_value = cookie_value.substr(cpos);\n                cookies_[std::move(cookie_name)] = std::move(cookie_value);\n            }\n            value = value.substr(pos + 1);\n        }\n        if (value.length() > 0)\n        {\n            std::string &coo = value;\n            auto epos = coo.find('=');\n            if (epos != std::string::npos)\n            {\n                std::string cookie_name = coo.substr(0, epos);\n                std::string::size_type cpos = 0;\n                while (cpos < cookie_name.length() &&\n                       isspace(static_cast<unsigned char>(cookie_name[cpos])))\n                    ++cpos;\n                cookie_name = cookie_name.substr(cpos);\n                std::string cookie_value = coo.substr(epos + 1);\n                cpos = 0;\n                while (cpos < cookie_value.length() &&\n                       isspace(static_cast<unsigned char>(cookie_value[cpos])))\n                    ++cpos;\n                cookie_value = cookie_value.substr(cpos);\n                cookies_[std::move(cookie_name)] = std::move(cookie_value);\n            }\n        }\n    }\n    else\n    {\n        switch (field.length())\n        {\n            case 6:\n                if (field == \"expect\")\n                {\n                    expectPtr_ = std::make_unique<std::string>(value);\n                }\n                break;\n            case 10:\n            {\n                if (field == \"connection\")\n                {\n                    if (version_ == Version::kHttp11)\n                    {\n                        if (value.length() == 5 && value == \"close\")\n                            keepAlive_ = false;\n                    }\n                    else if (value.length() == 10 &&\n                             (value == \"Keep-Alive\" || value == \"keep-alive\"))\n                    {\n                        keepAlive_ = true;\n                    }\n                }\n            }\n            break;\n\n            default:\n                break;\n        }\n        headers_.emplace(std::move(field), std::move(value));\n    }\n}\n\nHttpRequestPtr HttpRequest::newHttpRequest()\n{\n    auto req = std::make_shared<HttpRequestImpl>(nullptr);\n    req->setMethod(drogon::Get);\n    req->setVersion(drogon::Version::kHttp11);\n    return req;\n}\n\nHttpRequestPtr HttpRequest::newHttpFormPostRequest()\n{\n    auto req = std::make_shared<HttpRequestImpl>(nullptr);\n    req->setMethod(drogon::Post);\n    req->setVersion(drogon::Version::kHttp11);\n    req->contentType_ = CT_APPLICATION_X_FORM;\n    req->flagForParsingContentType_ = true;\n    return req;\n}\n\nHttpRequestPtr HttpRequest::newHttpJsonRequest(const Json::Value &data)\n{\n    static std::once_flag once;\n    static Json::StreamWriterBuilder builder;\n    std::call_once(once, []() {\n        builder[\"commentStyle\"] = \"None\";\n        builder[\"indentation\"] = \"\";\n        if (!app().isUnicodeEscapingUsedInJson())\n        {\n            builder[\"emitUTF8\"] = true;\n        }\n        auto &precision = app().getFloatPrecisionInJson();\n        if (precision.first != 0)\n        {\n            builder[\"precision\"] = precision.first;\n            builder[\"precisionType\"] = precision.second;\n        }\n    });\n    auto req = std::make_shared<HttpRequestImpl>(nullptr);\n    req->setMethod(drogon::Get);\n    req->setVersion(drogon::Version::kHttp11);\n    req->contentType_ = CT_APPLICATION_JSON;\n    req->setContent(writeString(builder, data));\n    req->flagForParsingContentType_ = true;\n    return req;\n}\n\nHttpRequestPtr HttpRequest::newFileUploadRequest(\n    const std::vector<UploadFile> &files)\n{\n    return std::make_shared<HttpFileUploadRequest>(files);\n}\n\nvoid HttpRequestImpl::swap(HttpRequestImpl &that) noexcept\n{\n    using std::swap;\n    swap(method_, that.method_);\n    swap(version_, that.version_);\n    swap(flagForParsingJson_, that.flagForParsingJson_);\n    swap(flagForParsingParameters_, that.flagForParsingParameters_);\n    swap(matchedPathPattern_, that.matchedPathPattern_);\n    swap(path_, that.path_);\n    swap(originalPath_, that.originalPath_);\n    swap(pathEncode_, that.pathEncode_);\n    swap(query_, that.query_);\n    swap(headers_, that.headers_);\n    swap(cookies_, that.cookies_);\n    swap(contentLengthHeaderValue_, that.contentLengthHeaderValue_);\n    swap(realContentLength_, that.realContentLength_);\n    swap(parameters_, that.parameters_);\n    swap(jsonPtr_, that.jsonPtr_);\n    swap(sessionPtr_, that.sessionPtr_);\n    swap(attributesPtr_, that.attributesPtr_);\n    swap(cacheFilePtr_, that.cacheFilePtr_);\n    swap(peer_, that.peer_);\n    swap(local_, that.local_);\n    swap(creationDate_, that.creationDate_);\n    swap(content_, that.content_);\n    swap(expectPtr_, that.expectPtr_);\n    swap(contentType_, that.contentType_);\n    swap(contentTypeString_, that.contentTypeString_);\n    swap(keepAlive_, that.keepAlive_);\n    swap(loop_, that.loop_);\n    swap(flagForParsingContentType_, that.flagForParsingContentType_);\n    swap(jsonParsingErrorPtr_, that.jsonParsingErrorPtr_);\n    swap(routingParams_, that.routingParams_);\n    // stream\n    swap(streamStatus_, that.streamStatus_);\n    swap(streamReaderPtr_, that.streamReaderPtr_);\n    swap(streamFinishCb_, that.streamFinishCb_);\n    swap(streamExceptionPtr_, that.streamExceptionPtr_);\n    swap(startProcessing_, that.startProcessing_);\n    swap(connPtr_, that.connPtr_);\n}\n\nconst char *HttpRequestImpl::versionString() const\n{\n    const char *result = \"UNKNOWN\";\n    switch (version_)\n    {\n        case Version::kHttp10:\n            result = \"HTTP/1.0\";\n            break;\n\n        case Version::kHttp11:\n            result = \"HTTP/1.1\";\n            break;\n\n        default:\n            break;\n    }\n    return result;\n}\n\nconst char *HttpRequestImpl::methodString() const\n{\n    const char *result = \"UNKNOWN\";\n    switch (method_)\n    {\n        case Get:\n            result = \"GET\";\n            break;\n        case Post:\n            result = \"POST\";\n            break;\n        case Head:\n            result = \"HEAD\";\n            break;\n        case Put:\n            result = \"PUT\";\n            break;\n        case Delete:\n            result = \"DELETE\";\n            break;\n        case Options:\n            result = \"OPTIONS\";\n            break;\n        case Patch:\n            result = \"PATCH\";\n            break;\n        default:\n            break;\n    }\n    return result;\n}\n\nbool HttpRequestImpl::setMethod(const char *start, const char *end)\n{\n    assert(method_ == Invalid);\n    std::string_view m(start, end - start);\n    switch (m.length())\n    {\n        case 3:\n            if (m == \"GET\")\n            {\n                method_ = Get;\n            }\n            else if (m == \"PUT\")\n            {\n                method_ = Put;\n            }\n            else\n            {\n                method_ = Invalid;\n            }\n            break;\n        case 4:\n            if (m == \"POST\")\n            {\n                method_ = Post;\n            }\n            else if (m == \"HEAD\")\n            {\n                method_ = Head;\n            }\n            else\n            {\n                method_ = Invalid;\n            }\n            break;\n        case 5:\n            if (m == \"PATCH\")\n            {\n                method_ = Patch;\n            }\n            else\n            {\n                method_ = Invalid;\n            }\n            break;\n        case 6:\n            if (m == \"DELETE\")\n            {\n                method_ = Delete;\n            }\n            else\n            {\n                method_ = Invalid;\n            }\n            break;\n        case 7:\n            if (m == \"OPTIONS\")\n            {\n                method_ = Options;\n            }\n            else\n            {\n                method_ = Invalid;\n            }\n            break;\n        default:\n            method_ = Invalid;\n            break;\n    }\n\n    // if (method_ != Invalid)\n    // {\n    //     content_ = \"\";\n    //     query_ = \"\";\n    //     cookies_.clear();\n    //     parameters_.clear();\n    //     headers_.clear();\n    // }\n    return method_ != Invalid;\n}\n\nHttpRequestImpl::~HttpRequestImpl()\n{\n}\n\nvoid HttpRequestImpl::reserveBodySize(size_t length)\n{\n    assert(loop_->isInLoopThread());\n    if (cacheFilePtr_)\n    {\n        return;\n    }\n    if (length <= HttpAppFrameworkImpl::instance().getClientMaxMemoryBodySize())\n    {\n        content_.reserve(length);\n    }\n    else\n    {\n        // Store data of body to a temporary file\n        createTmpFile();\n    }\n}\n\nvoid HttpRequestImpl::appendToBody(const char *data, size_t length)\n{\n    assert(loop_->isInLoopThread());\n    realContentLength_ += length;\n    if (streamReaderPtr_)\n    {\n        assert(streamStatus_ == ReqStreamStatus::Open);\n        streamReaderPtr_->onStreamData(data, length);\n    }\n    else if (cacheFilePtr_)\n    {\n        cacheFilePtr_->append(data, length);\n    }\n    else\n    {\n        if (content_.length() + length <=\n            HttpAppFrameworkImpl::instance().getClientMaxMemoryBodySize())\n        {\n            content_.append(data, length);\n        }\n        else\n        {\n            createTmpFile();\n            cacheFilePtr_->append(content_);\n            cacheFilePtr_->append(data, length);\n            content_.clear();\n        }\n    }\n}\n\nvoid HttpRequestImpl::createTmpFile()\n{\n    auto tmpfile = HttpAppFrameworkImpl::instance().getUploadPath();\n    auto fileName = utils::getUuid(false);\n    tmpfile.append(\"/tmp/\")\n        .append(1, fileName[0])\n        .append(1, fileName[1])\n        .append(\"/\")\n        .append(fileName);\n    cacheFilePtr_ = std::make_unique<CacheFile>(tmpfile);\n}\n\nvoid HttpRequestImpl::setContentTypeString(const char *typeString,\n                                           size_t typeStringLength)\n{\n    std::string sv(typeString, typeStringLength);\n    auto contentType = parseContentType(sv);\n    if (contentType == CT_NONE)\n        contentType = CT_CUSTOM;\n    contentType_ = contentType;\n    contentTypeString_ = std::string(sv);\n    flagForParsingContentType_ = true;\n}\n\nStreamDecompressStatus HttpRequestImpl::decompressBody()\n{\n    auto &contentEncoding = getHeaderBy(\"content-encoding\");\n    if (contentEncoding.empty() || contentEncoding == \"identity\")\n    {\n        removeHeaderBy(\"content-encoding\");\n        return StreamDecompressStatus::Ok;\n    }\n#ifdef USE_BROTLI\n    else if (contentEncoding == \"br\")\n    {\n        removeHeaderBy(\"content-encoding\");\n        return decompressBodyBrotli();\n    }\n#endif\n    else if (contentEncoding == \"gzip\")\n    {\n        removeHeaderBy(\"content-encoding\");\n        return decompressBodyGzip();\n    }\n    return StreamDecompressStatus::NotSupported;\n}\n\n#ifdef USE_BROTLI\nStreamDecompressStatus HttpRequestImpl::decompressBodyBrotli() noexcept\n{\n    // Workaround for Windows min and max are macros\n    auto minVal = [](size_t a, size_t b) { return a < b ? a : b; };\n    std::unique_ptr<CacheFile> cacheFileHolder;\n    std::string contentHolder;\n    std::string_view compressed;\n    if (cacheFilePtr_)\n    {\n        cacheFileHolder = std::move(cacheFilePtr_);\n        compressed = cacheFileHolder->getStringView();\n    }\n    else\n    {\n        contentHolder = std::move(content_);\n        compressed = contentHolder;\n    }\n\n    setBody(\"\");\n    const size_t maxBodySize =\n        HttpAppFrameworkImpl::instance().getClientMaxBodySize();\n    const size_t maxMemorySize =\n        HttpAppFrameworkImpl::instance().getClientMaxMemoryBodySize();\n\n    size_t availableIn = compressed.size();\n    auto nextIn = (const uint8_t *)(compressed.data());\n    auto decompressed = std::string(minVal(maxMemorySize, availableIn * 3), 0);\n    auto nextOut = (uint8_t *)(decompressed.data());\n    size_t totalOut{0};\n    auto s = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);\n    size_t lastOut = 0;\n    StreamDecompressStatus status = StreamDecompressStatus::Ok;\n    while (true)\n    {\n        uint8_t *outPtr = (uint8_t *)decompressed.data();\n        size_t availableOut = decompressed.size();\n        auto result = BrotliDecoderDecompressStream(\n            s, &availableIn, &nextIn, &availableOut, &outPtr, &totalOut);\n        size_t outSize = totalOut - lastOut;\n        lastOut = totalOut;\n\n        if (totalOut > maxBodySize)\n        {\n            setBody(\"\");\n            status = StreamDecompressStatus::TooLarge;\n            break;\n        }\n\n        if (result == BROTLI_DECODER_RESULT_SUCCESS)\n        {\n            appendToBody(decompressed.data(), outSize);\n            break;\n        }\n        else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT)\n        {\n            appendToBody(decompressed.data(), outSize);\n            size_t currentSize = decompressed.size();\n            decompressed.clear();\n            decompressed.resize(minVal(currentSize * 2, maxMemorySize));\n        }\n        else\n        {\n            setBody(\"\");\n            status = StreamDecompressStatus::DecompressError;\n            break;\n        }\n    }\n    BrotliDecoderDestroyInstance(s);\n    return StreamDecompressStatus::Ok;\n}\n#endif\n\nStreamDecompressStatus HttpRequestImpl::decompressBodyGzip() noexcept\n{\n    // Workaround for Windows min and max are macros\n    auto minVal = [](size_t a, size_t b) { return a < b ? a : b; };\n    std::unique_ptr<CacheFile> cacheFileHolder;\n    std::string contentHolder;\n    std::string_view compressed;\n    if (cacheFilePtr_)\n    {\n        cacheFileHolder = std::move(cacheFilePtr_);\n        compressed = cacheFileHolder->getStringView();\n    }\n    else\n    {\n        contentHolder = std::move(content_);\n        compressed = contentHolder;\n    }\n\n    z_stream strm = {nullptr,\n                     0,\n                     0,\n                     nullptr,\n                     0,\n                     0,\n                     nullptr,\n                     nullptr,\n                     nullptr,\n                     nullptr,\n                     nullptr,\n                     0,\n                     0,\n                     0};\n    strm.next_in = (Bytef *)compressed.data();\n    strm.avail_in = static_cast<uInt>(compressed.size());\n    strm.total_out = 0;\n    strm.zalloc = Z_NULL;\n    strm.zfree = Z_NULL;\n    setBody(\"\");\n    const size_t maxBodySize =\n        HttpAppFrameworkImpl::instance().getClientMaxBodySize();\n    const size_t maxMemorySize =\n        HttpAppFrameworkImpl::instance().getClientMaxMemoryBodySize();\n    auto decompressed =\n        std::string(minVal(compressed.size() * 2, maxMemorySize), 0);\n    strm.next_out = (Bytef *)decompressed.data();\n    strm.avail_out = static_cast<uInt>(decompressed.size());\n    size_t lastOut = 0;\n    if (inflateInit2(&strm, (15 + 32)) != Z_OK)\n    {\n        return StreamDecompressStatus::DecompressError;\n    }\n\n    StreamDecompressStatus status = StreamDecompressStatus::Ok;\n    while (true)\n    {\n        // Inflate another chunk.\n        int decompressStatus = inflate(&strm, Z_SYNC_FLUSH);\n\n        if (strm.total_out > maxBodySize)\n        {\n            setBody(\"\");\n            status = StreamDecompressStatus::TooLarge;\n            break;\n        }\n\n        size_t outSize = strm.total_out - lastOut;\n        lastOut = strm.total_out;\n        if (decompressStatus == Z_STREAM_END)\n        {\n            appendToBody(decompressed.data(), outSize);\n            break;\n        }\n        else if (decompressStatus != Z_OK)\n        {\n            setBody(\"\");\n            status = StreamDecompressStatus::DecompressError;\n            break;\n        }\n        else\n        {\n            appendToBody(decompressed.data(), outSize);\n            size_t currentSize = decompressed.size();\n            decompressed.clear();\n            decompressed.resize(minVal(currentSize * 2, maxMemorySize));\n            strm.next_out = (Bytef *)decompressed.data();\n            strm.avail_out = static_cast<uInt>(decompressed.size());\n        }\n    }\n    if (inflateEnd(&strm) != Z_OK)\n    {\n        setBody(\"\");\n        if (status == StreamDecompressStatus::Ok)\n            status = StreamDecompressStatus::DecompressError;\n        return status;\n    }\n    return status;\n}\n\nvoid HttpRequestImpl::setStreamReader(RequestStreamReaderPtr reader)\n{\n    assert(loop_->isInLoopThread());\n    assert(!streamReaderPtr_);\n    assert(streamStatus_ > ReqStreamStatus::None);\n\n    if (streamExceptionPtr_)\n    {\n        assert(streamStatus_ == ReqStreamStatus::Error);\n        reader->onStreamFinish(std::move(streamExceptionPtr_));\n        streamExceptionPtr_ = nullptr;\n        return;\n    }\n\n    // Consume already received body\n    if (cacheFilePtr_)\n    {\n        auto bodyPieceView = cacheFilePtr_->getStringView();\n        if (!bodyPieceView.empty())\n            reader->onStreamData(bodyPieceView.data(), bodyPieceView.length());\n        cacheFilePtr_.reset();\n    }\n    else if (!content_.empty())\n    {\n        reader->onStreamData(content_.data(), content_.length());\n        content_.clear();\n    }\n    if (streamStatus_ == ReqStreamStatus::Finish)\n    {\n        reader->onStreamFinish({});\n    }\n    else\n    {\n        streamReaderPtr_ = std::move(reader);\n    }\n}\n\nvoid HttpRequestImpl::streamStart()\n{\n    assert(streamStatus_ == ReqStreamStatus::None);\n    streamStatus_ = ReqStreamStatus::Open;\n}\n\nvoid HttpRequestImpl::streamFinish()\n{\n    assert(loop_->isInLoopThread());\n    assert(streamStatus_ == ReqStreamStatus::Open);\n    streamStatus_ = ReqStreamStatus::Finish;\n    if (streamFinishCb_)\n    {\n        auto cb = std::move(streamFinishCb_);\n        streamFinishCb_ = nullptr;\n        cb();\n    }\n    if (streamReaderPtr_)\n    {\n        streamReaderPtr_->onStreamFinish({});\n        streamReaderPtr_ = nullptr;\n    }\n}\n\nvoid HttpRequestImpl::streamError(std::exception_ptr ex)\n{\n    // TODO: can we be sure that streamError() only be called once?\n    // If not, we could allow it to be called multiple times, and\n    // only handle the first one.\n    assert(loop_->isInLoopThread());\n    assert(streamStatus_ == ReqStreamStatus::Open);\n    streamStatus_ = ReqStreamStatus::Error;\n    if (streamReaderPtr_)\n    {\n        streamReaderPtr_->onStreamFinish(std::move(ex));\n        streamReaderPtr_ = nullptr;\n    }\n    else\n    {\n        streamExceptionPtr_ = std::move(ex);\n    }\n\n    if (streamFinishCb_)\n    {\n        auto cb = std::move(streamFinishCb_);\n        streamFinishCb_ = nullptr;\n        cb();\n    }\n}\n\nvoid HttpRequestImpl::waitForStreamFinish(std::function<void()> &&cb)\n{\n    assert(loop_->isInLoopThread());\n    assert(streamStatus_ > ReqStreamStatus::None);\n\n    if (streamStatus_ <= ReqStreamStatus::Open)\n    {\n        assert(!streamFinishCb_);  // should only be called once\n        streamFinishCb_ = std::move(cb);\n    }\n    else\n    {\n        cb();\n    }\n}\n\nvoid HttpRequestImpl::quitStreamMode()\n{\n    assert(loop_->isInLoopThread());\n    assert(streamStatus_ >= ReqStreamStatus::Finish);\n    assert(!streamReaderPtr_);\n    streamStatus_ = ReqStreamStatus::None;\n}\n"
  },
  {
    "path": "lib/src/HttpRequestImpl.h",
    "content": "/**\n *\n *  @file HttpRequestImpl.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"HttpUtils.h\"\n#include \"CacheFile.h\"\n#include \"impl_forwards.h\"\n#include <drogon/utils/Utilities.h>\n#include <drogon/HttpRequest.h>\n#include <drogon/RequestStream.h>\n#include <drogon/utils/Utilities.h>\n#include <trantor/net/EventLoop.h>\n#include <trantor/net/InetAddress.h>\n#include <trantor/net/Certificate.h>\n#include <trantor/utils/Logger.h>\n#include <trantor/utils/MsgBuffer.h>\n#include <trantor/utils/NonCopyable.h>\n#include <trantor/net/TcpConnection.h>\n#include <algorithm>\n#include <functional>\n#include <memory>\n#include <string>\n#include <future>\n#include <unordered_map>\n#include <assert.h>\n#include <stdio.h>\n\nnamespace drogon\n{\nenum class StreamDecompressStatus\n{\n    TooLarge,\n    DecompressError,\n    NotSupported,\n    Ok\n};\n\nenum class ReqStreamStatus\n{\n    None = 0,\n    Open = 1,\n    Finish = 2,\n    Error = 3\n};\n\nclass HttpRequestImpl : public HttpRequest\n{\n  public:\n    friend class HttpRequestParser;\n\n    explicit HttpRequestImpl(trantor::EventLoop *loop)\n        : creationDate_(trantor::Date::now()), loop_(loop)\n    {\n    }\n\n    void reset()\n    {\n        method_ = Invalid;\n        previousMethod_ = Invalid;\n        version_ = Version::kUnknown;\n        flagForParsingJson_ = false;\n        headers_.clear();\n        cookies_.clear();\n        contentLengthHeaderValue_.reset();\n        realContentLength_ = 0;\n        flagForParsingParameters_ = false;\n        path_.clear();\n        originalPath_.clear();\n        pathEncode_ = true;\n        matchedPathPattern_ = \"\";\n        query_.clear();\n        parameters_.clear();\n        jsonPtr_.reset();\n        sessionPtr_.reset();\n        attributesPtr_.reset();\n        cacheFilePtr_.reset();\n        expectPtr_.reset();\n        content_.clear();\n        contentType_ = CT_TEXT_PLAIN;\n        flagForParsingContentType_ = false;\n        contentTypeString_.clear();\n        keepAlive_ = true;\n        jsonParsingErrorPtr_.reset();\n        peerCertificate_.reset();\n        routingParams_.clear();\n        // stream\n        streamStatus_ = ReqStreamStatus::None;\n        streamReaderPtr_.reset();\n        streamFinishCb_ = nullptr;\n        streamExceptionPtr_ = nullptr;\n        startProcessing_ = false;\n        connPtr_.reset();\n    }\n\n    trantor::EventLoop *getLoop()\n    {\n        return loop_;\n    }\n\n    void setVersion(Version v)\n    {\n        version_ = v;\n        if (version_ == Version::kHttp10)\n        {\n            keepAlive_ = false;\n        }\n    }\n\n    Version version() const override\n    {\n        return version_;\n    }\n\n    const char *versionString() const override;\n\n    bool setMethod(const char *start, const char *end);\n\n    void setSecure(bool secure)\n    {\n        isOnSecureConnection_ = secure;\n    }\n\n    void setMethod(const HttpMethod method) override\n    {\n        previousMethod_ = method_;\n        method_ = method;\n        return;\n    }\n\n    HttpMethod method() const override\n    {\n        return method_;\n    }\n\n    bool isHead() const override\n    {\n        return (method_ == HttpMethod::Head) ||\n               ((method_ == HttpMethod::Get) &&\n                (previousMethod_ == HttpMethod::Head));\n    }\n\n    const char *methodString() const override;\n\n    void setPath(const char *start, const char *end)\n    {\n        if (utils::needUrlDecoding(start, end))\n        {\n            originalPath_.append(start, end);\n            path_ = utils::urlDecode(start, end);\n        }\n        else\n        {\n            path_.append(start, end);\n        }\n    }\n\n    const std::vector<std::string> &getRoutingParameters() const override\n    {\n        return routingParams_;\n    }\n\n    void setRoutingParameters(std::vector<std::string> &&params) override\n    {\n        routingParams_ = std::move(params);\n    }\n\n    void setPath(const std::string &path) override\n    {\n        path_ = path;\n    }\n\n    void setPath(std::string &&path) override\n    {\n        path_ = std::move(path);\n    }\n\n    void setPathEncode(bool pathEncode) override\n    {\n        pathEncode_ = pathEncode;\n    }\n\n    const SafeStringMap<std::string> &parameters() const override\n    {\n        parseParametersOnce();\n        return parameters_;\n    }\n\n    const std::string &getParameter(const std::string &key) const override\n    {\n        static const std::string defaultVal;\n        parseParametersOnce();\n        auto iter = parameters_.find(key);\n        if (iter != parameters_.end())\n            return iter->second;\n        return defaultVal;\n    }\n\n    const std::string &path() const override\n    {\n        return path_;\n    }\n\n    const std::string &getOriginalPath() const override\n    {\n        return originalPath_.empty() ? path_ : originalPath_;\n    }\n\n    void setQuery(const char *start, const char *end)\n    {\n        query_.assign(start, end);\n    }\n\n    void setQuery(const std::string &query)\n    {\n        query_ = query;\n    }\n\n    std::string_view bodyView() const\n    {\n        if (isStreamMode())\n        {\n            return emptySv_;\n        }\n        if (cacheFilePtr_)\n        {\n            return cacheFilePtr_->getStringView();\n        }\n        return content_;\n    }\n\n    const char *bodyData() const override\n    {\n        if (isStreamMode())\n        {\n            return emptySv_.data();\n        }\n        if (cacheFilePtr_)\n        {\n            return cacheFilePtr_->getStringView().data();\n        }\n        return content_.data();\n    }\n\n    size_t bodyLength() const override\n    {\n        if (isStreamMode())\n        {\n            return emptySv_.length();\n        }\n        if (cacheFilePtr_)\n        {\n            return cacheFilePtr_->getStringView().length();\n        }\n        return content_.length();\n    }\n\n    void appendToBody(const char *data, size_t length);\n\n    void reserveBodySize(size_t length);\n\n    std::string_view queryView() const\n    {\n        return query_;\n    }\n\n    std::string_view contentView() const\n    {\n        if (isStreamMode())\n        {\n            return emptySv_;\n        }\n        if (cacheFilePtr_)\n            return cacheFilePtr_->getStringView();\n        return content_;\n    }\n\n    const std::string &query() const override\n    {\n        return query_;\n    }\n\n    const trantor::InetAddress &peerAddr() const override\n    {\n        return peer_;\n    }\n\n    const trantor::InetAddress &localAddr() const override\n    {\n        return local_;\n    }\n\n    const trantor::Date &creationDate() const override\n    {\n        return creationDate_;\n    }\n\n    const trantor::CertificatePtr &peerCertificate() const override\n    {\n        return peerCertificate_;\n    }\n\n    void setCreationDate(const trantor::Date &date)\n    {\n        creationDate_ = date;\n    }\n\n    void setPeerAddr(const trantor::InetAddress &peer)\n    {\n        peer_ = peer;\n    }\n\n    void setLocalAddr(const trantor::InetAddress &local)\n    {\n        local_ = local;\n    }\n\n    void setPeerCertificate(const trantor::CertificatePtr &cert)\n    {\n        peerCertificate_ = cert;\n    }\n\n    void setConnectionPtr(const std::shared_ptr<trantor::TcpConnection> &ptr)\n    {\n        connPtr_ = ptr;\n    }\n\n    void addHeader(const char *start, const char *colon, const char *end);\n\n    void removeHeader(std::string key) override\n    {\n        transform(key.begin(), key.end(), key.begin(), [](unsigned char c) {\n            return tolower(c);\n        });\n        removeHeaderBy(key);\n    }\n\n    void removeHeaderBy(const std::string &lowerKey)\n    {\n        headers_.erase(lowerKey);\n    }\n\n    const std::string &getHeader(std::string field) const override\n    {\n        std::transform(field.begin(),\n                       field.end(),\n                       field.begin(),\n                       [](unsigned char c) { return tolower(c); });\n        return getHeaderBy(field);\n    }\n\n    const std::string &getHeaderBy(const std::string &lowerField) const\n    {\n        static const std::string defaultVal;\n        auto it = headers_.find(lowerField);\n        if (it != headers_.end())\n        {\n            return it->second;\n        }\n        return defaultVal;\n    }\n\n    const std::string &getCookie(const std::string &field) const override\n    {\n        static const std::string defaultVal;\n        auto it = cookies_.find(field);\n        if (it != cookies_.end())\n        {\n            return it->second;\n        }\n        return defaultVal;\n    }\n\n    const SafeStringMap<std::string> &headers() const override\n    {\n        return headers_;\n    }\n\n    const SafeStringMap<std::string> &cookies() const override\n    {\n        return cookies_;\n    }\n\n    std::optional<size_t> getContentLengthHeaderValue() const\n    {\n        return contentLengthHeaderValue_;\n    }\n\n    size_t realContentLength() const override\n    {\n        return realContentLength_;\n    }\n\n    void setParameter(const std::string &key, const std::string &value) override\n    {\n        flagForParsingParameters_ = true;\n        parameters_[key] = value;\n    }\n\n    const std::string &getContent() const\n    {\n        return content_;\n    }\n\n    void swap(HttpRequestImpl &that) noexcept;\n\n    void setContent(const std::string &content)\n    {\n        content_ = content;\n    }\n\n    void setBody(const std::string &body) override\n    {\n        content_ = body;\n    }\n\n    void setBody(std::string &&body) override\n    {\n        content_ = std::move(body);\n    }\n\n    void addHeader(std::string field, const std::string &value) override\n    {\n        transform(field.begin(),\n                  field.end(),\n                  field.begin(),\n                  [](unsigned char c) { return tolower(c); });\n        headers_[std::move(field)] = value;\n    }\n\n    void addHeader(std::string field, std::string &&value) override\n    {\n        transform(field.begin(),\n                  field.end(),\n                  field.begin(),\n                  [](unsigned char c) { return tolower(c); });\n        headers_[std::move(field)] = std::move(value);\n    }\n\n    void addCookie(std::string key, std::string value) override\n    {\n        cookies_[std::move(key)] = std::move(value);\n    }\n\n    void setPassThrough(bool flag) override\n    {\n        passThrough_ = flag;\n    }\n\n    bool passThrough() const\n    {\n        return passThrough_;\n    }\n\n    void appendToBuffer(trantor::MsgBuffer *output) const;\n\n    const SessionPtr &session() const override\n    {\n        return sessionPtr_;\n    }\n\n    void setSession(const SessionPtr &session)\n    {\n        sessionPtr_ = session;\n    }\n\n    const AttributesPtr &attributes() const override\n    {\n        if (!attributesPtr_)\n        {\n            attributesPtr_ = std::make_shared<Attributes>();\n        }\n        return attributesPtr_;\n    }\n\n    const std::shared_ptr<Json::Value> &jsonObject() const override\n    {\n        // Not multi-thread safe but good, because we basically call this\n        // function in a single thread\n        if (!flagForParsingJson_)\n        {\n            flagForParsingJson_ = true;\n            parseJson();\n        }\n        return jsonPtr_;\n    }\n\n    void setCustomContentTypeString(const std::string &type) override\n    {\n        contentType_ = CT_NONE;\n        flagForParsingContentType_ = true;\n        bool haveHeader = type.find(\"content-type: \") == 0;\n        bool haveCRLF = type.rfind(\"\\r\\n\") == type.size() - 2;\n\n        size_t endOffset = 0;\n        if (haveHeader)\n            endOffset += 14;\n        if (haveCRLF)\n            endOffset += 2;\n        contentTypeString_ = std::string(type.begin() + (haveHeader ? 14 : 0),\n                                         type.end() - endOffset);\n    }\n\n    void setContentTypeCode(const ContentType type) override\n    {\n        contentType_ = type;\n        flagForParsingContentType_ = true;\n        auto &typeStr = contentTypeToMime(type);\n        setContentType(std::string(typeStr.data(), typeStr.length()));\n    }\n\n    void setContentTypeString(const char *typeString,\n                              size_t typeStringLength) override;\n\n    // void setContentTypeCodeAndCharacterSet(ContentType type, const\n    // std::string &charSet = \"utf-8\") override\n    // {\n    //     contentType_ = type;\n    //     setContentType(webContentTypeAndCharsetToString(type, charSet));\n    // }\n\n    ContentType contentType() const override\n    {\n        parseContentTypeAndString();\n        return contentType_;\n    }\n\n    const char *matchedPathPatternData() const override\n    {\n        return matchedPathPattern_.data();\n    }\n\n    size_t matchedPathPatternLength() const override\n    {\n        return matchedPathPattern_.length();\n    }\n\n    void setMatchedPathPattern(const std::string &pathPattern)\n    {\n        matchedPathPattern_ = pathPattern;\n    }\n\n    const std::string &expect() const\n    {\n        static const std::string none{\"\"};\n        if (expectPtr_)\n            return *expectPtr_;\n        return none;\n    }\n\n    bool keepAlive() const\n    {\n        return keepAlive_;\n    }\n\n    bool connected() const noexcept override\n    {\n        if (auto conn = connPtr_.lock())\n        {\n            return conn->connected();\n        }\n        return false;\n    }\n\n    const std::weak_ptr<trantor::TcpConnection> &getConnectionPtr()\n        const noexcept override\n    {\n        return connPtr_;\n    }\n\n    bool isOnSecureConnection() const noexcept override\n    {\n        return isOnSecureConnection_;\n    }\n\n    const std::string &getJsonError() const override\n    {\n        static const std::string none{\"\"};\n        if (jsonParsingErrorPtr_)\n            return *jsonParsingErrorPtr_;\n        return none;\n    }\n\n    StreamDecompressStatus decompressBody();\n\n    // Stream mode api\n    ReqStreamStatus streamStatus() const\n    {\n        return streamStatus_;\n    }\n\n    bool isStreamMode() const\n    {\n        return streamStatus_ > ReqStreamStatus::None;\n    }\n\n    void streamStart();\n    void streamFinish();\n    void streamError(std::exception_ptr ex);\n\n    void setStreamReader(RequestStreamReaderPtr reader);\n    void waitForStreamFinish(std::function<void()> &&cb);\n    void quitStreamMode();\n\n    void startProcessing()\n    {\n        startProcessing_ = true;\n    }\n\n    bool isProcessingStarted() const\n    {\n        return startProcessing_;\n    }\n\n    ~HttpRequestImpl() override;\n\n  protected:\n    friend class HttpRequest;\n\n    void setContentType(const std::string &contentType)\n    {\n        contentTypeString_ = contentType;\n    }\n\n    void setContentType(std::string &&contentType)\n    {\n        contentTypeString_ = std::move(contentType);\n    }\n\n    void parseContentTypeAndString() const\n    {\n        if (!flagForParsingContentType_)\n        {\n            flagForParsingContentType_ = true;\n            auto &contentTypeString = getHeaderBy(\"content-type\");\n            if (contentTypeString == \"\")\n            {\n                contentType_ = CT_NONE;\n            }\n            else\n            {\n                auto pos = contentTypeString.find(';');\n                if (pos != std::string::npos)\n                {\n                    contentType_ = parseContentType(\n                        std::string_view(contentTypeString.data(), pos));\n                }\n                else\n                {\n                    contentType_ =\n                        parseContentType(std::string_view(contentTypeString));\n                }\n\n                if (contentType_ == CT_NONE)\n                    contentType_ = CT_CUSTOM;\n                contentTypeString_ = contentTypeString;\n            }\n        }\n    }\n\n  private:\n    void parseParameters() const;\n\n    void parseParametersOnce() const\n    {\n        // Not multi-thread safe but good, because we basically call this\n        // function in a single thread\n        if (!flagForParsingParameters_)\n        {\n            flagForParsingParameters_ = true;\n            parseParameters();\n        }\n    }\n\n    void createTmpFile();\n    void parseJson() const;\n#ifdef USE_BROTLI\n    StreamDecompressStatus decompressBodyBrotli() noexcept;\n#endif\n    StreamDecompressStatus decompressBodyGzip() noexcept;\n\n    static constexpr const std::string_view emptySv_{\"\"};\n\n    mutable bool flagForParsingParameters_{false};\n    mutable bool flagForParsingJson_{false};\n    HttpMethod method_{Invalid};\n    HttpMethod previousMethod_{Invalid};\n    Version version_{Version::kUnknown};\n    std::string path_;\n    /// Contains the encoded `path_` if and only if `path_` is set in encoded\n    /// form. If path is in a normal form and needed no decoding, then this will\n    /// be empty, as we do not need to store a duplicate.\n    std::string originalPath_;\n    bool pathEncode_{true};\n    std::string_view matchedPathPattern_{\"\"};\n    std::string query_;\n    SafeStringMap<std::string> headers_;\n    SafeStringMap<std::string> cookies_;\n    std::optional<size_t> contentLengthHeaderValue_;\n    size_t realContentLength_{0};\n    mutable SafeStringMap<std::string> parameters_;\n    mutable std::shared_ptr<Json::Value> jsonPtr_;\n    SessionPtr sessionPtr_;\n    mutable AttributesPtr attributesPtr_;\n    trantor::InetAddress peer_;\n    trantor::InetAddress local_;\n    trantor::Date creationDate_;\n    trantor::CertificatePtr peerCertificate_;\n    std::unique_ptr<CacheFile> cacheFilePtr_;\n    mutable std::unique_ptr<std::string> jsonParsingErrorPtr_;\n    std::unique_ptr<std::string> expectPtr_;\n    bool keepAlive_{true};\n    bool isOnSecureConnection_{false};\n    bool passThrough_{false};\n    std::vector<std::string> routingParams_;\n\n    ReqStreamStatus streamStatus_{ReqStreamStatus::None};\n    std::function<void()> streamFinishCb_;\n    RequestStreamReaderPtr streamReaderPtr_;\n    std::exception_ptr streamExceptionPtr_;\n    bool startProcessing_{false};\n    std::weak_ptr<trantor::TcpConnection> connPtr_;\n\n  protected:\n    std::string content_;\n    trantor::EventLoop *loop_;\n    mutable ContentType contentType_{CT_TEXT_PLAIN};\n    mutable bool flagForParsingContentType_{false};\n    mutable std::string contentTypeString_;\n};\n\nusing HttpRequestImplPtr = std::shared_ptr<HttpRequestImpl>;\n\ninline void swap(HttpRequestImpl &one, HttpRequestImpl &two) noexcept\n{\n    one.swap(two);\n}\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpRequestParser.cc",
    "content": "/**\n *\n *  HttpRequestParser.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpRequestParser.h\"\n#include <drogon/HttpTypes.h>\n#include <trantor/utils/Logger.h>\n#include <trantor/utils/MsgBuffer.h>\n#include <iostream>\n#include \"HttpAppFrameworkImpl.h\"\n#include \"HttpRequestImpl.h\"\n#include \"HttpResponseImpl.h\"\n#include \"HttpUtils.h\"\n\nusing namespace trantor;\nusing namespace drogon;\n\nstatic constexpr size_t CRLF_LEN = 2;            // strlen(\"crlf\")\nstatic constexpr size_t METHOD_MAX_LEN = 7;      // strlen(\"OPTIONS\")\nstatic constexpr size_t TRUNK_LEN_MAX_LEN = 16;  // 0xFFFFFFFF,FFFFFFFF\n\nHttpRequestParser::HttpRequestParser(const trantor::TcpConnectionPtr &connPtr)\n    : status_(HttpRequestParseStatus::kExpectMethod),\n      loop_(connPtr->getLoop()),\n      conn_(connPtr)\n{\n}\n\nbool HttpRequestParser::processRequestLine(const char *begin, const char *end)\n{\n    bool succeed = false;\n    const char *start = begin;\n    const char *space = std::find(start, end, ' ');\n    if (space != end)\n    {\n        const char *slash = std::find(start, space, '/');\n        if (slash != start && slash + 1 < space && *(slash + 1) == '/')\n        {\n            // scheme precedents\n            slash = std::find(slash + 2, space, '/');\n        }\n        const char *question = std::find(slash, space, '?');\n        if (slash != space)\n        {\n            request_->setPath(slash, question);\n        }\n        else\n        {\n            // An empty abs_path is equivalent to an abs_path of \"/\"\n            request_->setPath(\"/\");\n        }\n        if (question != space)\n        {\n            request_->setQuery(question + 1, space);\n        }\n        start = space + 1;\n        succeed = end - start == 8 && std::equal(start, end - 1, \"HTTP/1.\");\n        if (succeed)\n        {\n            if (*(end - 1) == '1')\n            {\n                request_->setVersion(Version::kHttp11);\n            }\n            else if (*(end - 1) == '0')\n            {\n                request_->setVersion(Version::kHttp10);\n            }\n            else\n            {\n                succeed = false;\n            }\n        }\n    }\n    return succeed;\n}\n\nHttpRequestImplPtr HttpRequestParser::makeRequestForPool(HttpRequestImpl *ptr)\n{\n    return std::shared_ptr<HttpRequestImpl>(\n        ptr, [weakPtr = weak_from_this()](HttpRequestImpl *p) {\n            auto thisPtr = weakPtr.lock();\n            if (thisPtr)\n            {\n                if (thisPtr->loop_->isInLoopThread())\n                {\n                    p->reset();\n                    thisPtr->requestsPool_.emplace_back(\n                        thisPtr->makeRequestForPool(p));\n                }\n                else\n                {\n                    auto &loop = thisPtr->loop_;\n                    loop->queueInLoop([thisPtr = std::move(thisPtr), p]() {\n                        p->reset();\n                        thisPtr->requestsPool_.emplace_back(\n                            thisPtr->makeRequestForPool(p));\n                    });\n                }\n            }\n            else\n            {\n                delete p;\n            }\n        });\n}\n\nvoid HttpRequestParser::reset()\n{\n    assert(loop_->isInLoopThread());\n    remainContentLength_ = 0;\n    status_ = HttpRequestParseStatus::kExpectMethod;\n    if (requestsPool_.empty())\n    {\n        request_ = makeRequestForPool(new HttpRequestImpl(loop_));\n    }\n    else\n    {\n        auto req = std::move(requestsPool_.back());\n        requestsPool_.pop_back();\n        request_ = std::move(req);\n        request_->setCreationDate(trantor::Date::now());\n    }\n}\n\n/**\n * @return return -HttpStatusCode if encounters any http errors in request\n * @return return -1 if encounters any other errors in request\n * @return return 0 if request is not ready\n * @return return 1 if request is ready\n * @return return 2 if request is ready and entering stream mode\n * @return return 3 if request header is ready and entering stream mode\n */\nint HttpRequestParser::parseRequest(MsgBuffer *buf)\n{\n    while (true)\n    {\n        switch (status_)\n        {\n            case (HttpRequestParseStatus::kExpectMethod):\n            {\n                auto *space = std::find(buf->peek(),\n                                        (const char *)buf->beginWrite(),\n                                        ' ');\n                // no space in buffer\n                if (space == buf->beginWrite())\n                {\n                    if (buf->readableBytes() > METHOD_MAX_LEN)\n                    {\n                        return -k400BadRequest;\n                    }\n                    return 0;\n                }\n                // try read method\n                if (!request_->setMethod(buf->peek(), space))\n                {\n                    return -k405MethodNotAllowed;\n                }\n                status_ = HttpRequestParseStatus::kExpectRequestLine;\n                buf->retrieveUntil(space + 1);\n                continue;\n            }\n            case HttpRequestParseStatus::kExpectRequestLine:\n            {\n                const char *crlf = buf->findCRLF();\n                if (!crlf)\n                {\n                    if (buf->readableBytes() >= 64 * 1024)\n                    {\n                        /// The limit for request line is 64K bytes. response\n                        /// k414RequestURITooLarge\n                        /// TODO: Make this configurable?\n                        return -k414RequestURITooLarge;\n                    }\n                    return 0;\n                }\n                if (!processRequestLine(buf->peek(), crlf))\n                {\n                    // error\n                    return -k400BadRequest;\n                }\n                buf->retrieveUntil(crlf + CRLF_LEN);\n                status_ = HttpRequestParseStatus::kExpectHeaders;\n                continue;\n            }\n            case HttpRequestParseStatus::kExpectHeaders:\n            {\n                const char *crlf = buf->findCRLF();\n                if (!crlf)\n                {\n                    if (buf->readableBytes() >= 64 * 1024)\n                    {\n                        /// The limit for every request header is 64K bytes;\n                        /// TODO: Make this configurable?\n                        return -k400BadRequest;\n                    }\n                    return 0;\n                }\n\n                const char *colon = std::find(buf->peek(), crlf, ':');\n                // found colon\n                if (colon != crlf)\n                {\n                    request_->addHeader(buf->peek(), colon, crlf);\n                    buf->retrieveUntil(crlf + CRLF_LEN);\n                    continue;\n                }\n                buf->retrieveUntil(crlf + CRLF_LEN);\n                // end of headers\n\n                // We might want a kProcessHeaders status for code readability\n                // and maintainability.\n\n                // process header information\n                auto &len = request_->getHeaderBy(\"content-length\");\n                if (!len.empty())\n                {\n                    try\n                    {\n                        remainContentLength_ =\n                            static_cast<size_t>(std::stoull(len));\n                    }\n                    catch (...)\n                    {\n                        return -k400BadRequest;\n                    }\n                    request_->contentLengthHeaderValue_ = remainContentLength_;\n                    if (remainContentLength_ == 0)\n                    {\n                        // content-length = 0, request is over.\n                        status_ = HttpRequestParseStatus::kGotAll;\n                    }\n                    else\n                    {\n                        status_ = HttpRequestParseStatus::kExpectBody;\n                    }\n                }\n                else\n                {\n                    const std::string &encode =\n                        request_->getHeaderBy(\"transfer-encoding\");\n                    if (encode.empty())\n                    {\n                        // no content-length and no transfer-encoding,\n                        // request is over.\n                        status_ = HttpRequestParseStatus::kGotAll;\n                    }\n                    else if (encode == \"chunked\")\n                    {\n                        status_ = HttpRequestParseStatus::kExpectChunkLen;\n                    }\n                    else\n                    {\n                        return -k501NotImplemented;\n                    }\n                }\n\n                // Check max body size\n                if (remainContentLength_ >\n                    HttpAppFrameworkImpl::instance().getClientMaxBodySize())\n                {\n                    return -k413RequestEntityTooLarge;\n                }\n\n                // Check expect:100-continue\n                auto &expect = request_->expect();\n                if (expect == \"100-continue\" &&\n                    request_->getVersion() >= Version::kHttp11)\n                {\n                    if (remainContentLength_ == 0)\n                    {\n                        // error\n                        return -k400BadRequest;\n                    }\n                    else\n                    {\n                        // rfc2616-8.2.3\n                        // TODO: consider adding an AOP for expect header\n                        auto connPtr = conn_.lock();  // ugly\n                        if (!connPtr)\n                        {\n                            return -1;\n                        }\n                        auto resp = HttpResponse::newHttpResponse();\n                        resp->setStatusCode(k100Continue);\n                        auto httpString =\n                            static_cast<HttpResponseImpl *>(resp.get())\n                                ->renderToBuffer();\n                        connPtr->send(std::move(*httpString));\n                    }\n                }\n                else if (!expect.empty())\n                {\n                    LOG_WARN << \"417ExpectationFailed for \\\"\" << expect << \"\\\"\";\n                    return -k417ExpectationFailed;\n                }\n\n                assert(status_ == HttpRequestParseStatus::kGotAll ||\n                       status_ == HttpRequestParseStatus::kExpectBody ||\n                       status_ == HttpRequestParseStatus::kExpectChunkLen);\n\n                if (app().isRequestStreamEnabled())\n                {\n                    request_->streamStart();\n                    if (status_ == HttpRequestParseStatus::kGotAll)\n                    {\n                        ++requestsCounter_;\n                        return 2;\n                    }\n                    else\n                    {\n                        return 3;\n                    }\n                }\n\n                // Reserve space for full body in non-stream mode.\n                // For stream mode requests that match a non-stream handler,\n                // we will reserve full body before waitForStreamFinish().\n                if (remainContentLength_)\n                {\n                    request_->reserveBodySize(remainContentLength_);\n                }\n                continue;\n            }\n            case HttpRequestParseStatus::kExpectBody:\n            {\n                size_t bytesToConsume =\n                    remainContentLength_ <= buf->readableBytes()\n                        ? remainContentLength_\n                        : buf->readableBytes();\n                if (bytesToConsume)\n                {\n                    request_->appendToBody(buf->peek(), bytesToConsume);\n                    buf->retrieve(bytesToConsume);\n                    remainContentLength_ -= bytesToConsume;\n                }\n\n                if (remainContentLength_ == 0)\n                {\n                    status_ = HttpRequestParseStatus::kGotAll;\n                    ++requestsCounter_;\n                    return 1;\n                }\n                // readableBytes() == 0, function should return.\n                return 0;\n            }\n            case HttpRequestParseStatus::kExpectChunkLen:\n            {\n                const char *crlf = buf->findCRLF();\n                if (!crlf)\n                {\n                    if (buf->readableBytes() > TRUNK_LEN_MAX_LEN + CRLF_LEN)\n                    {\n                        return -k400BadRequest;\n                    }\n                    return 0;\n                }\n                // chunk length line\n                std::string len(buf->peek(), crlf - buf->peek());\n                char *end;\n                currentChunkLength_ = strtol(len.c_str(), &end, 16);\n                if (currentChunkLength_ != 0)\n                {\n                    if (currentChunkLength_ + remainContentLength_ >\n                        HttpAppFrameworkImpl::instance().getClientMaxBodySize())\n                    {\n                        return -k413RequestEntityTooLarge;\n                    }\n                    status_ = HttpRequestParseStatus::kExpectChunkBody;\n                }\n                else\n                {\n                    status_ = HttpRequestParseStatus::kExpectLastEmptyChunk;\n                }\n                buf->retrieveUntil(crlf + CRLF_LEN);\n                continue;\n            }\n            case HttpRequestParseStatus::kExpectChunkBody:\n            {\n                if (buf->readableBytes() < (currentChunkLength_ + CRLF_LEN))\n                {\n                    return 0;\n                }\n                if (*(buf->peek() + currentChunkLength_) != '\\r' ||\n                    *(buf->peek() + currentChunkLength_ + 1) != '\\n')\n                {\n                    // error!\n                    return -k400BadRequest;\n                }\n                request_->appendToBody(buf->peek(), currentChunkLength_);\n                buf->retrieve(currentChunkLength_ + CRLF_LEN);\n                remainContentLength_ += currentChunkLength_;\n                currentChunkLength_ = 0;\n                status_ = HttpRequestParseStatus::kExpectChunkLen;\n                continue;\n            }\n            case HttpRequestParseStatus::kExpectLastEmptyChunk:\n            {\n                // last empty chunk\n                if (buf->readableBytes() < CRLF_LEN)\n                {\n                    return 0;\n                }\n                if (*(buf->peek()) != '\\r' || *(buf->peek() + 1) != '\\n')\n                {\n                    // error!\n                    return -k400BadRequest;\n                }\n                buf->retrieve(CRLF_LEN);\n\n                if (!request_->isStreamMode())\n                {\n                    // Previously we only have non-stream mode, drogon handled\n                    // chunked encoding internally, and give user a regular\n                    // request as if it has a content-length header.\n                    //\n                    // We have to keep compatibility for non-stream mode.\n                    //\n                    // But I don't think it's a good implementation. We should\n                    // instead add an api to access real content-length of\n                    // requests.\n                    // Now HttpRequest::realContentLength() is added, and user\n                    // should no longer parse content-length header by\n                    // themselves.\n                    //\n                    // NOTE: request forward behavior may be infected in stream\n                    // mode, we should check it out.\n                    request_->addHeader(\"content-length\",\n                                        std::to_string(\n                                            request_->realContentLength()));\n                    request_->removeHeaderBy(\"transfer-encoding\");\n                }\n                status_ = HttpRequestParseStatus::kGotAll;\n                ++requestsCounter_;\n                return 1;\n            }\n            case HttpRequestParseStatus::kGotAll:\n            {\n                ++requestsCounter_;\n                return 1;\n            }\n        }\n    }\n    return -1;  // won't reach here, just to make compiler happy\n}\n\nvoid HttpRequestParser::pushRequestToPipelining(const HttpRequestPtr &req,\n                                                bool isHeadMethod)\n{\n    assert(loop_->isInLoopThread());\n    requestPipelining_.push_back({req, {nullptr, isHeadMethod}});\n}\n\n/**\n * @return returns true if the the response is the first in pipeline\n */\nbool HttpRequestParser::pushResponseToPipelining(const HttpRequestPtr &req,\n                                                 HttpResponsePtr resp)\n{\n    assert(loop_->isInLoopThread());\n    for (size_t i = 0; i != requestPipelining_.size(); ++i)\n    {\n        if (requestPipelining_[i].first == req)\n        {\n            requestPipelining_[i].second.first = std::move(resp);\n            return i == 0;\n        }\n    }\n    assert(false);  // Should always find a match\n    return false;\n}\n\nvoid HttpRequestParser::popReadyResponses(\n    std::vector<std::pair<HttpResponsePtr, bool>> &buffer)\n{\n    while (!requestPipelining_.empty() &&\n           requestPipelining_.front().second.first)\n    {\n        buffer.push_back(std::move(requestPipelining_.front().second));\n        requestPipelining_.pop_front();\n    }\n}\n"
  },
  {
    "path": "lib/src/HttpRequestParser.h",
    "content": "/**\n *\n *  HttpRequestParser.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/HttpTypes.h>\n#include <trantor/net/TcpConnection.h>\n#include <trantor/utils/MsgBuffer.h>\n#include <trantor/utils/NonCopyable.h>\n#include <deque>\n#include <memory>\n#include <mutex>\n#include \"impl_forwards.h\"\n\nnamespace drogon\n{\nclass HttpRequestParser : public trantor::NonCopyable,\n                          public std::enable_shared_from_this<HttpRequestParser>\n{\n  public:\n    enum class HttpRequestParseStatus\n    {\n        kExpectMethod,\n        kExpectRequestLine,\n        kExpectHeaders,\n        kExpectBody,\n        kExpectChunkLen,\n        kExpectChunkBody,\n        kExpectLastEmptyChunk,\n        kGotAll,\n    };\n\n    explicit HttpRequestParser(const trantor::TcpConnectionPtr &connPtr);\n\n    int parseRequest(trantor::MsgBuffer *buf);\n\n    bool gotAll() const\n    {\n        return status_ == HttpRequestParseStatus::kGotAll;\n    }\n\n    void reset();\n\n    const HttpRequestImplPtr &requestImpl() const\n    {\n        return request_;\n    }\n\n    bool firstReq()\n    {\n        if (firstRequest_)\n        {\n            firstRequest_ = false;\n            return true;\n        }\n        return false;\n    }\n\n    const WebSocketConnectionImplPtr &webSocketConn() const\n    {\n        return websockConnPtr_;\n    }\n\n    void setWebsockConnection(const WebSocketConnectionImplPtr &conn)\n    {\n        websockConnPtr_ = conn;\n    }\n\n    // to support request pipelining(rfc2616-8.1.2.2)\n    void pushRequestToPipelining(const HttpRequestPtr &, bool isHeadMethod);\n    bool pushResponseToPipelining(const HttpRequestPtr &, HttpResponsePtr);\n    void popReadyResponses(std::vector<std::pair<HttpResponsePtr, bool>> &);\n\n    size_t numberOfRequestsInPipelining() const\n    {\n        return requestPipelining_.size();\n    }\n\n    bool emptyPipelining()\n    {\n        return requestPipelining_.empty();\n    }\n\n    bool isStop() const\n    {\n        return stopWorking_;\n    }\n\n    void stop()\n    {\n        stopWorking_ = true;\n    }\n\n    size_t numberOfRequestsParsed() const\n    {\n        return requestsCounter_;\n    }\n\n    trantor::MsgBuffer &getBuffer()\n    {\n        return sendBuffer_;\n    }\n\n    std::vector<std::pair<HttpResponsePtr, bool>> &getResponseBuffer()\n    {\n        assert(loop_->isInLoopThread());\n        if (!responseBuffer_)\n        {\n            responseBuffer_ =\n                std::unique_ptr<std::vector<std::pair<HttpResponsePtr, bool>>>(\n                    new std::vector<std::pair<HttpResponsePtr, bool>>);\n        }\n        return *responseBuffer_;\n    }\n\n    std::vector<HttpRequestImplPtr> &getRequestBuffer()\n    {\n        assert(loop_->isInLoopThread());\n        if (!requestBuffer_)\n        {\n            requestBuffer_ = std::unique_ptr<std::vector<HttpRequestImplPtr>>(\n                new std::vector<HttpRequestImplPtr>);\n        }\n        return *requestBuffer_;\n    }\n\n  private:\n    HttpRequestImplPtr makeRequestForPool(HttpRequestImpl *p);\n    bool processRequestLine(const char *begin, const char *end);\n    HttpRequestParseStatus status_;\n    trantor::EventLoop *loop_;\n    HttpRequestImplPtr request_;\n    bool firstRequest_{true};\n    WebSocketConnectionImplPtr websockConnPtr_;\n    std::deque<std::pair<HttpRequestPtr, std::pair<HttpResponsePtr, bool>>>\n        requestPipelining_;\n    size_t requestsCounter_{0};\n    std::weak_ptr<trantor::TcpConnection> conn_;\n    bool stopWorking_{false};\n    trantor::MsgBuffer sendBuffer_;\n    std::unique_ptr<std::vector<std::pair<HttpResponsePtr, bool>>>\n        responseBuffer_;\n    std::unique_ptr<std::vector<HttpRequestImplPtr>> requestBuffer_;\n    std::vector<HttpRequestImplPtr> requestsPool_;\n    size_t currentChunkLength_{0};\n    size_t remainContentLength_{0};\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpResponseImpl.cc",
    "content": "/**\n *\n *  @file HttpResponseImpl.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpResponseImpl.h\"\n#include \"AOPAdvice.h\"\n#include \"HttpAppFrameworkImpl.h\"\n#include \"HttpUtils.h\"\n#include <drogon/HttpViewData.h>\n#include <drogon/IOThreadStorage.h>\n#include <filesystem>\n#include <fstream>\n#include <memory>\n#include <cstdio>\n#include <string>\n#include <sys/stat.h>\n#include <trantor/utils/Logger.h>\n\nusing namespace trantor;\nusing namespace drogon;\nusing namespace std::literals::string_literals;\nusing namespace std::placeholders;\n#ifdef _WIN32\n#undef max\n#endif\n\nnamespace drogon\n{\n// \"Fri, 23 Aug 2019 12:58:03 GMT\" length = 29\nstatic const size_t httpFullDateStringLength = 29;\n\nstatic inline HttpResponsePtr genHttpResponse(const std::string &viewName,\n                                              const HttpViewData &data,\n                                              const HttpRequestPtr &req)\n{\n    auto templ = DrTemplateBase::newTemplate(viewName);\n    if (templ)\n    {\n        auto res = HttpResponse::newHttpResponse();\n        res->setBody(templ->genText(data));\n        return res;\n    }\n    return drogon::HttpResponse::newNotFoundResponse(req);\n}\n}  // namespace drogon\n\nvoid HttpResponseImpl::setAllowCompression(bool allow)\n{\n    allowCompression_ = allow;\n}\n\nbool HttpResponseImpl::allowCompression() const\n{\n    return allowCompression_;\n}\n\nHttpResponsePtr HttpResponse::newHttpResponse()\n{\n    auto res = std::make_shared<HttpResponseImpl>(k200OK, CT_TEXT_HTML);\n    AopAdvice::instance().passResponseCreationAdvices(res);\n    return res;\n}\n\nHttpResponsePtr HttpResponse::newHttpResponse(HttpStatusCode code,\n                                              ContentType type)\n{\n    auto res = std::make_shared<HttpResponseImpl>(code, type);\n    AopAdvice::instance().passResponseCreationAdvices(res);\n    return res;\n}\n\nHttpResponsePtr HttpResponse::newHttpJsonResponse(const Json::Value &data)\n{\n    auto res = std::make_shared<HttpResponseImpl>(k200OK, CT_APPLICATION_JSON);\n    res->setJsonObject(data);\n    AopAdvice::instance().passResponseCreationAdvices(res);\n    return res;\n}\n\nHttpResponsePtr HttpResponse::newHttpJsonResponse(Json::Value &&data)\n{\n    auto res = std::make_shared<HttpResponseImpl>(k200OK, CT_APPLICATION_JSON);\n    res->setJsonObject(std::move(data));\n    AopAdvice::instance().passResponseCreationAdvices(res);\n    return res;\n}\n\nconst char *HttpResponseImpl::versionString() const\n{\n    const char *result = \"UNKNOWN\";\n    switch (version_)\n    {\n        case Version::kHttp10:\n            result = \"HTTP/1.0\";\n            break;\n\n        case Version::kHttp11:\n            result = \"HTTP/1.1\";\n            break;\n\n        default:\n            break;\n    }\n    return result;\n}\n\nvoid HttpResponseImpl::generateBodyFromJson() const\n{\n    if (!jsonPtr_ || flagForSerializingJson_)\n    {\n        return;\n    }\n    flagForSerializingJson_ = true;\n    static std::once_flag once;\n    static Json::StreamWriterBuilder builder;\n    std::call_once(once, []() {\n        builder[\"commentStyle\"] = \"None\";\n        builder[\"indentation\"] = \"\";\n        if (!app().isUnicodeEscapingUsedInJson())\n        {\n            builder[\"emitUTF8\"] = true;\n        }\n        auto &precision = app().getFloatPrecisionInJson();\n        if (precision.first != 0)\n        {\n            builder[\"precision\"] = precision.first;\n            builder[\"precisionType\"] = precision.second;\n        }\n    });\n    bodyPtr_ = std::make_shared<HttpMessageStringBody>(\n        writeString(builder, *jsonPtr_));\n}\n\nHttpResponsePtr HttpResponse::newNotFoundResponse(const HttpRequestPtr &req)\n{\n    auto loop = trantor::EventLoop::getEventLoopOfCurrentThread();\n    auto &resp = HttpAppFrameworkImpl::instance().getCustom404Page();\n    if (resp)\n    {\n        if (loop && loop->index() < app().getThreadNum())\n        {\n            return resp;\n        }\n        else\n        {\n            return HttpResponsePtr{new HttpResponseImpl(\n                *static_cast<HttpResponseImpl *>(resp.get()))};\n        }\n    }\n    else\n    {\n        if (HttpAppFrameworkImpl::instance().isUsingCustomErrorHandler())\n        {\n            return app().getCustomErrorHandler()(k404NotFound, req);\n        }\n        else if (loop && loop->index() < app().getThreadNum())\n        {\n            // If the current thread is an IO thread\n            static std::once_flag threadOnce;\n            static IOThreadStorage<HttpResponsePtr> thread404Pages;\n            std::call_once(threadOnce, [req = req] {\n                thread404Pages.init([req = req](drogon::HttpResponsePtr &resp,\n                                                size_t /*index*/) {\n                    HttpViewData data;\n                    data.insert(\"version\", drogon::getVersion());\n                    resp = HttpResponse::newHttpViewResponse(\"drogon::NotFound\",\n                                                             data);\n                    resp->setStatusCode(k404NotFound);\n                    resp->setExpiredTime(0);\n                });\n            });\n            LOG_TRACE << \"Use cached 404 response\";\n            return thread404Pages.getThreadData();\n        }\n        else\n        {\n            HttpViewData data;\n            data.insert(\"version\", drogon::getVersion());\n            auto notFoundResp =\n                HttpResponse::newHttpViewResponse(\"drogon::NotFound\", data);\n            notFoundResp->setStatusCode(k404NotFound);\n            return notFoundResp;\n        }\n    }\n}\n\nHttpResponsePtr HttpResponse::newRedirectionResponse(\n    const std::string &location,\n    HttpStatusCode status)\n{\n    auto res = std::make_shared<HttpResponseImpl>();\n    res->setStatusCode(status);\n    res->redirect(location);\n    AopAdvice::instance().passResponseCreationAdvices(res);\n    return res;\n}\n\nHttpResponsePtr HttpResponse::newHttpViewResponse(const std::string &viewName,\n                                                  const HttpViewData &data,\n                                                  const HttpRequestPtr &req)\n{\n    return genHttpResponse(viewName, data, req);\n}\n\nHttpResponsePtr HttpResponse::newFileResponse(\n    const unsigned char *pBuffer,\n    size_t bufferLength,\n    const std::string &attachmentFileName,\n    ContentType type,\n    const std::string &typeString)\n{\n    // Make Raw HttpResponse\n    auto resp = std::make_shared<HttpResponseImpl>();\n\n    // Set response body and length\n    resp->setBody(\n        std::string(reinterpret_cast<const char *>(pBuffer), bufferLength));\n\n    // Set status of message\n    resp->setStatusCode(k200OK);\n\n    // Check for type and assign proper content type in header\n    if (!typeString.empty())\n    {\n        if (type == CT_NONE)\n            type = parseContentType(typeString);\n        if (type == CT_NONE)\n            type = CT_APPLICATION_OCTET_STREAM;  // XXX: Is this Ok?\n        static_cast<HttpResponse *>(resp.get())\n            ->setContentTypeCodeAndCustomString(type,\n                                                typeString.c_str(),\n                                                typeString.size());\n    }\n    else if (type != CT_NONE)\n    {\n        resp->setContentTypeCode(type);\n    }\n    else if (!attachmentFileName.empty())\n    {\n        resp->setContentTypeCode(drogon::getContentType(attachmentFileName));\n    }\n    else\n    {\n        resp->setContentTypeCode(\n            CT_APPLICATION_OCTET_STREAM);  // default content-type for file;\n    }\n\n    // Add additional header values\n    if (!attachmentFileName.empty())\n    {\n        resp->addHeader(\"Content-Disposition\",\n                        \"attachment; filename=\" + attachmentFileName);\n    }\n\n    // Finalize and return response\n    AopAdvice::instance().passResponseCreationAdvices(resp);\n    return resp;\n}\n\nHttpResponsePtr HttpResponse::newFileResponse(\n    const std::string &fullPath,\n    const std::string &attachmentFileName,\n    ContentType type,\n    const std::string &typeString,\n    const HttpRequestPtr &req)\n{\n    return newFileResponse(\n        fullPath, 0, 0, false, attachmentFileName, type, typeString, req);\n}\n\nHttpResponsePtr HttpResponse::newFileResponse(\n    const std::string &fullPath,\n    size_t offset,\n    size_t length,\n    bool setContentRange,\n    const std::string &attachmentFileName,\n    ContentType type,\n    const std::string &typeString,\n    const HttpRequestPtr &req)\n{\n    std::ifstream infile(utils::toNativePath(fullPath), std::ifstream::binary);\n    LOG_TRACE << \"send http file:\" << fullPath << \" offset \" << offset\n              << \" length \" << length;\n    if (!infile)\n    {\n        auto resp = HttpResponse::newNotFoundResponse(req);\n        return resp;\n    }\n    auto resp = std::make_shared<HttpResponseImpl>();\n    std::streambuf *pbuf = infile.rdbuf();\n    size_t filesize =\n        static_cast<size_t>(pbuf->pubseekoff(0, std::ifstream::end));\n    if (offset > filesize || length > filesize ||  // in case of overflow\n        offset + length > filesize)\n    {\n        resp->setStatusCode(k416RequestedRangeNotSatisfiable);\n        if (setContentRange)\n        {\n            char buf[64];\n            snprintf(buf, sizeof(buf), \"bytes */%zu\", filesize);\n            resp->addHeader(\"Content-Range\", std::string(buf));\n        }\n        return resp;\n    }\n    if (length == 0)\n    {\n        length = filesize - offset;\n    }\n    pbuf->pubseekoff(offset, std::ifstream::beg);  // rewind\n\n    if (HttpAppFrameworkImpl::instance().useSendfile() && length > 1024 * 200)\n    // TODO : Is 200k an appropriate value? Or set it to be configurable\n    {\n        // The advantages of sendfile() can only be reflected in sending large\n        // files.\n        resp->setSendfile(fullPath);\n        // Must set length with the right value! Content-Length header relies on\n        // this value.\n        resp->setSendfileRange(offset, length);\n    }\n    else\n    {\n        std::string str;\n        str.resize(length);\n        pbuf->sgetn(&str[0], length);\n        resp->setBody(std::move(str));\n        resp->setSendfileRange(offset, length);\n    }\n\n    // Set correct status code\n    if (length < filesize)\n    {\n        resp->setStatusCode(k206PartialContent);\n    }\n    else\n    {\n        resp->setStatusCode(k200OK);\n    }\n\n    // Infer content type\n    if (type == CT_NONE)\n    {\n        if (!typeString.empty())\n        {\n            auto r = static_cast<HttpResponse *>(resp.get());\n            if (type == CT_NONE)\n                type = parseContentType(typeString);\n            if (type == CT_NONE)\n                type = CT_CUSTOM;  // XXX: Is this Ok?\n            r->setContentTypeCodeAndCustomString(type, typeString);\n        }\n        else if (!attachmentFileName.empty())\n        {\n            resp->setContentTypeCode(\n                drogon::getContentType(attachmentFileName));\n        }\n        else\n        {\n            resp->setContentTypeCode(drogon::getContentType(fullPath));\n        }\n    }\n    else\n    {\n        if (typeString.empty())\n            resp->setContentTypeCode(type);\n        else\n        {\n            auto r = static_cast<HttpResponse *>(resp.get());\n            if (type == CT_NONE)\n                type = parseContentType(typeString);\n            if (type == CT_NONE)\n                type = CT_CUSTOM;  // XXX: Is this Ok?\n            r->setContentTypeCodeAndCustomString(type, typeString);\n        }\n    }\n\n    // Set headers\n    if (!attachmentFileName.empty())\n    {\n        resp->addHeader(\"Content-Disposition\",\n                        \"attachment; filename=\" + attachmentFileName);\n    }\n    if (setContentRange && length > 0)\n    {\n        char buf[128];\n        snprintf(buf,\n                 sizeof(buf),\n                 \"bytes %zu-%zu/%zu\",\n                 offset,\n                 offset + length - 1,\n                 filesize);\n        resp->addHeader(\"Content-Range\", std::string(buf));\n    }\n    AopAdvice::instance().passResponseCreationAdvices(resp);\n    return resp;\n}\n\nHttpResponsePtr HttpResponse::newStreamResponse(\n    const std::function<std::size_t(char *, std::size_t)> &callback,\n    const std::string &attachmentFileName,\n    ContentType type,\n    const std::string &typeString,\n    const HttpRequestPtr &req)\n{\n    LOG_TRACE << \"send stream as \"s\n              << (attachmentFileName.empty() ? \"raw data\"s\n                                             : \"file: \"s + attachmentFileName);\n    if (!callback)\n    {\n        auto resp = HttpResponse::newNotFoundResponse();\n        return resp;\n    }\n    auto resp = std::make_shared<HttpResponseImpl>();\n    resp->setStreamCallback(callback);\n    resp->setStatusCode(k200OK);\n\n    // Infer content type\n    if (type == CT_NONE)\n    {\n        if (!typeString.empty())\n        {\n            auto r = static_cast<HttpResponse *>(resp.get());\n            if (type == CT_NONE)\n                type = parseContentType(typeString);\n            if (type == CT_NONE)\n                type = CT_CUSTOM;  // XXX: Is this Ok?\n            r->setContentTypeCodeAndCustomString(type, typeString);\n        }\n        else if (!attachmentFileName.empty())\n        {\n            resp->setContentTypeCode(\n                drogon::getContentType(attachmentFileName));\n        }\n    }\n    else\n    {\n        if (typeString.empty())\n            resp->setContentTypeCode(type);\n        else\n        {\n            auto r = static_cast<HttpResponse *>(resp.get());\n            if (type == CT_NONE)\n                type = parseContentType(typeString);\n            if (type == CT_NONE)\n                type = CT_CUSTOM;  // XXX: Is this Ok?\n            r->setContentTypeCodeAndCustomString(type, typeString);\n        }\n    }\n\n    // Set headers\n    if (!attachmentFileName.empty())\n    {\n        resp->addHeader(\"Content-Disposition\",\n                        \"attachment; filename=\" + attachmentFileName);\n    }\n    AopAdvice::instance().passResponseCreationAdvices(resp);\n    return resp;\n}\n\nHttpResponsePtr HttpResponse::newAsyncStreamResponse(\n    const std::function<void(ResponseStreamPtr)> &callback,\n    bool disableKickoffTimeout)\n{\n    if (!callback)\n    {\n        auto resp = HttpResponse::newNotFoundResponse();\n        return resp;\n    }\n    auto resp = std::make_shared<HttpResponseImpl>();\n    resp->setAsyncStreamCallback(callback, disableKickoffTimeout);\n    resp->setStatusCode(k200OK);\n    AopAdvice::instance().passResponseCreationAdvices(resp);\n    return resp;\n}\n\nvoid HttpResponseImpl::makeHeaderString(trantor::MsgBuffer &buffer)\n{\n    buffer.ensureWritableBytes(128);\n    int len{0};\n    if (version_ == Version::kHttp11)\n    {\n        if (customStatusCode_ >= 0)\n        {\n            len = snprintf(buffer.beginWrite(),\n                           buffer.writableBytes(),\n                           \"HTTP/1.1 %d \",\n                           customStatusCode_);\n        }\n        else\n        {\n            len = snprintf(buffer.beginWrite(),\n                           buffer.writableBytes(),\n                           \"HTTP/1.1 %d \",\n                           statusCode_);\n        }\n    }\n    else\n    {\n        if (customStatusCode_ >= 0)\n        {\n            len = snprintf(buffer.beginWrite(),\n                           buffer.writableBytes(),\n                           \"HTTP/1.0 %d \",\n                           customStatusCode_);\n        }\n        else\n        {\n            len = snprintf(buffer.beginWrite(),\n                           buffer.writableBytes(),\n                           \"HTTP/1.0 %d \",\n                           statusCode_);\n        }\n    }\n\n    buffer.hasWritten(len);\n\n    if (!statusMessage_.empty())\n        buffer.append(statusMessage_.data(), statusMessage_.length());\n    buffer.append(\"\\r\\n\");\n    generateBodyFromJson();\n    if (!passThrough_)\n    {\n        buffer.ensureWritableBytes(64);\n        if (!contentLengthIsAllowed())\n        {\n            len = 0;\n            if ((bodyPtr_ && bodyPtr_->length() > 0) ||\n                !sendfileName_.empty() || streamCallback_ ||\n                asyncStreamCallback_)\n            {\n                LOG_ERROR << \"The body should be empty when the content-length \"\n                             \"is not allowed!\";\n            }\n        }\n        else if (streamCallback_ || asyncStreamCallback_)\n        {\n            // When the headers are created, it is time to set the transfer\n            // encoding to chunked if the contents size is not specified\n            if (!ifCloseConnection() &&\n                headers_.find(\"content-length\") == headers_.end())\n            {\n                LOG_DEBUG << \"send stream with transfer-encoding chunked\";\n                headers_[\"transfer-encoding\"] = \"chunked\";\n            }\n            len = 0;\n        }\n        else if (sendfileName_.empty())\n        {\n            auto bodyLength = bodyPtr_ ? bodyPtr_->length() : 0;\n            len = snprintf(buffer.beginWrite(),\n                           buffer.writableBytes(),\n                           contentLengthFormatString<decltype(bodyLength)>(),\n                           bodyLength);\n        }\n        else\n        {\n            auto bodyLength = sendfileRange_.second;\n            len = snprintf(buffer.beginWrite(),\n                           buffer.writableBytes(),\n                           contentLengthFormatString<decltype(bodyLength)>(),\n                           bodyLength);\n        }\n        buffer.hasWritten(len);\n        if (headers_.find(\"connection\") == headers_.end())\n        {\n            if (closeConnection_)\n            {\n                buffer.append(\"connection: close\\r\\n\");\n            }\n            else if (version_ == Version::kHttp10)\n            {\n                buffer.append(\"connection: Keep-Alive\\r\\n\");\n            }\n        }\n\n        if (!contentTypeString_.empty())\n        {\n            buffer.append(\"content-type: \");\n            buffer.append(contentTypeString_);\n            buffer.append(\"\\r\\n\");\n        }\n        if (HttpAppFrameworkImpl::instance().sendServerHeader())\n        {\n            buffer.append(\n                HttpAppFrameworkImpl::instance().getServerHeaderString());\n        }\n    }\n\n    for (auto it = headers_.begin(); it != headers_.end(); ++it)\n    {\n        buffer.append(it->first);\n        buffer.append(\": \");\n        buffer.append(it->second);\n        buffer.append(\"\\r\\n\");\n    }\n}\n\nvoid HttpResponseImpl::renderToBuffer(trantor::MsgBuffer &buffer)\n{\n    if (expriedTime_ >= 0)\n    {\n        auto strPtr = renderToBuffer();\n        buffer.append(strPtr->peek(), strPtr->readableBytes());\n        return;\n    }\n\n    if (!fullHeaderString_)\n    {\n        makeHeaderString(buffer);\n    }\n    else\n    {\n        buffer.append(*fullHeaderString_);\n    }\n\n    // output cookies\n    if (!cookies_.empty())\n    {\n        for (auto it = cookies_.begin(); it != cookies_.end(); ++it)\n        {\n            buffer.append(it->second.cookieString());\n        }\n    }\n\n    // output Date header\n    if (!passThrough_ &&\n        drogon::HttpAppFrameworkImpl::instance().sendDateHeader())\n    {\n        buffer.append(\"date: \");\n        buffer.append(utils::getHttpFullDateStr(trantor::Date::date()));\n        buffer.append(\"\\r\\n\\r\\n\");\n    }\n    else\n    {\n        buffer.append(\"\\r\\n\");\n    }\n    if (bodyPtr_ && contentLengthIsAllowed())\n        buffer.append(bodyPtr_->data(), bodyPtr_->length());\n}\n\nstd::shared_ptr<trantor::MsgBuffer> HttpResponseImpl::renderToBuffer()\n{\n    if (expriedTime_ >= 0)\n    {\n        if (!passThrough_ &&\n            drogon::HttpAppFrameworkImpl::instance().sendDateHeader())\n        {\n            if (datePos_ != static_cast<size_t>(-1))\n            {\n                auto now = trantor::Date::now();\n                bool isDateChanged =\n                    ((now.microSecondsSinceEpoch() /\n                      trantor::Date::MICRO_SECONDS_PER_SEC) != httpStringDate_);\n                assert(httpString_);\n                if (isDateChanged)\n                {\n                    httpStringDate_ = now.microSecondsSinceEpoch() /\n                                      trantor::Date::MICRO_SECONDS_PER_SEC;\n                    auto newDate = utils::getHttpFullDate(now);\n\n                    httpString_ =\n                        std::make_shared<trantor::MsgBuffer>(*httpString_);\n                    memcpy((void *)&(*httpString_)[datePos_],\n                           newDate,\n                           httpFullDateStringLength);\n                    return httpString_;\n                }\n\n                return httpString_;\n            }\n        }\n        else\n        {\n            if (httpString_)\n                return httpString_;\n        }\n    }\n    auto httpString = std::make_shared<trantor::MsgBuffer>(256);\n    if (!fullHeaderString_)\n    {\n        makeHeaderString(*httpString);\n    }\n    else\n    {\n        httpString->append(*fullHeaderString_);\n    }\n\n    // output cookies\n    if (!cookies_.empty())\n    {\n        for (auto it = cookies_.begin(); it != cookies_.end(); ++it)\n        {\n            httpString->append(it->second.cookieString());\n        }\n    }\n\n    // output Date header\n    if (!passThrough_ &&\n        drogon::HttpAppFrameworkImpl::instance().sendDateHeader())\n    {\n        httpString->append(\"date: \");\n        auto datePos = httpString->readableBytes();\n        httpString->append(utils::getHttpFullDateStr(trantor::Date::date()));\n        httpString->append(\"\\r\\n\\r\\n\");\n        datePos_ = datePos;\n    }\n    else\n    {\n        httpString->append(\"\\r\\n\");\n    }\n\n    LOG_TRACE << \"response(no body):\"\n              << std::string_view{httpString->peek(),\n                                  httpString->readableBytes()};\n    if (bodyPtr_)\n        httpString->append(bodyPtr_->data(), bodyPtr_->length());\n    if (expriedTime_ >= 0)\n    {\n        httpString_ = httpString;\n    }\n    return httpString;\n}\n\nstd::shared_ptr<trantor::MsgBuffer> HttpResponseImpl::\n    renderHeaderForHeadMethod()\n{\n    auto httpString = std::make_shared<trantor::MsgBuffer>(256);\n    if (!fullHeaderString_)\n    {\n        makeHeaderString(*httpString);\n    }\n    else\n    {\n        httpString->append(*fullHeaderString_);\n    }\n\n    // output cookies\n    if (!cookies_.empty())\n    {\n        for (auto it = cookies_.begin(); it != cookies_.end(); ++it)\n        {\n            httpString->append(it->second.cookieString());\n        }\n    }\n\n    // output Date header\n    if (!passThrough_ &&\n        drogon::HttpAppFrameworkImpl::instance().sendDateHeader())\n    {\n        httpString->append(\"date: \");\n        httpString->append(utils::getHttpFullDate(trantor::Date::date()),\n                           httpFullDateStringLength);\n        httpString->append(\"\\r\\n\\r\\n\");\n    }\n    else\n    {\n        httpString->append(\"\\r\\n\");\n    }\n\n    return httpString;\n}\n\nvoid HttpResponseImpl::addHeader(const char *start,\n                                 const char *colon,\n                                 const char *end)\n{\n    fullHeaderString_.reset();\n    std::string field(start, colon);\n    transform(field.begin(), field.end(), field.begin(), [](unsigned char c) {\n        return tolower(c);\n    });\n    ++colon;\n    while (colon < end && isspace(static_cast<unsigned char>(*colon)))\n    {\n        ++colon;\n    }\n    std::string value(colon, end);\n    while (!value.empty() &&\n           isspace(static_cast<unsigned char>(value[value.size() - 1])))\n    {\n        value.resize(value.size() - 1);\n    }\n\n    if (field == \"set-cookie\")\n    {\n        // LOG_INFO<<\"cookies!!!:\"<<value;\n        auto values = utils::splitString(value, \";\");\n        Cookie cookie;\n        cookie.setHttpOnly(false);\n        for (size_t i = 0; i < values.size(); ++i)\n        {\n            std::string &coo = values[i];\n            std::string cookie_name;\n            std::string cookie_value;\n            auto epos = coo.find('=');\n            if (epos != std::string::npos)\n            {\n                cookie_name = coo.substr(0, epos);\n                std::string::size_type cpos = 0;\n                while (cpos < cookie_name.length() &&\n                       isspace(static_cast<unsigned char>(cookie_name[cpos])))\n                    ++cpos;\n                cookie_name = cookie_name.substr(cpos);\n                ++epos;\n                while (epos < coo.length() &&\n                       isspace(static_cast<unsigned char>(coo[epos])))\n                    ++epos;\n                cookie_value = coo.substr(epos);\n            }\n            else\n            {\n                std::string::size_type cpos = 0;\n                while (cpos < coo.length() &&\n                       isspace(static_cast<unsigned char>(coo[cpos])))\n                    ++cpos;\n                cookie_name = coo.substr(cpos);\n            }\n            if (i == 0)\n            {\n                cookie.setKey(cookie_name);\n                cookie.setValue(cookie_value);\n            }\n            else\n            {\n                std::transform(cookie_name.begin(),\n                               cookie_name.end(),\n                               cookie_name.begin(),\n                               [](unsigned char c) { return tolower(c); });\n                if (cookie_name == \"path\")\n                {\n                    cookie.setPath(cookie_value);\n                }\n                else if (cookie_name == \"domain\")\n                {\n                    cookie.setDomain(cookie_value);\n                }\n                else if (cookie_name == \"expires\")\n                {\n                    cookie.setExpiresDate(utils::getHttpDate(cookie_value));\n                }\n                else if (cookie_name == \"secure\")\n                {\n                    cookie.setSecure(true);\n                }\n                else if (cookie_name == \"httponly\")\n                {\n                    cookie.setHttpOnly(true);\n                }\n                else if (cookie_name == \"samesite\")\n                {\n                    cookie.setSameSite(\n                        cookie.convertString2SameSite(cookie_value));\n                }\n                else if (cookie_name == \"max-age\")\n                {\n                    cookie.setMaxAge(std::stoi(cookie_value));\n                }\n            }\n        }\n        if (!cookie.key().empty())\n        {\n            cookies_[cookie.key()] = std::move(cookie);\n        }\n    }\n    else\n    {\n        headers_[std::move(field)] = std::move(value);\n    }\n}\n\nvoid HttpResponseImpl::swap(HttpResponseImpl &that) noexcept\n{\n    using std::swap;\n    headers_.swap(that.headers_);\n    cookies_.swap(that.cookies_);\n    swap(statusCode_, that.statusCode_);\n    swap(version_, that.version_);\n    swap(statusMessage_, that.statusMessage_);\n    swap(closeConnection_, that.closeConnection_);\n    bodyPtr_.swap(that.bodyPtr_);\n    swap(contentType_, that.contentType_);\n    swap(flagForParsingContentType_, that.flagForParsingContentType_);\n    swap(flagForParsingJson_, that.flagForParsingJson_);\n    swap(sendfileName_, that.sendfileName_);\n    swap(streamCallback_, that.streamCallback_);\n    swap(asyncStreamCallback_, that.asyncStreamCallback_);\n    jsonPtr_.swap(that.jsonPtr_);\n    fullHeaderString_.swap(that.fullHeaderString_);\n    httpString_.swap(that.httpString_);\n    swap(datePos_, that.datePos_);\n    swap(jsonParsingErrorPtr_, that.jsonParsingErrorPtr_);\n}\n\nvoid HttpResponseImpl::clear()\n{\n    statusCode_ = kUnknown;\n    version_ = Version::kHttp11;\n    statusMessage_ = std::string_view{};\n    fullHeaderString_.reset();\n    jsonParsingErrorPtr_.reset();\n    sendfileName_.clear();\n    if (streamCallback_)\n    {\n        LOG_TRACE << \"Cleanup HttpResponse stream callback\";\n        streamCallback_(nullptr, 0);  // callback internal cleanup\n        streamCallback_ = {};\n    }\n    if (asyncStreamCallback_)\n    {\n        // asyncStreamCallback_(nullptr);\n        asyncStreamCallback_ = {};\n    }\n    headers_.clear();\n    cookies_.clear();\n    bodyPtr_.reset();\n    jsonPtr_.reset();\n    expriedTime_ = -1;\n    datePos_ = std::string::npos;\n    flagForParsingContentType_ = false;\n    flagForParsingJson_ = false;\n}\n\nvoid HttpResponseImpl::parseJson() const\n{\n    static std::once_flag once;\n    static Json::CharReaderBuilder builder;\n    std::call_once(once, []() {\n        builder[\"collectComments\"] = false;\n        builder[\"stackLimit\"] =\n            static_cast<Json::UInt>(drogon::app().getJsonParserStackLimit());\n    });\n    JSONCPP_STRING errs;\n    std::unique_ptr<Json::CharReader> reader(builder.newCharReader());\n    if (bodyPtr_)\n    {\n        jsonPtr_ = std::make_shared<Json::Value>();\n        if (!reader->parse(bodyPtr_->data(),\n                           bodyPtr_->data() + bodyPtr_->length(),\n                           jsonPtr_.get(),\n                           &errs))\n        {\n            LOG_ERROR << errs;\n            LOG_ERROR << \"body: \" << bodyPtr_->getString();\n            jsonPtr_.reset();\n            jsonParsingErrorPtr_ =\n                std::make_shared<std::string>(std::move(errs));\n        }\n        else\n        {\n            jsonParsingErrorPtr_.reset();\n        }\n    }\n    else\n    {\n        jsonPtr_.reset();\n        jsonParsingErrorPtr_ =\n            std::make_shared<std::string>(\"empty response body\");\n    }\n}\n\nbool HttpResponseImpl::shouldBeCompressed() const\n{\n    // If the developer said \"No\" stop immediately.\n    if (!allowCompression_)\n    {\n        return false;\n    }\n\n    if (streamCallback_ || asyncStreamCallback_ || !sendfileName_.empty() ||\n        contentType() >= CT_APPLICATION_OCTET_STREAM ||\n        getBody().length() < 1024 ||\n        !(getHeaderBy(\"content-encoding\").empty()) || !contentLengthIsAllowed())\n    {\n        return false;\n    }\n    return true;\n}\n\nvoid HttpResponseImpl::setContentTypeString(const char *typeString,\n                                            size_t typeStringLength)\n{\n    std::string sv(typeString, typeStringLength);\n    auto contentType = parseContentType(sv);\n    if (contentType == CT_NONE)\n        contentType = CT_CUSTOM;\n    contentType_ = contentType;\n    contentTypeString_ = std::string(sv);\n    flagForParsingContentType_ = true;\n}\n"
  },
  {
    "path": "lib/src/HttpResponseImpl.h",
    "content": "/**\n *\n *  @file HttpResponseImpl.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"HttpUtils.h\"\n#include \"HttpMessageBody.h\"\n#include <drogon/exports.h>\n#include <drogon/HttpResponse.h>\n#include <drogon/utils/Utilities.h>\n#include <trantor/net/InetAddress.h>\n#include <trantor/utils/Date.h>\n#include <trantor/utils/MsgBuffer.h>\n#include <memory>\n#include <mutex>\n#include <string>\n#include <atomic>\n#include <unordered_map>\n\nnamespace drogon\n{\nclass DROGON_EXPORT HttpResponseImpl : public HttpResponse\n{\n    friend class HttpResponseParser;\n\n  public:\n    HttpResponseImpl() : creationDate_(trantor::Date::now())\n    {\n    }\n\n    HttpResponseImpl(HttpStatusCode code, ContentType type)\n        : statusCode_(code),\n          statusMessage_(statusCodeToString(code)),\n          creationDate_(trantor::Date::now()),\n          contentType_(type),\n          flagForParsingContentType_(true),\n          contentTypeString_(contentTypeToMime(type))\n    {\n    }\n\n    void setPassThrough(bool flag) override\n    {\n        passThrough_ = flag;\n    }\n\n    HttpStatusCode statusCode() const override\n    {\n        return statusCode_;\n    }\n\n    const trantor::Date &creationDate() const override\n    {\n        return creationDate_;\n    }\n\n    void setStatusCode(HttpStatusCode code) override\n    {\n        statusCode_ = code;\n        setStatusMessage(statusCodeToString(code));\n    }\n\n    void setVersion(const Version v) override\n    {\n        version_ = v;\n        if (version_ == Version::kHttp10)\n        {\n            closeConnection_ = true;\n        }\n    }\n\n    Version version() const override\n    {\n        return version_;\n    }\n\n    const char *versionString() const override;\n\n    void setCloseConnection(bool on) override\n    {\n        closeConnection_ = on;\n    }\n\n    bool ifCloseConnection() const override\n    {\n        return closeConnection_;\n    }\n\n    void setContentTypeCode(ContentType type) override\n    {\n        contentType_ = type;\n        auto ct = contentTypeToMime(type);\n        contentTypeString_ = std::string(ct.data(), ct.size());\n        flagForParsingContentType_ = true;\n    }\n\n    //  void setContentTypeCodeAndCharacterSet(ContentType type, const\n    // std::string &charSet = \"utf-8\") override\n    // {\n    //     contentType_ = type;\n    //     setContentType(webContentTypeAndCharsetToString(type, charSet));\n    // }\n\n    ContentType contentType() const override\n    {\n        parseContentTypeAndString();\n        return contentType_;\n    }\n\n    const std::string &getHeader(std::string key) const override\n    {\n        transform(key.begin(), key.end(), key.begin(), [](unsigned char c) {\n            return tolower(c);\n        });\n        return getHeaderBy(key);\n    }\n\n    void removeHeader(std::string key) override\n    {\n        transform(key.begin(), key.end(), key.begin(), [](unsigned char c) {\n            return tolower(c);\n        });\n        removeHeaderBy(key);\n    }\n\n    const SafeStringMap<std::string> &headers() const override\n    {\n        return headers_;\n    }\n\n    const std::string &getHeaderBy(const std::string &lowerKey) const\n    {\n        static const std::string defaultVal;\n        auto iter = headers_.find(lowerKey);\n        if (iter == headers_.end())\n        {\n            return defaultVal;\n        }\n        return iter->second;\n    }\n\n    void removeHeaderBy(const std::string &lowerKey)\n    {\n        fullHeaderString_.reset();\n        headers_.erase(lowerKey);\n    }\n\n    void addHeader(std::string field, const std::string &value) override\n    {\n        fullHeaderString_.reset();\n        transform(field.begin(),\n                  field.end(),\n                  field.begin(),\n                  [](unsigned char c) { return tolower(c); });\n        headers_[std::move(field)] = value;\n    }\n\n    void addHeader(std::string field, std::string &&value) override\n    {\n        fullHeaderString_.reset();\n        transform(field.begin(),\n                  field.end(),\n                  field.begin(),\n                  [](unsigned char c) { return tolower(c); });\n        headers_[std::move(field)] = std::move(value);\n    }\n\n    void addHeader(const char *start, const char *colon, const char *end);\n\n    void addCookie(const std::string &key, const std::string &value) override\n    {\n        cookies_[key] = Cookie(key, value);\n    }\n\n    void addCookie(const Cookie &cookie) override\n    {\n        cookies_[cookie.key()] = cookie;\n    }\n\n    void addCookie(Cookie &&cookie) override\n    {\n        cookies_[cookie.key()] = std::move(cookie);\n    }\n\n    const Cookie &getCookie(const std::string &key) const override\n    {\n        static const Cookie defaultCookie;\n        auto it = cookies_.find(key);\n        if (it != cookies_.end())\n        {\n            return it->second;\n        }\n        return defaultCookie;\n    }\n\n    const SafeStringMap<Cookie> &cookies() const override\n    {\n        return cookies_;\n    }\n\n    void removeCookie(const std::string &key) override\n    {\n        cookies_.erase(key);\n    }\n\n    void setBody(const std::string &body) override\n    {\n        bodyPtr_ = std::make_shared<HttpMessageStringBody>(body);\n        if (passThrough_)\n        {\n            addHeader(\"content-length\", std::to_string(bodyPtr_->length()));\n        }\n    }\n\n    void setBody(std::string &&body) override\n    {\n        bodyPtr_ = std::make_shared<HttpMessageStringBody>(std::move(body));\n        if (passThrough_)\n        {\n            addHeader(\"content-length\", std::to_string(bodyPtr_->length()));\n        }\n    }\n\n    void redirect(const std::string &url)\n    {\n        headers_[\"location\"] = url;\n    }\n\n    std::shared_ptr<trantor::MsgBuffer> renderToBuffer();\n    void renderToBuffer(trantor::MsgBuffer &buffer);\n    std::shared_ptr<trantor::MsgBuffer> renderHeaderForHeadMethod();\n    void clear() override;\n\n    void setExpiredTime(ssize_t expiredTime) override\n    {\n        expriedTime_ = expiredTime;\n        datePos_ = std::string::npos;\n        if (expriedTime_ < 0 && version_ == Version::kHttp10)\n        {\n            fullHeaderString_.reset();\n        }\n    }\n\n    ssize_t expiredTime() const override\n    {\n        return expriedTime_;\n    }\n\n    const char *getBodyData() const override\n    {\n        if (!flagForSerializingJson_ && jsonPtr_)\n        {\n            generateBodyFromJson();\n        }\n        else if (!bodyPtr_)\n        {\n            return nullptr;\n        }\n        return bodyPtr_->data();\n    }\n\n    size_t getBodyLength() const override\n    {\n        if (bodyPtr_)\n            return bodyPtr_->length();\n        return 0;\n    }\n\n    void swap(HttpResponseImpl &that) noexcept;\n    void parseJson() const;\n\n    const std::shared_ptr<Json::Value> &jsonObject() const override\n    {\n        // Not multi-thread safe but good, because we basically call this\n        // function in a single thread\n        if (!flagForParsingJson_)\n        {\n            flagForParsingJson_ = true;\n            parseJson();\n        }\n        return jsonPtr_;\n    }\n\n    const std::string &getJsonError() const override\n    {\n        static const std::string none;\n        if (jsonParsingErrorPtr_)\n            return *jsonParsingErrorPtr_;\n        return none;\n    }\n\n    void setJsonObject(const Json::Value &pJson)\n    {\n        flagForParsingJson_ = true;\n        flagForSerializingJson_ = false;\n        jsonPtr_ = std::make_shared<Json::Value>(pJson);\n    }\n\n    void setJsonObject(Json::Value &&pJson)\n    {\n        flagForParsingJson_ = true;\n        flagForSerializingJson_ = false;\n        jsonPtr_ = std::make_shared<Json::Value>(std::move(pJson));\n    }\n\n    bool shouldBeCompressed() const;\n    void generateBodyFromJson() const;\n\n    const std::string &sendfileName() const override\n    {\n        return sendfileName_;\n    }\n\n    const SendfileRange &sendfileRange() const override\n    {\n        return sendfileRange_;\n    }\n\n    const trantor::CertificatePtr &peerCertificate() const override\n    {\n        return peerCertificate_;\n    }\n\n    void setPeerCertificate(const trantor::CertificatePtr &cert)\n    {\n        peerCertificate_ = cert;\n    }\n\n    void setSendfile(const std::string &filename)\n    {\n        sendfileName_ = filename;\n    }\n\n    void setSendfileRange(size_t offset, size_t len)\n    {\n        sendfileRange_.first = offset;\n        sendfileRange_.second = len;\n    }\n\n    const std::function<std::size_t(char *, std::size_t)> &streamCallback()\n        const override\n    {\n        return streamCallback_;\n    }\n\n    void setStreamCallback(\n        const std::function<std::size_t(char *, std::size_t)> &callback)\n    {\n        streamCallback_ = callback;\n    }\n\n    const std::function<void(ResponseStreamPtr)> &asyncStreamCallback()\n        const override\n    {\n        return asyncStreamCallback_;\n    }\n\n    void setAsyncStreamCallback(\n        const std::function<void(ResponseStreamPtr)> &callback,\n        bool disableKickoffTimeout)\n    {\n        asyncStreamCallback_ = callback;\n        asyncStreamDisableKickoff_ = disableKickoffTimeout;\n    }\n\n    bool asyncStreamKickoffDisabled() const\n    {\n        return asyncStreamDisableKickoff_;\n    }\n\n    void makeHeaderString()\n    {\n        fullHeaderString_ = std::make_shared<trantor::MsgBuffer>(128);\n        makeHeaderString(*fullHeaderString_);\n    }\n\n    std::string contentTypeString() const override\n    {\n        parseContentTypeAndString();\n        return contentTypeString_;\n    }\n\n    void gunzip()\n    {\n        if (bodyPtr_)\n        {\n            auto gunzipBody =\n                utils::gzipDecompress(bodyPtr_->data(), bodyPtr_->length());\n            removeHeaderBy(\"content-encoding\");\n            bodyPtr_ =\n                std::make_shared<HttpMessageStringBody>(std::move(gunzipBody));\n            addHeader(\"content-length\", std::to_string(bodyPtr_->length()));\n        }\n    }\n\n    bool contentLengthIsAllowed() const\n    {\n        int statusCode =\n            customStatusCode_ >= 0 ? customStatusCode_ : statusCode_;\n\n        // return false if status code is 1xx or 204\n        return (statusCode >= k200OK || statusCode < k100Continue) &&\n               statusCode != k204NoContent;\n    }\n#ifdef USE_BROTLI\n    void brDecompress()\n    {\n        if (bodyPtr_)\n        {\n            auto gunzipBody =\n                utils::brotliDecompress(bodyPtr_->data(), bodyPtr_->length());\n            removeHeaderBy(\"content-encoding\");\n            bodyPtr_ =\n                std::make_shared<HttpMessageStringBody>(std::move(gunzipBody));\n            addHeader(\"content-length\", std::to_string(bodyPtr_->length()));\n        }\n    }\n#endif\n    ~HttpResponseImpl() override = default;\n\n  protected:\n    void makeHeaderString(trantor::MsgBuffer &headerString);\n\n    void parseContentTypeAndString() const\n    {\n        if (!flagForParsingContentType_)\n        {\n            flagForParsingContentType_ = true;\n            auto &contentTypeString = getHeaderBy(\"content-type\");\n            if (contentTypeString == \"\")\n            {\n                contentType_ = CT_NONE;\n            }\n            else\n            {\n                auto pos = contentTypeString.find(';');\n                if (pos != std::string::npos)\n                {\n                    contentType_ = parseContentType(\n                        std::string_view(contentTypeString.data(), pos));\n                }\n                else\n                {\n                    contentType_ =\n                        parseContentType(std::string_view(contentTypeString));\n                }\n\n                if (contentType_ == CT_NONE)\n                    contentType_ = CT_CUSTOM;\n                contentTypeString_ = contentTypeString;\n            }\n        }\n    }\n\n  private:\n    bool allowCompression_{true};\n\n    void setAllowCompression(bool allow) override;\n\n    bool allowCompression() const override;\n\n    void setBody(const char *body, size_t len) override\n    {\n        bodyPtr_ = std::make_shared<HttpMessageStringViewBody>(body, len);\n        if (passThrough_)\n        {\n            addHeader(\"content-length\", std::to_string(bodyPtr_->length()));\n        }\n    }\n\n    void setContentTypeCodeAndCustomString(ContentType type,\n                                           const char *typeString,\n                                           size_t typeStringLength) override\n    {\n        contentType_ = type;\n        flagForParsingContentType_ = true;\n\n        std::string_view sv(typeString, typeStringLength);\n        bool haveHeader = sv.find(\"content-type: \") == 0;\n        bool haveCRLF = sv.rfind(\"\\r\\n\") == sv.size() - 2;\n\n        size_t endOffset = 0;\n        if (haveHeader)\n            endOffset += 14;\n        if (haveCRLF)\n            endOffset += 2;\n        setContentType(std::string_view{typeString + (haveHeader ? 14 : 0),\n                                        typeStringLength - endOffset});\n    }\n\n    void setContentTypeString(const char *typeString,\n                              size_t typeStringLength) override;\n\n    void setCustomStatusCode(int code,\n                             const char *message,\n                             size_t messageLength) override\n    {\n        assert(code >= 0);\n        customStatusCode_ = code;\n        statusMessage_ = std::string_view{message, messageLength};\n    }\n\n    SafeStringMap<std::string> headers_;\n    SafeStringMap<Cookie> cookies_;\n\n    int customStatusCode_{-1};\n    HttpStatusCode statusCode_{kUnknown};\n    std::string_view statusMessage_;\n\n    trantor::Date creationDate_;\n    Version version_{Version::kHttp11};\n    bool closeConnection_{false};\n    mutable std::shared_ptr<HttpMessageBody> bodyPtr_;\n    ssize_t expriedTime_{-1};\n    std::string sendfileName_;\n    SendfileRange sendfileRange_{0, 0};\n    std::function<std::size_t(char *, std::size_t)> streamCallback_;\n    std::function<void(ResponseStreamPtr)> asyncStreamCallback_;\n    bool asyncStreamDisableKickoff_{false};\n\n    mutable std::shared_ptr<Json::Value> jsonPtr_;\n\n    std::shared_ptr<trantor::MsgBuffer> fullHeaderString_;\n    trantor::CertificatePtr peerCertificate_;\n    mutable std::shared_ptr<trantor::MsgBuffer> httpString_;\n    mutable size_t datePos_{static_cast<size_t>(-1)};\n    mutable int64_t httpStringDate_{-1};\n    mutable bool flagForParsingJson_{false};\n    mutable bool flagForSerializingJson_{true};\n    mutable ContentType contentType_{CT_TEXT_PLAIN};\n    mutable bool flagForParsingContentType_{false};\n    mutable std::shared_ptr<std::string> jsonParsingErrorPtr_;\n    mutable std::string contentTypeString_{\"text/html; charset=utf-8\"};\n    bool passThrough_{false};\n\n    void setContentType(const std::string_view &contentType)\n    {\n        contentTypeString_ =\n            std::string(contentType.data(), contentType.size());\n    }\n\n    void setStatusMessage(const std::string_view &message)\n    {\n        statusMessage_ = message;\n    }\n};\n\nusing HttpResponseImplPtr = std::shared_ptr<HttpResponseImpl>;\n\ninline void swap(HttpResponseImpl &one, HttpResponseImpl &two) noexcept\n{\n    one.swap(two);\n}\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpResponseParser.cc",
    "content": "/**\n *\n *  @file HttpResponseParser.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpResponseParser.h\"\n#include \"HttpResponseImpl.h\"\n#include <trantor/utils/Logger.h>\n#include <trantor/utils/MsgBuffer.h>\n#include <algorithm>\n\nusing namespace trantor;\nusing namespace drogon;\n\nvoid HttpResponseParser::reset()\n{\n    status_ = HttpResponseParseStatus::kExpectResponseLine;\n    responsePtr_.reset(new HttpResponseImpl);\n    parseResponseForHeadMethod_ = false;\n    leftBodyLength_ = 0;\n    currentChunkLength_ = 0;\n}\n\nHttpResponseParser::HttpResponseParser(const trantor::TcpConnectionPtr &connPtr)\n    : status_(HttpResponseParseStatus::kExpectResponseLine),\n      responsePtr_(new HttpResponseImpl),\n      conn_(connPtr)\n{\n}\n\nbool HttpResponseParser::processResponseLine(const char *begin, const char *end)\n{\n    const char *start = begin;\n    const char *space = std::find(start, end, ' ');\n    if (space != end)\n    {\n        LOG_TRACE << *(space - 1);\n        if (*(space - 1) == '1')\n        {\n            responsePtr_->setVersion(Version::kHttp11);\n        }\n        else if (*(space - 1) == '0')\n        {\n            responsePtr_->setVersion(Version::kHttp10);\n        }\n        else\n        {\n            return false;\n        }\n    }\n\n    start = space + 1;\n    space = std::find(start, end, ' ');\n    if (space != end)\n    {\n        std::string status_code(start, space - start);\n        std::string status_message(space + 1, end - space - 1);\n        LOG_TRACE << status_code << \" \" << status_message;\n        auto code = atoi(status_code.c_str());\n        responsePtr_->setStatusCode(HttpStatusCode(code));\n\n        return true;\n    }\n    return false;\n}\n\nbool HttpResponseParser::parseResponseOnClose()\n{\n    if (status_ == HttpResponseParseStatus::kExpectClose)\n    {\n        status_ = HttpResponseParseStatus::kGotAll;\n        return true;\n    }\n    return false;\n}\n\n// return false if any error\nbool HttpResponseParser::parseResponse(MsgBuffer *buf)\n{\n    bool ok = true;\n    bool hasMore = true;\n    while (hasMore)\n    {\n        if (status_ == HttpResponseParseStatus::kExpectResponseLine)\n        {\n            const char *crlf = buf->findCRLF();\n            if (crlf)\n            {\n                ok = processResponseLine(buf->peek(), crlf);\n                if (ok)\n                {\n                    // responsePtr_->setReceiveTime(receiveTime);\n                    buf->retrieveUntil(crlf + 2);\n                    status_ = HttpResponseParseStatus::kExpectHeaders;\n                }\n                else\n                {\n                    hasMore = false;\n                }\n            }\n            else\n            {\n                hasMore = false;\n            }\n        }\n        else if (status_ == HttpResponseParseStatus::kExpectHeaders)\n        {\n            const char *crlf = buf->findCRLF();\n            if (crlf)\n            {\n                const char *colon = std::find(buf->peek(), crlf, ':');\n                if (colon != crlf)\n                {\n                    responsePtr_->addHeader(buf->peek(), colon, crlf);\n                }\n                else\n                {\n                    const std::string &len =\n                        responsePtr_->getHeaderBy(\"content-length\");\n                    // LOG_INFO << \"content len=\" << len;\n                    if (!len.empty())\n                    {\n                        leftBodyLength_ = static_cast<size_t>(std::stoull(len));\n                        status_ = HttpResponseParseStatus::kExpectBody;\n                    }\n                    else\n                    {\n                        const std::string &encode =\n                            responsePtr_->getHeaderBy(\"transfer-encoding\");\n                        if (encode == \"chunked\")\n                        {\n                            status_ = HttpResponseParseStatus::kExpectChunkLen;\n                            hasMore = true;\n                        }\n                        else\n                        {\n                            if (responsePtr_->statusCode() == k204NoContent ||\n                                (responsePtr_->statusCode() ==\n                                         k101SwitchingProtocols &&\n                                     [this]() -> bool {\n                                    std::string upgradeValue =\n                                        responsePtr_->getHeaderBy(\"upgrade\");\n                                    std::transform(upgradeValue.begin(),\n                                                   upgradeValue.end(),\n                                                   upgradeValue.begin(),\n                                                   [](unsigned char c) {\n                                                       return tolower(c);\n                                                   });\n                                    return upgradeValue == \"websocket\";\n                                }()))\n                            {\n                                // The Websocket response may not have a\n                                // content-length header.\n                                status_ = HttpResponseParseStatus::kGotAll;\n                                hasMore = false;\n                            }\n                            else\n                            {\n                                status_ = HttpResponseParseStatus::kExpectClose;\n                                auto connPtr = conn_.lock();\n                                connPtr->shutdown();\n                                hasMore = true;\n                            }\n                        }\n                    }\n                    if (parseResponseForHeadMethod_)\n                    {\n                        leftBodyLength_ = 0;\n                        status_ = HttpResponseParseStatus::kGotAll;\n                        hasMore = false;\n                    }\n                }\n                buf->retrieveUntil(crlf + 2);\n            }\n            else\n            {\n                hasMore = false;\n            }\n        }\n        else if (status_ == HttpResponseParseStatus::kExpectBody)\n        {\n            // LOG_INFO << \"expectBody:len=\" << request_->contentLen;\n            // LOG_INFO << \"expectBody:buf=\" << buf;\n            if (buf->readableBytes() == 0)\n            {\n                if (leftBodyLength_ == 0)\n                {\n                    status_ = HttpResponseParseStatus::kGotAll;\n                }\n                break;\n            }\n            if (!responsePtr_->bodyPtr_)\n            {\n                responsePtr_->bodyPtr_ =\n                    std::make_shared<HttpMessageStringBody>();\n            }\n            if (leftBodyLength_ >= buf->readableBytes())\n            {\n                leftBodyLength_ -= buf->readableBytes();\n\n                responsePtr_->bodyPtr_->append(buf->peek(),\n                                               buf->readableBytes());\n                buf->retrieveAll();\n            }\n            else\n            {\n                responsePtr_->bodyPtr_->append(buf->peek(), leftBodyLength_);\n                buf->retrieve(leftBodyLength_);\n                leftBodyLength_ = 0;\n            }\n            if (leftBodyLength_ == 0)\n            {\n                status_ = HttpResponseParseStatus::kGotAll;\n                LOG_TRACE << \"post got all:len=\" << leftBodyLength_;\n                // LOG_INFO<<\"content:\"<<request_->content_;\n                LOG_TRACE << \"content(END)\";\n                hasMore = false;\n            }\n        }\n        else if (status_ == HttpResponseParseStatus::kExpectClose)\n        {\n            if (!responsePtr_->bodyPtr_)\n            {\n                responsePtr_->bodyPtr_ =\n                    std::make_shared<HttpMessageStringBody>();\n            }\n            responsePtr_->bodyPtr_->append(buf->peek(), buf->readableBytes());\n            buf->retrieveAll();\n            break;\n        }\n        else if (status_ == HttpResponseParseStatus::kExpectChunkLen)\n        {\n            const char *crlf = buf->findCRLF();\n            if (crlf)\n            {\n                // chunk length line\n                std::string len(buf->peek(), crlf - buf->peek());\n                char *end;\n                currentChunkLength_ = strtol(len.c_str(), &end, 16);\n                // LOG_TRACE << \"chun length : \" <<\n                // currentChunkLength_;\n                if (currentChunkLength_ != 0)\n                {\n                    status_ = HttpResponseParseStatus::kExpectChunkBody;\n                }\n                else\n                {\n                    status_ = HttpResponseParseStatus::kExpectLastEmptyChunk;\n                }\n                buf->retrieveUntil(crlf + 2);\n            }\n            else\n            {\n                hasMore = false;\n            }\n        }\n        else if (status_ == HttpResponseParseStatus::kExpectChunkBody)\n        {\n            // LOG_TRACE<<\"expect chunk\n            // len=\"<<currentChunkLength_;\n            if (buf->readableBytes() >= (currentChunkLength_ + 2))\n            {\n                if (*(buf->peek() + currentChunkLength_) == '\\r' &&\n                    *(buf->peek() + currentChunkLength_ + 1) == '\\n')\n                {\n                    if (!responsePtr_->bodyPtr_)\n                    {\n                        responsePtr_->bodyPtr_ =\n                            std::make_shared<HttpMessageStringBody>();\n                    }\n                    responsePtr_->bodyPtr_->append(buf->peek(),\n                                                   currentChunkLength_);\n                    buf->retrieve(currentChunkLength_ + 2);\n                    currentChunkLength_ = 0;\n                    status_ = HttpResponseParseStatus::kExpectChunkLen;\n                }\n                else\n                {\n                    // error!\n                    buf->retrieveAll();\n                    return false;\n                }\n            }\n            else\n            {\n                hasMore = false;\n            }\n        }\n        else if (status_ == HttpResponseParseStatus::kExpectLastEmptyChunk)\n        {\n            // last empty chunk\n            const char *crlf = buf->findCRLF();\n            if (crlf)\n            {\n                buf->retrieveUntil(crlf + 2);\n                status_ = HttpResponseParseStatus::kGotAll;\n                responsePtr_->addHeader(\"content-length\",\n                                        std::to_string(\n                                            responsePtr_->getBody().length()));\n                responsePtr_->removeHeaderBy(\"transfer-encoding\");\n                break;\n            }\n            else\n            {\n                hasMore = false;\n            }\n        }\n    }\n    return ok;\n}\n"
  },
  {
    "path": "lib/src/HttpResponseParser.h",
    "content": "/**\n *\n *  @file HttpResponseParser.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"impl_forwards.h\"\n#include <trantor/utils/NonCopyable.h>\n#include <trantor/net/TcpConnection.h>\n#include <trantor/utils/MsgBuffer.h>\n#include <list>\n#include <mutex>\n\nnamespace drogon\n{\nclass HttpResponseParser : public trantor::NonCopyable\n{\n  public:\n    enum class HttpResponseParseStatus\n    {\n        kExpectResponseLine,\n        kExpectHeaders,\n        kExpectBody,\n        kExpectChunkLen,\n        kExpectChunkBody,\n        kExpectLastEmptyChunk,\n        kExpectClose,\n        kGotAll,\n    };\n\n    explicit HttpResponseParser(const trantor::TcpConnectionPtr &connPtr);\n\n    // default copy-ctor, dtor and assignment are fine\n\n    // return false if any error\n    bool parseResponse(trantor::MsgBuffer *buf);\n    bool parseResponseOnClose();\n\n    bool gotAll() const\n    {\n        return status_ == HttpResponseParseStatus::kGotAll;\n    }\n\n    void setForHeadMethod()\n    {\n        parseResponseForHeadMethod_ = true;\n    }\n\n    void reset();\n\n    const HttpResponseImplPtr &responseImpl() const\n    {\n        return responsePtr_;\n    }\n\n  private:\n    bool processResponseLine(const char *begin, const char *end);\n\n    HttpResponseParseStatus status_;\n    HttpResponseImplPtr responsePtr_;\n    bool parseResponseForHeadMethod_{false};\n    size_t leftBodyLength_{0};\n    size_t currentChunkLength_{0};\n    std::weak_ptr<trantor::TcpConnection> conn_;\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpServer.cc",
    "content": "/**\n *\n *  @file HttpServer.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpServer.h\"\n#include <drogon/HttpResponse.h>\n#include <drogon/utils/Utilities.h>\n#include <trantor/utils/Logger.h>\n#include <functional>\n#include <memory>\n#include <utility>\n#include \"AOPAdvice.h\"\n#include \"MiddlewaresFunction.h\"\n#include \"HttpAppFrameworkImpl.h\"\n#include \"HttpConnectionLimit.h\"\n#include \"HttpControllerBinder.h\"\n#include \"HttpRequestImpl.h\"\n#include \"HttpRequestParser.h\"\n#include \"HttpResponseImpl.h\"\n#include \"HttpControllersRouter.h\"\n#include \"StaticFileRouter.h\"\n#include \"WebSocketConnectionImpl.h\"\n#include \"impl_forwards.h\"\n\n#if COZ_PROFILING\n#include <coz.h>\n#else\n#define COZ_PROGRESS\n#define COZ_PROGRESS_NAMED(name)\n#define COZ_BEGIN(name)\n#define COZ_END(name)\n#endif\n\nusing namespace std::placeholders;\nusing namespace drogon;\nusing namespace trantor;\n\nstatic inline bool isWebSocket(const HttpRequestImplPtr &req);\nstatic inline HttpResponsePtr tryDecompressRequest(\n    const HttpRequestImplPtr &req);\nstatic inline bool passSyncAdvices(\n    const HttpRequestImplPtr &req,\n    const std::shared_ptr<HttpRequestParser> &requestParser,\n    bool shouldBePipelined,\n    bool isHeadMethod);\nstatic inline HttpResponsePtr getCompressedResponse(\n    const HttpRequestImplPtr &req,\n    const HttpResponsePtr &response,\n    bool isHeadMethod);\n\nstatic void handleInvalidHttpMethod(\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback);\n\nstatic void handleHttpOptions(\n    const HttpRequestImplPtr &req,\n    const std::string &allowMethods,\n    std::function<void(const HttpResponsePtr &)> &&callback);\n\nHttpServer::HttpServer(EventLoop *loop,\n                       const InetAddress &listenAddr,\n                       std::string name)\n#ifdef __linux__\n    : server_(loop, listenAddr, std::move(name))\n#else\n    : server_(loop, listenAddr, std::move(name), true, app().reusePort())\n#endif\n{\n    server_.setConnectionCallback(\n        [this](const trantor::TcpConnectionPtr &conn) {\n            onConnection(conn);\n            if (connectionCallback_)\n                connectionCallback_(conn);\n        });\n    server_.setRecvMessageCallback(onMessage);\n    server_.kickoffIdleConnections(\n        HttpAppFrameworkImpl::instance().getIdleConnectionTimeout());\n}\n\nHttpServer::~HttpServer() = default;\n\nvoid HttpServer::start()\n{\n    if (beforeListenSetSockOptCallback_)\n    {\n        server_.setBeforeListenSockOptCallback(beforeListenSetSockOptCallback_);\n    }\n    if (afterAcceptSetSockOptCallback_)\n    {\n        server_.setAfterAcceptSockOptCallback(afterAcceptSetSockOptCallback_);\n    }\n    LOG_TRACE << \"HttpServer[\" << server_.name() << \"] starts listening on \"\n              << server_.ipPort();\n    server_.start();\n}\n\nvoid HttpServer::stop()\n{\n    server_.stop();\n}\n\nvoid HttpServer::onConnection(const TcpConnectionPtr &conn)\n{\n    if (conn->connected())\n    {\n        auto parser = std::make_shared<HttpRequestParser>(conn);\n        parser->reset();\n        conn->setContext(parser);\n        if (!HttpConnectionLimit::instance().tryAddConnection(conn))\n        {\n            LOG_ERROR << \"too much connections!force close!\";\n            conn->forceClose();\n            return;\n        }\n        if (!AopAdvice::instance().passNewConnectionAdvices(conn))\n        {\n            conn->forceClose();\n        }\n    }\n    else if (conn->disconnected())\n    {\n        LOG_TRACE << \"conn disconnected!\";\n        HttpConnectionLimit::instance().releaseConnection(conn);\n        auto requestParser = conn->getContext<HttpRequestParser>();\n        if (requestParser)\n        {\n            if (requestParser->webSocketConn())\n            {\n                requestParser->webSocketConn()->onClose();\n            }\n            else if (requestParser->requestImpl()->isStreamMode())\n            {\n                requestParser->requestImpl()->streamError(\n                    std::make_exception_ptr(\n                        StreamError(StreamErrorCode::kConnectionBroken,\n                                    \"Connection closed\")));\n            }\n            conn->clearContext();\n        }\n    }\n}\n\nvoid HttpServer::onMessage(const TcpConnectionPtr &conn, MsgBuffer *buf)\n{\n    if (!conn->hasContext())\n        return;\n    auto requestParser = conn->getContext<HttpRequestParser>();\n    if (!requestParser)\n        return;\n    if (requestParser->webSocketConn())\n    {\n        // Websocket payload\n        requestParser->webSocketConn()->onNewMessage(conn, buf);\n        return;\n    }\n\n    auto &requests = requestParser->getRequestBuffer();\n    // With the pipelining feature or web socket, it is possible to receive\n    // multiple messages at once, so the while loop is necessary\n    while (buf->readableBytes() > 0)\n    {\n        if (requestParser->isStop())\n        {\n            // The number of requests has reached the limit.\n            buf->retrieveAll();\n            return;\n        }\n\n        auto &req = requestParser->requestImpl();\n        // if stream mode enabled, parseRequest() may return >0 multiple times\n        // for the same request\n        int parseRes = requestParser->parseRequest(buf);\n        if (parseRes < 0)\n        {\n            if (req->isStreamMode() && req->isProcessingStarted())\n            {\n                // After entering stream mode, if request matches a non-stream\n                // handler, stream error would be intercepted by the\n                // `waitForStreamFinish()` call.\n                // If request matches a stream handler, stream error should be\n                // captured by user provided StreamReader, and response should\n                // also be sent by user.\n                req->streamError(std::make_exception_ptr(\n                    StreamError(StreamErrorCode::kBadRequest, \"Bad request\")));\n            }\n            else if (parseRes != -1)\n            {\n                // In non-stream mode, request won't be process until it's fully\n                // parsed. To keep the old behavior, we send response directly\n                // through conn. (This response won't go through pre-sending\n                // aop, maybe we should change this behavior).\n                auto code = static_cast<HttpStatusCode>(-parseRes);\n                conn->send(utils::formattedString(\n                    \"HTTP/1.1 %d %s\\r\\nConnection: close\\r\\n\\r\\n\",\n                    code,\n                    statusCodeToString(code).data()));\n            }\n            buf->retrieveAll();\n            // NOTE: should we call conn->forceClose() instead?\n            // Calling shutdown() handles socket more elegantly.\n            conn->shutdown();\n            // We have to call clearContext() here in order to ignore following\n            // illegal data from client\n            conn->clearContext();\n            requestParser->reset();\n            return;\n        }\n        if (parseRes == 0)\n        {\n            break;\n        }\n        if (parseRes >= 2 || parseRes == 1 && !req->isStreamMode())\n        {\n            req->setPeerAddr(conn->peerAddr());\n            req->setLocalAddr(conn->localAddr());\n            req->setCreationDate(trantor::Date::date());\n            req->setSecure(conn->isSSLConnection());\n            req->setPeerCertificate(conn->peerCertificate());\n            req->setConnectionPtr(conn);\n            // TODO: maybe call onRequests() directly in stream mode\n            requests.push_back(req);\n        }\n        if (parseRes == 1 || parseRes == 2)\n        {\n            assert(requestParser->gotAll());\n            if (req->isStreamMode())\n            {\n                req->streamFinish();\n            }\n            requestParser->reset();\n        }\n    }\n    if (!requests.empty())\n    {\n        onRequests(conn, requests, requestParser);\n        requests.clear();\n    }\n}\n\nstruct CallbackParamPack\n{\n    CallbackParamPack(trantor::TcpConnectionPtr conn,\n                      HttpRequestImplPtr req,\n                      std::shared_ptr<bool> loopFlag,\n                      std::shared_ptr<HttpRequestParser> requestParser,\n                      bool isHeadMethod)\n        : conn_(std::move(conn)),\n          req_(std::move(req)),\n          loopFlag_(std::move(loopFlag)),\n          requestParser_(std::move(requestParser)),\n          isHeadMethod_(isHeadMethod)\n    {\n    }\n\n    trantor::TcpConnectionPtr conn_;\n    HttpRequestImplPtr req_;\n    std::shared_ptr<bool> loopFlag_;\n    std::shared_ptr<HttpRequestParser> requestParser_;\n    bool isHeadMethod_;\n    std::atomic<bool> responseSent_{false};\n};\n\nvoid HttpServer::onRequests(\n    const TcpConnectionPtr &conn,\n    const std::vector<HttpRequestImplPtr> &requests,\n    const std::shared_ptr<HttpRequestParser> &requestParser)\n{\n    assert(!requests.empty());\n\n    // will only be checked for the first request\n    if (requestParser->firstReq() && requests.size() == 1 &&\n        isWebSocket(requests[0]))\n    {\n        auto &req = requests[0];\n        req->startProcessing();\n        if (passSyncAdvices(req,\n                            requestParser,\n                            false /* Not pipelined */,\n                            false /* Not HEAD */))\n        {\n            auto wsConn = std::make_shared<WebSocketConnectionImpl>(conn);\n            wsConn->setPingMessage(\"\", std::chrono::seconds{30});\n            onWebsocketRequest(\n                req,\n                [conn, wsConn, requestParser, req](\n                    const HttpResponsePtr &resp0) mutable {\n                    if (conn->connected())\n                    {\n                        auto resp = HttpAppFrameworkImpl::instance()\n                                        .handleSessionForResponse(req, resp0);\n                        AopAdvice::instance().passPreSendingAdvices(req, resp);\n                        if (resp->statusCode() == k101SwitchingProtocols)\n                        {\n                            requestParser->setWebsockConnection(wsConn);\n                        }\n                        auto httpString =\n                            ((HttpResponseImpl *)resp.get())->renderToBuffer();\n                        conn->send(httpString);\n                        COZ_PROGRESS\n                    }\n                },\n                std::move(wsConn));\n            return;\n        }\n\n        // flush response for not passing sync advice\n        if (conn->connected() && !requestParser->getResponseBuffer().empty())\n        {\n            sendResponses(conn,\n                          requestParser->getResponseBuffer(),\n                          requestParser->getBuffer());\n            requestParser->getResponseBuffer().clear();\n        }\n        return;\n    }\n\n    if (HttpAppFrameworkImpl::instance().keepaliveRequestsNumber() > 0 &&\n        requestParser->numberOfRequestsParsed() >=\n            HttpAppFrameworkImpl::instance().keepaliveRequestsNumber())\n    {\n        requestParser->stop();\n        conn->shutdown();\n        return;\n    }\n    if (HttpAppFrameworkImpl::instance().pipeliningRequestsNumber() > 0 &&\n        requestParser->numberOfRequestsInPipelining() + requests.size() >=\n            HttpAppFrameworkImpl::instance().pipeliningRequestsNumber())\n    {\n        requestParser->stop();\n        conn->shutdown();\n        return;\n    }\n    if (!conn->connected())\n    {\n        return;\n    }\n    auto loopFlagPtr = std::make_shared<bool>(true);\n\n    for (auto &req : requests)\n    {\n        req->startProcessing();\n        bool isHeadMethod = (req->method() == Head);\n        if (isHeadMethod)\n        {\n            req->setMethod(Get);\n        }\n        bool reqPipelined = false;\n        if (!requestParser->emptyPipelining())\n        {\n            requestParser->pushRequestToPipelining(req, isHeadMethod);\n            reqPipelined = true;\n        }\n        if (!passSyncAdvices(req, requestParser, reqPipelined, isHeadMethod))\n        {\n            continue;\n        }\n\n        // Optimization: Avoids dynamic allocation when copying the callback in\n        // handlers (ex: copying callback into lambda captures in DB calls)\n        bool respReady{false};\n        auto paramPack = std::make_shared<CallbackParamPack>(\n            conn, req, loopFlagPtr, requestParser, isHeadMethod);\n\n        auto errResp = tryDecompressRequest(req);\n        if (errResp)\n        {\n            handleResponse(errResp, paramPack, &respReady);\n        }\n        else\n        {\n            // `handleResponse()` callback may be called synchronously. In this\n            // case, the generated response should not be sent right away, but\n            // be queued in buffer instead. Those ready responses will be sent\n            // together after the end of the for loop.\n            //\n            // By doing this, we could reduce some system calls when sending\n            // through socket. In order to achieve this, we create a\n            // `respReady` variable.\n            onHttpRequest(req,\n                          [respReadyPtr = &respReady,\n                           paramPack = std::move(paramPack)](\n                              const HttpResponsePtr &response) {\n                              handleResponse(response, paramPack, respReadyPtr);\n                          });\n        }\n        if (!reqPipelined && !respReady)\n        {\n            requestParser->pushRequestToPipelining(req, isHeadMethod);\n        }\n    }\n    *loopFlagPtr = false;\n    if (conn->connected() && !requestParser->getResponseBuffer().empty())\n    {\n        sendResponses(conn,\n                      requestParser->getResponseBuffer(),\n                      requestParser->getBuffer());\n        requestParser->getResponseBuffer().clear();\n    }\n}\n\nvoid HttpServer::onHttpRequest(\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    LOG_TRACE << \"new request:\" << req->peerAddr().toIpPort() << \"->\"\n              << req->localAddr().toIpPort();\n    LOG_TRACE << \"Headers \" << req->methodString() << \" \" << req->path();\n    LOG_TRACE << \"http path=\" << req->path();\n    if (req->method() == Options && (req->path() == \"*\" || req->path() == \"/*\"))\n    {\n        auto resp = HttpResponse::newHttpResponse();\n        resp->setContentTypeCode(ContentType::CT_TEXT_PLAIN);\n        resp->addHeader(\"Allow\", \"GET,HEAD,POST,PUT,DELETE,OPTIONS,PATCH\");\n        resp->setExpiredTime(0);\n        callback(resp);\n        return;\n    }\n\n    // TODO: move session related codes to its own singleton class\n    HttpAppFrameworkImpl::instance().findSessionForRequest(req);\n    // pre-routing aop\n    auto &aop = AopAdvice::instance();\n    aop.passPreRoutingObservers(req);\n    if (!aop.hasPreRoutingAdvices())\n    {\n        httpRequestRouting(req, std::move(callback));\n        return;\n    }\n    aop.passPreRoutingAdvices(req,\n                              [req, callback = std::move(callback)](\n                                  const HttpResponsePtr &resp) mutable {\n                                  if (resp)\n                                  {\n                                      callback(resp);\n                                  }\n                                  else\n                                  {\n                                      httpRequestRouting(req,\n                                                         std::move(callback));\n                                  }\n                              });\n}\n\nvoid HttpServer::httpRequestRouting(\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    // How to access router here?? Make router class singleton?\n    RouteResult result = HttpControllersRouter::instance().route(req);\n    if (result.result == RouteResult::Success)\n    {\n        HttpRequestParamPack pack{std::move(result.binderPtr),\n                                  std::move(callback)};\n        requestPostRouting(req, std::move(pack));\n        return;\n    }\n    if (result.result == RouteResult::MethodNotAllowed)\n    {\n        handleInvalidHttpMethod(req, std::move(callback));\n        return;\n    }\n\n    // Fallback to static file router\n    // TODO: make this router a plugin\n    if (req->path() == \"/\" &&\n        !HttpAppFrameworkImpl::instance().getHomePage().empty())\n    {\n        req->setPath(\"/\" + HttpAppFrameworkImpl::instance().getHomePage());\n    }\n    StaticFileRouter::instance().route(req, std::move(callback));\n}\n\ntemplate <typename Pack>\nvoid HttpServer::requestPostRouting(const HttpRequestImplPtr &req, Pack &&pack)\n{\n    // Handle stream mode for non-stream handlers\n    if (req->streamStatus() >= ReqStreamStatus::Open &&\n        !pack.binderPtr->isStreamHandler())\n    {\n        LOG_TRACE << \"Wait for request stream finish\";\n        if (req->streamStatus() == ReqStreamStatus::Finish)\n        {\n            req->quitStreamMode();\n        }\n        else\n        {\n            auto contentLength = req->getContentLengthHeaderValue();\n            if (contentLength.has_value())\n            {\n                req->reserveBodySize(contentLength.value());\n            }\n            req->waitForStreamFinish([weakReq = std::weak_ptr(req),\n                                      pack =\n                                          std::forward<Pack>(pack)]() mutable {\n                auto req = weakReq.lock();\n                if (!req)\n                    return;\n                if (req->streamStatus() == ReqStreamStatus::Finish)\n                {\n                    req->quitStreamMode();\n                    // call requestPostRouting again\n                    requestPostRouting(req, std::forward<Pack>(pack));\n                    return;\n                }\n                else\n                {\n                    req->quitStreamMode();\n                    LOG_ERROR << \"Stop processing request due to stream error\";\n                    pack.callback(\n                        app().getCustomErrorHandler()(k400BadRequest, req));\n                }\n            });\n            return;\n        }\n    }\n\n    // post-routing aop\n    auto &aop = AopAdvice::instance();\n    aop.passPostRoutingObservers(req);\n    if (!aop.hasPostRoutingAdvices())\n    {\n        requestPassMiddlewares(req, std::forward<Pack>(pack));\n        return;\n    }\n    aop.passPostRoutingAdvices(req,\n                               [req, pack = std::forward<Pack>(pack)](\n                                   const HttpResponsePtr &resp) mutable {\n                                   if (resp)\n                                   {\n                                       pack.callback(resp);\n                                   }\n                                   else\n                                   {\n                                       requestPassMiddlewares(req,\n                                                              std::move(pack));\n                                   }\n                               });\n}\n\ntemplate <typename Pack>\nvoid HttpServer::requestPassMiddlewares(const HttpRequestImplPtr &req,\n                                        Pack &&pack)\n{\n    // pass middlewares\n    auto &middlewares = pack.binderPtr->middlewares_;\n    if (middlewares.empty())\n    {\n        requestPreHandling(req, std::forward<Pack>(pack));\n        return;\n    }\n\n    auto callback = std::move(pack.callback);\n    pack.callback = nullptr;\n    middlewares_function::passMiddlewares(\n        middlewares,\n        req,\n        std::move(callback),\n        [req, pack = std::forward<Pack>(pack)](\n            std::function<void(const HttpResponsePtr &)>\n                &&middlewarePostCb) mutable {\n            pack.callback = std::move(middlewarePostCb);\n            requestPreHandling(req, std::forward<Pack>(pack));\n        });\n}\n\ntemplate <typename Pack>\nvoid HttpServer::requestPreHandling(const HttpRequestImplPtr &req, Pack &&pack)\n{\n    // Handle CORS preflight request, except when custom handling is desired\n    if (req->method() == Options)\n    {\n        if (!req->attributes()->get<bool>(\"drogon.customCORShandling\"))\n        {\n            handleHttpOptions(req,\n                              *pack.binderPtr->corsMethods_,\n                              std::move(pack.callback));\n            return;\n        }\n        req->attributes()->insert(\"drogon.corsMethods\",\n                                  *pack.binderPtr->corsMethods_);\n    }\n\n    // pre-handling aop\n    auto &aop = AopAdvice::instance();\n    aop.passPreHandlingObservers(req);\n    if (!aop.hasPreHandlingAdvices())\n    {\n        if constexpr (std::is_same_v<std::decay_t<Pack>, HttpRequestParamPack>)\n        {\n            httpRequestHandling(req,\n                                std::move(pack.binderPtr),\n                                std::move(pack.callback));\n        }\n        else\n        {\n            websocketRequestHandling(req,\n                                     std::move(pack.binderPtr),\n                                     std::move(pack.callback),\n                                     std::move(pack.wsConnPtr));\n        }\n        return;\n    }\n    aop.passPreHandlingAdvices(\n        req,\n        [req,\n         pack = std::forward<Pack>(pack)](const HttpResponsePtr &resp) mutable {\n            if (resp)\n            {\n                pack.callback(resp);\n                return;\n            }\n            if constexpr (std::is_same_v<std::decay_t<Pack>,\n                                         HttpRequestParamPack>)\n            {\n                httpRequestHandling(req,\n                                    std::move(pack.binderPtr),\n                                    std::move(pack.callback));\n            }\n            else\n            {\n                websocketRequestHandling(req,\n                                         std::move(pack.binderPtr),\n                                         std::move(pack.callback),\n                                         std::move(pack.wsConnPtr));\n            }\n        });\n}\n\nvoid HttpServer::httpRequestHandling(\n    const HttpRequestImplPtr &req,\n    std::shared_ptr<ControllerBinderBase> &&binderPtr,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    // Check cached response\n    auto &cachedResp = *(binderPtr->responseCache_);\n    if (cachedResp)\n    {\n        if (cachedResp->expiredTime() == 0 ||\n            (trantor::Date::now() <\n             cachedResp->creationDate().after(\n                 static_cast<double>(cachedResp->expiredTime()))))\n        {\n            // use cached response!\n            LOG_TRACE << \"Use cached response\";\n\n            // post-handling aop\n            AopAdvice::instance().passPostHandlingAdvices(req, cachedResp);\n            callback(cachedResp);\n            return;\n        }\n        else\n        {\n            cachedResp.reset();\n        }\n    }\n\n    auto &binderRef = *binderPtr;\n    binderRef.handleRequest(\n        req,\n        // This is the actual callback being passed to controller\n        [req, binderPtr = std::move(binderPtr), callback = std::move(callback)](\n            const HttpResponsePtr &resp) mutable {\n            // Check if we need to cache the response\n            if (resp->expiredTime() >= 0 && resp->statusCode() != k404NotFound)\n            {\n                static_cast<HttpResponseImpl *>(resp.get())->makeHeaderString();\n                auto loop = req->getLoop();\n                if (loop->isInLoopThread())\n                {\n                    binderPtr->responseCache_.setThreadData(resp);\n                }\n                else\n                {\n                    loop->queueInLoop(\n                        [binderPtr = std::move(binderPtr), resp]() {\n                            binderPtr->responseCache_.setThreadData(resp);\n                        });\n                }\n            }\n            // post-handling aop\n            AopAdvice::instance().passPostHandlingAdvices(req, resp);\n            callback(resp);\n        });\n}\n\nvoid HttpServer::onWebsocketRequest(\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback,\n    WebSocketConnectionImplPtr &&wsConnPtr)\n{\n    HttpAppFrameworkImpl::instance().findSessionForRequest(req);\n    // pre-routing aop\n    auto &aop = AopAdvice::instance();\n    aop.passPreRoutingObservers(req);\n    if (!aop.hasPreRoutingAdvices())\n    {\n        websocketRequestRouting(req, std::move(callback), std::move(wsConnPtr));\n        return;\n    }\n    aop.passPreRoutingAdvices(\n        req,\n        [req, wsConnPtr = std::move(wsConnPtr), callback = std::move(callback)](\n            const HttpResponsePtr &resp) mutable {\n            if (resp)\n            {\n                callback(resp);\n            }\n            else\n            {\n                websocketRequestRouting(req,\n                                        std::move(callback),\n                                        std::move(wsConnPtr));\n            }\n        });\n}\n\nvoid HttpServer::websocketRequestRouting(\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback,\n    WebSocketConnectionImplPtr &&wsConnPtr)\n{\n    RouteResult result = HttpControllersRouter::instance().routeWs(req);\n\n    if (result.result == RouteResult::Success)\n    {\n        WsRequestParamPack pack{std::move(result.binderPtr),\n                                std::move(callback),\n                                std::move(wsConnPtr)};\n        requestPostRouting(req, std::move(pack));\n        return;\n    }\n    if (result.result == RouteResult::MethodNotAllowed)\n    {\n        handleInvalidHttpMethod(req, std::move(callback));\n        return;\n    }\n\n    // Not found\n    auto resp = drogon::HttpResponse::newNotFoundResponse(req);\n    resp->setCloseConnection(true);\n    callback(resp);\n}\n\nvoid HttpServer::websocketRequestHandling(\n    const HttpRequestImplPtr &req,\n    std::shared_ptr<ControllerBinderBase> &&binderPtr,\n    std::function<void(const HttpResponsePtr &)> &&callback,\n    WebSocketConnectionImplPtr &&wsConnPtr)\n{\n    binderPtr->handleRequest(\n        req,\n        [req, callback = std::move(callback)](const HttpResponsePtr &resp) {\n            AopAdvice::instance().passPostHandlingAdvices(req, resp);\n            callback(resp);\n        });\n\n    // TODO: more elegant?\n    static_cast<WebsocketControllerBinder *>(binderPtr.get())\n        ->handleNewConnection(req, wsConnPtr);\n}\n\nvoid HttpServer::handleResponse(\n    const HttpResponsePtr &response,\n    const std::shared_ptr<CallbackParamPack> &paramPack,\n    bool *respReadyPtr)\n{\n    auto &conn = paramPack->conn_;\n    auto &req = paramPack->req_;\n    auto &requestParser = paramPack->requestParser_;\n    auto &loopFlagPtr = paramPack->loopFlag_;\n    const bool isHeadMethod = paramPack->isHeadMethod_;\n\n    if (!response)\n        return;\n    if (!conn->connected())\n        return;\n\n    if (paramPack->responseSent_.exchange(true, std::memory_order_acq_rel))\n    {\n        LOG_ERROR << \"Sending more than 1 response for request. \"\n                     \"Ignoring later response\";\n        return;\n    }\n\n    auto resp =\n        HttpAppFrameworkImpl::instance().handleSessionForResponse(req,\n                                                                  response);\n    resp->setVersion(req->getVersion());\n    resp->setCloseConnection(!req->keepAlive());\n    AopAdvice::instance().passPreSendingAdvices(req, resp);\n\n    auto newResp = getCompressedResponse(req, resp, isHeadMethod);\n    if (conn->getLoop()->isInLoopThread())\n    {\n        /*\n         * A client that supports persistent connections MAY\n         * \"pipeline\" its requests (i.e., send multiple requests\n         * without waiting for each response). A server MUST send\n         * its responses to those requests in the same order that\n         * the requests were received. rfc2616-8.1.1.2\n         */\n        if (requestParser->emptyPipelining())\n        {\n            // response must have arrived synchronously\n            assert(*loopFlagPtr);\n            // TODO: change to weakPtr to be sure. But may drop performance.\n            *respReadyPtr = true;\n            requestParser->getResponseBuffer().emplace_back(std::move(newResp),\n                                                            isHeadMethod);\n            return;\n        }\n        if (requestParser->pushResponseToPipelining(req, std::move(newResp)))\n        {\n            auto &responseBuffer = requestParser->getResponseBuffer();\n            requestParser->popReadyResponses(responseBuffer);\n            if (!*loopFlagPtr)\n            {\n                // We have passed the point where `onRequests()` sends\n                // responses. So, at here we should send ready responses from\n                // the beginning of pipeline queue.\n                sendResponses(conn, responseBuffer, requestParser->getBuffer());\n                responseBuffer.clear();\n            }\n        }\n    }\n    else\n    {\n        conn->getLoop()->queueInLoop(\n            [conn, req, requestParser, newResp = std::move(newResp)]() mutable {\n                if (!conn->connected())\n                {\n                    return;\n                }\n                if (requestParser->pushResponseToPipelining(req,\n                                                            std::move(newResp)))\n                {\n                    std::vector<std::pair<HttpResponsePtr, bool>> responses;\n                    requestParser->popReadyResponses(responses);\n                    sendResponses(conn, responses, requestParser->getBuffer());\n                }\n            });\n    }\n}\n\nstruct ChunkingParams\n{\n    using DataCallback = std::function<std::size_t(char *, std::size_t)>;\n\n    explicit ChunkingParams(DataCallback cb) : dataCallback(std::move(cb))\n    {\n    }\n\n    DataCallback dataCallback;\n    bool bFinished{false};\n#ifndef NDEBUG  // defined by CMake for release build\n    std::size_t nDataReturned{0};\n#endif\n};\n\nstatic std::size_t chunkingCallback(\n    const std::shared_ptr<ChunkingParams> &cbParams,\n    char *pBuffer,\n    std::size_t nSize)\n{\n    if (!cbParams)\n        return 0;\n    // Cleanup\n    if (pBuffer == nullptr)\n    {\n        LOG_TRACE << \"Chunking callback cleanup\";\n        if (cbParams && cbParams->dataCallback)\n        {\n            cbParams->dataCallback(pBuffer, nSize);\n            cbParams->dataCallback = {};\n        }\n        return 0;\n    }\n    // Terminal chunk already returned\n    if (cbParams->bFinished)\n    {\n        LOG_TRACE << \"Chunking callback has no more data\";\n#ifndef NDEBUG  // defined by CMake for release build\n        LOG_TRACE << \"Chunking callback: total data returned: \"\n                  << cbParams->nDataReturned << \" bytes\";\n#endif\n        return 0;\n    }\n\n    // Reserve size to prepend the chunk size & append cr/lf, and get data\n    struct\n    {\n        std::size_t operator()(std::size_t n)\n        {\n            return n == 0 ? 0 : 1 + (*this)(n >> 4);\n        }\n    } neededDigits;\n\n    auto nHeaderSize = neededDigits(nSize) + 2;\n    auto nDataSize =\n        cbParams->dataCallback(pBuffer + nHeaderSize, nSize - nHeaderSize - 2);\n    if (nDataSize == 0)\n    {\n        // Terminal chunk + cr/lf\n        cbParams->bFinished = true;\n#ifdef _WIN32\n        memcpy_s(pBuffer, nSize, \"0\\r\\n\\r\\n\", 5);\n#else\n        memcpy(pBuffer, \"0\\r\\n\\r\\n\", 5);\n#endif\n        LOG_TRACE << \"Chunking callback: no more data, return last chunk of \"\n                     \"size 0 & end of message\";\n        return 5;\n    }\n    // Non-terminal chunks\n    pBuffer[nHeaderSize + nDataSize] = '\\r';\n    pBuffer[nHeaderSize + nDataSize + 1] = '\\n';\n    // The spec does not say if the chunk size is allowed tohave leading zeroes\n    // Use a fixed size header with leading zeroes\n    // (tested to work with Chrome, Firefox, Safari, Edge, wget, curl and VLC)\n#ifdef _WIN32\n    char pszFormat[]{\"%04llx\\r\"};\n#else\n    char pszFormat[]{\"%04lx\\r\"};\n#endif\n    pszFormat[2] = '0' + char(nHeaderSize - 2);\n    snprintf(pBuffer, nHeaderSize, pszFormat, nDataSize);\n    pBuffer[nHeaderSize - 1] = '\\n';\n    LOG_TRACE << \"Chunking callback: return chunk of size \" << nDataSize;\n#ifndef NDEBUG  // defined by CMake for release build\n    cbParams->nDataReturned += nDataSize;\n#endif\n    return nHeaderSize + nDataSize + 2;\n    // Alternative code if there are client software that do not support chunk\n    // size with leading zeroes\n    //    auto nHeaderLen =\n    // #ifdef _WIN32\n    //    sprintf_s(pBuffer,\n    //    nHeaderSize, \"%llx\\r\",\n    //    nDataSize);\n    // #else\n    //    sprintf(pBuffer, \"%lx\\r\",\n    //    nDataSize);\n    // #endif\n    //    pBuffer[nHeaderLen++] = '\\n';\n    //    if (nHeaderLen < nHeaderSize)  // smaller that what was reserved ->\n    //    move data\n    // #ifdef _WIN32\n    //    memmove_s(pBuffer +\n    //    nHeaderLen,\n    //              nSize - nHeaderLen,\n    //              pBuffer +\n    //              nHeaderSize,\n    //              nDataSize + 2);\n    // #else\n    //    memmove(pBuffer + nHeaderLen,\n    //            pBuffer + nHeaderSize,\n    //            nDataSize + 2);\n    // #endif\n    //    return nHeaderLen + nDataSize + 2;\n}\n\nvoid HttpServer::sendResponse(const TcpConnectionPtr &conn,\n                              const HttpResponsePtr &response,\n                              bool isHeadMethod)\n{\n    conn->getLoop()->assertInLoopThread();\n    auto respImplPtr = static_cast<HttpResponseImpl *>(response.get());\n    if (!isHeadMethod)\n    {\n        auto httpString = respImplPtr->renderToBuffer();\n        conn->send(httpString);\n        if (!respImplPtr->contentLengthIsAllowed())\n            return;\n        auto &asyncStreamCallback = respImplPtr->asyncStreamCallback();\n        if (asyncStreamCallback)\n        {\n            if (!respImplPtr->ifCloseConnection())\n            {\n                asyncStreamCallback(\n                    std::make_unique<ResponseStream>(conn->sendAsyncStream(\n                        respImplPtr->asyncStreamKickoffDisabled())));\n            }\n            else\n            {\n                LOG_INFO << \"Chunking Set CloseConnection !!!\";\n            }\n        }\n        auto &streamCallback = respImplPtr->streamCallback();\n        const std::string &sendfileName = respImplPtr->sendfileName();\n        if (streamCallback || !sendfileName.empty())\n        {\n            if (streamCallback)\n            {\n                auto &headers = respImplPtr->headers();\n                // When the transfer-encoding is chunked, wrap data callback\n                // chunking callback\n                auto bChunked =\n                    !respImplPtr->ifCloseConnection() &&\n                    (headers.find(\"transfer-encoding\") != headers.end()) &&\n                    (headers.at(\"transfer-encoding\") == \"chunked\");\n                if (bChunked)\n                {\n                    conn->sendStream(\n                        [ctx = std::make_shared<ChunkingParams>(\n                             streamCallback)](char *buffer, size_t len) {\n                            return chunkingCallback(ctx, buffer, len);\n                        });\n                }\n                else\n                    conn->sendStream(streamCallback);\n            }\n            else\n            {\n                const auto &range = respImplPtr->sendfileRange();\n                conn->sendFile(sendfileName.c_str(), range.first, range.second);\n            }\n        }\n        COZ_PROGRESS\n    }\n    else\n    {\n        auto httpString = respImplPtr->renderHeaderForHeadMethod();\n        conn->send(std::move(*httpString));\n        COZ_PROGRESS\n    }\n\n    if (response->ifCloseConnection())\n    {\n        conn->shutdown();\n        COZ_PROGRESS\n    }\n}\n\nvoid HttpServer::sendResponses(\n    const TcpConnectionPtr &conn,\n    const std::vector<std::pair<HttpResponsePtr, bool>> &responses,\n    trantor::MsgBuffer &buffer)\n{\n    conn->getLoop()->assertInLoopThread();\n    if (responses.empty())\n        return;\n    if (responses.size() == 1)\n    {\n        sendResponse(conn, responses[0].first, responses[0].second);\n        return;\n    }\n    for (auto const &resp : responses)\n    {\n        auto respImplPtr = static_cast<HttpResponseImpl *>(resp.first.get());\n        if (!resp.second)\n        {\n            // Not HEAD method\n            respImplPtr->renderToBuffer(buffer);\n            if (!respImplPtr->contentLengthIsAllowed())\n                continue;\n            auto &asyncStreamCallback = respImplPtr->asyncStreamCallback();\n            if (asyncStreamCallback)\n            {\n                conn->send(buffer);\n                buffer.retrieveAll();\n                if (!respImplPtr->ifCloseConnection())\n                {\n                    asyncStreamCallback(\n                        std::make_unique<ResponseStream>(conn->sendAsyncStream(\n                            respImplPtr->asyncStreamKickoffDisabled())));\n                }\n                else\n                {\n                    LOG_INFO << \"Chunking Set CloseConnection !!!\";\n                }\n            }\n            auto &streamCallback = respImplPtr->streamCallback();\n            const std::string &sendfileName = respImplPtr->sendfileName();\n            if (streamCallback || !sendfileName.empty())\n            {\n                conn->send(buffer);\n                buffer.retrieveAll();\n                if (streamCallback)\n                {\n                    auto &headers = respImplPtr->headers();\n                    // When the transfer-encoding is chunked, encapsulate data\n                    // callback in chunking callback\n                    auto bChunked =\n                        !respImplPtr->ifCloseConnection() &&\n                        (headers.find(\"transfer-encoding\") != headers.end()) &&\n                        (headers.at(\"transfer-encoding\") == \"chunked\");\n                    if (bChunked)\n                    {\n                        conn->sendStream(\n                            [ctx = std::make_shared<ChunkingParams>(\n                                 streamCallback)](char *buffer, size_t len) {\n                                return chunkingCallback(ctx, buffer, len);\n                            });\n                    }\n                    else\n                        conn->sendStream(streamCallback);\n                }\n                else\n                {\n                    const auto &range = respImplPtr->sendfileRange();\n                    conn->sendFile(sendfileName.c_str(),\n                                   range.first,\n                                   range.second);\n                }\n                COZ_PROGRESS\n            }\n        }\n        else\n        {\n            auto httpString = respImplPtr->renderHeaderForHeadMethod();\n            buffer.append(httpString->peek(), httpString->readableBytes());\n        }\n        if (respImplPtr->ifCloseConnection())\n        {\n            if (buffer.readableBytes() > 0)\n            {\n                conn->send(buffer);\n                buffer.retrieveAll();\n                COZ_PROGRESS\n            }\n            conn->shutdown();\n            return;\n        }\n    }\n    if (conn->connected() && buffer.readableBytes() > 0)\n    {\n        conn->send(buffer);\n        COZ_PROGRESS\n    }\n    buffer.retrieveAll();\n}\n\nstatic inline bool isWebSocket(const HttpRequestImplPtr &req)\n{\n    if (req->method() != Get)\n        return false;\n\n    auto &headers = req->headers();\n    if (headers.find(\"upgrade\") == headers.end() ||\n        headers.find(\"connection\") == headers.end())\n        return false;\n\n    auto connectionField = req->getHeaderBy(\"connection\");\n    std::transform(connectionField.begin(),\n                   connectionField.end(),\n                   connectionField.begin(),\n                   [](unsigned char c) { return tolower(c); });\n    auto upgradeField = req->getHeaderBy(\"upgrade\");\n    std::transform(upgradeField.begin(),\n                   upgradeField.end(),\n                   upgradeField.begin(),\n                   [](unsigned char c) { return tolower(c); });\n    if (connectionField.find(\"upgrade\") != std::string::npos &&\n        upgradeField == \"websocket\")\n    {\n        LOG_TRACE << \"new websocket request\";\n        return true;\n    }\n    return false;\n}\n\n/**\n * @brief calling req->decompressBody(), if not success, generate corresponding\n * error response\n */\nstatic inline HttpResponsePtr tryDecompressRequest(\n    const HttpRequestImplPtr &req)\n{\n    static const bool enableDecompression = app().isCompressedRequestEnabled();\n    if (!enableDecompression)\n    {\n        return nullptr;\n    }\n    auto status = req->decompressBody();\n    if (status == StreamDecompressStatus::Ok)\n    {\n        return nullptr;\n    }\n    auto resp = HttpResponse::newHttpResponse();\n    switch (status)\n    {\n        case StreamDecompressStatus::TooLarge:\n            resp->setStatusCode(k413RequestEntityTooLarge);\n            break;\n        case StreamDecompressStatus::DecompressError:\n            resp->setStatusCode(k422UnprocessableEntity);\n            break;\n        case StreamDecompressStatus::NotSupported:\n            resp->setStatusCode(k415UnsupportedMediaType);\n            break;\n        case StreamDecompressStatus::Ok:\n            return nullptr;\n    }\n    return resp;\n}\n\n/**\n * @brief Check request against each sync advice, generate response if request\n * is rejected by any one of them.\n *\n * @return true if all sync advice are passed.\n * @return false if rejected by any sync advice.\n */\nstatic inline bool passSyncAdvices(\n    const HttpRequestImplPtr &req,\n    const std::shared_ptr<HttpRequestParser> &requestParser,\n    bool shouldBePipelined,\n    bool isHeadMethod)\n{\n    if (auto resp = AopAdvice::instance().passSyncAdvices(req))\n    {\n        // Rejected by sync advice\n        resp->setVersion(req->getVersion());\n        resp->setCloseConnection(!req->keepAlive());\n        if (!shouldBePipelined)\n        {\n            requestParser->getResponseBuffer().emplace_back(\n                getCompressedResponse(req, resp, isHeadMethod), isHeadMethod);\n        }\n        else\n        {\n            requestParser->pushResponseToPipelining(\n                req, getCompressedResponse(req, resp, isHeadMethod));\n        }\n        return false;\n    }\n    return true;\n}\n\nstatic inline HttpResponsePtr getCompressedResponse(\n    const HttpRequestImplPtr &req,\n    const HttpResponsePtr &response,\n    bool isHeadMethod)\n{\n    if (isHeadMethod ||\n        !static_cast<HttpResponseImpl *>(response.get())->shouldBeCompressed())\n    {\n        return response;\n    }\n#ifdef USE_BROTLI\n    if (app().isBrotliEnabled() &&\n        req->getHeaderBy(\"accept-encoding\").find(\"br\") != std::string::npos)\n    {\n        auto newResp = response;\n        auto strCompress =\n            drogon::utils::brotliCompress(response->getBody().data(),\n                                          response->getBody().length());\n        if (!strCompress.empty())\n        {\n            if (response->expiredTime() >= 0)\n            {\n                // cached response,we need to make a clone\n                newResp = std::make_shared<HttpResponseImpl>(\n                    *static_cast<HttpResponseImpl *>(response.get()));\n                newResp->setExpiredTime(-1);\n            }\n            newResp->setBody(std::move(strCompress));\n            newResp->addHeader(\"Content-Encoding\", \"br\");\n        }\n        else\n        {\n            LOG_ERROR << \"brotli got 0 length result\";\n        }\n        return newResp;\n    }\n#endif\n    if (app().isGzipEnabled() &&\n        req->getHeaderBy(\"accept-encoding\").find(\"gzip\") != std::string::npos)\n    {\n        auto newResp = response;\n        auto strCompress =\n            drogon::utils::gzipCompress(response->getBody().data(),\n                                        response->getBody().length());\n        if (!strCompress.empty())\n        {\n            if (response->expiredTime() >= 0)\n            {\n                // cached response,we need to make a clone\n                newResp = std::make_shared<HttpResponseImpl>(\n                    *static_cast<HttpResponseImpl *>(response.get()));\n                newResp->setExpiredTime(-1);\n            }\n            newResp->setBody(std::move(strCompress));\n            newResp->addHeader(\"Content-Encoding\", \"gzip\");\n        }\n        else\n        {\n            LOG_ERROR << \"gzip got 0 length result\";\n        }\n        return newResp;\n    }\n    return response;\n}\n\nstatic void handleInvalidHttpMethod(\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    if (req->method() != Options)\n    {\n        callback(app().getCustomErrorHandler()(k405MethodNotAllowed, req));\n    }\n    else\n    {\n        callback(app().getCustomErrorHandler()(k403Forbidden, req));\n    }\n}\n\nstatic void handleHttpOptions(\n    const HttpRequestImplPtr &req,\n    const std::string &allowMethods,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setContentTypeCode(ContentType::CT_TEXT_PLAIN);\n    resp->addHeader(\"Allow\", allowMethods);\n\n    auto &orig = req->getHeaderBy(\"origin\");\n    resp->addHeader(\"Access-Control-Allow-Origin\", orig.empty() ? \"*\" : orig);\n    resp->addHeader(\"Access-Control-Allow-Methods\", allowMethods);\n    auto &headers = req->getHeaderBy(\"access-control-request-headers\");\n    if (!headers.empty())\n    {\n        resp->addHeader(\"Access-Control-Allow-Headers\", headers);\n    }\n    callback(resp);\n}\n"
  },
  {
    "path": "lib/src/HttpServer.h",
    "content": "/**\n *\n *  @file HttpServer.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <trantor/net/TcpServer.h>\n#include <trantor/utils/NonCopyable.h>\n#include <functional>\n#include <string>\n#include <vector>\n#include \"impl_forwards.h\"\n\nstruct CallbackParamPack;\n\nnamespace drogon\n{\nstruct ControllerBinderBase;\n\nclass HttpServer : trantor::NonCopyable\n{\n  public:\n    HttpServer(trantor::EventLoop *loop,\n               const trantor::InetAddress &listenAddr,\n               std::string name);\n\n    ~HttpServer();\n\n    void setIoLoops(const std::vector<trantor::EventLoop *> &ioLoops)\n    {\n        server_.setIoLoops(ioLoops);\n    }\n\n    void start();\n    void stop();\n\n    void enableSSL(trantor::TLSPolicyPtr policy)\n    {\n        server_.enableSSL(std::move(policy));\n    }\n\n    void reloadSSL()\n    {\n        server_.reloadSSL();\n    }\n\n    const trantor::InetAddress &address() const\n    {\n        return server_.address();\n    }\n\n    void setBeforeListenSockOptCallback(std::function<void(int)> cb)\n    {\n        beforeListenSetSockOptCallback_ = std::move(cb);\n    }\n\n    void setAfterAcceptSockOptCallback(std::function<void(int)> cb)\n    {\n        afterAcceptSetSockOptCallback_ = std::move(cb);\n    }\n\n    void setConnectionCallback(\n        std::function<void(const trantor::TcpConnectionPtr &)> cb)\n    {\n        connectionCallback_ = std::move(cb);\n    }\n\n  private:\n    friend class HttpInternalForwardHelper;\n\n    static void onConnection(const trantor::TcpConnectionPtr &conn);\n    static void onMessage(const trantor::TcpConnectionPtr &,\n                          trantor::MsgBuffer *);\n    static void onRequests(const trantor::TcpConnectionPtr &,\n                           const std::vector<HttpRequestImplPtr> &,\n                           const std::shared_ptr<HttpRequestParser> &);\n\n    struct HttpRequestParamPack\n    {\n        std::shared_ptr<ControllerBinderBase> binderPtr;\n        std::function<void(const HttpResponsePtr &)> callback;\n    };\n\n    struct WsRequestParamPack\n    {\n        std::shared_ptr<ControllerBinderBase> binderPtr;\n        std::function<void(const HttpResponsePtr &)> callback;\n        WebSocketConnectionImplPtr wsConnPtr;\n    };\n\n    // Http request handling steps\n    static void onHttpRequest(const HttpRequestImplPtr &,\n                              std::function<void(const HttpResponsePtr &)> &&);\n    static void httpRequestRouting(\n        const HttpRequestImplPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback);\n    static void httpRequestHandling(\n        const HttpRequestImplPtr &req,\n        std::shared_ptr<ControllerBinderBase> &&binderPtr,\n        std::function<void(const HttpResponsePtr &)> &&callback);\n\n    // Websocket request handling steps\n    static void onWebsocketRequest(\n        const HttpRequestImplPtr &,\n        std::function<void(const HttpResponsePtr &)> &&,\n        WebSocketConnectionImplPtr &&);\n    static void websocketRequestRouting(\n        const HttpRequestImplPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback,\n        WebSocketConnectionImplPtr &&wsConnPtr);\n    static void websocketRequestHandling(\n        const HttpRequestImplPtr &req,\n        std::shared_ptr<ControllerBinderBase> &&binderPtr,\n        std::function<void(const HttpResponsePtr &)> &&callback,\n        WebSocketConnectionImplPtr &&wsConnPtr);\n\n    // Http/Websocket shared handling steps\n    template <typename Pack>\n    static void requestPostRouting(const HttpRequestImplPtr &req, Pack &&pack);\n    template <typename Pack>\n    static void requestPassMiddlewares(const HttpRequestImplPtr &req,\n                                       Pack &&pack);\n    template <typename Pack>\n    static void requestPreHandling(const HttpRequestImplPtr &req, Pack &&pack);\n\n    // Response buffering and sending\n    static void handleResponse(\n        const HttpResponsePtr &response,\n        const std::shared_ptr<CallbackParamPack> &paramPack,\n        bool *respReadyPtr);\n    static void sendResponse(const trantor::TcpConnectionPtr &,\n                             const HttpResponsePtr &,\n                             bool isHeadMethod);\n    static void sendResponses(\n        const trantor::TcpConnectionPtr &conn,\n        const std::vector<std::pair<HttpResponsePtr, bool>> &responses,\n        trantor::MsgBuffer &buffer);\n\n    trantor::TcpServer server_;\n\n    std::function<void(int)> beforeListenSetSockOptCallback_;\n    std::function<void(int)> afterAcceptSetSockOptCallback_;\n    std::function<void(const trantor::TcpConnectionPtr &)> connectionCallback_;\n};\n\nclass HttpInternalForwardHelper\n{\n  public:\n    static void forward(const HttpRequestImplPtr &req,\n                        std::function<void(const HttpResponsePtr &)> &&callback)\n    {\n        return HttpServer::onHttpRequest(req, std::move(callback));\n    }\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpUtils.cc",
    "content": "/**\n *\n *  @file HttpUtils.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpUtils.h\"\n#include <drogon/utils/Utilities.h>\n#include <trantor/utils/Logger.h>\n#include <map>\n#include <unordered_map>\n#include <mutex>\n\nnamespace drogon\n{\nstatic std::unordered_map<std::string, std::string> customMime;\n\n// https://en.wikipedia.org/wiki/List_of_file_formats\n// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types\n// https://www.digipres.org/formats/mime-types/\n// https://www.iana.org/assignments/media-types/media-types.xhtml\n// content type -> list of corresponding mime types, the first being the default\n// (more standard) one + the mime type to return in contentTypeToMime() when not\n// empty (mainly to return the charset for text types)\nstatic const std::unordered_map<\n    ContentType,\n    std::pair<std::vector<std::string_view>, std::string_view>>\n    mimeTypeDatabase_{\n        {CT_NONE, {{\"\"}, \"\"}},\n        {CT_APPLICATION_OCTET_STREAM, {{\"application/octet-stream\"}, \"\"}},\n        {CT_APPLICATION_X_FORM, {{\"application/x-www-form-urlencoded\"}, \"\"}},\n        {CT_MULTIPART_FORM_DATA, {{\"multipart/form-data\"}, \"\"}},\n        {CT_APPLICATION_GZIP, {{\"application/gzip\"}, \"\"}},\n        {CT_APPLICATION_JSON,\n         {{\"application/json\"}, \"application/json; charset=utf-8\"}},\n        {CT_APPLICATION_FONT_WOFF, {{\"application/font-woff\"}, \"\"}},\n        {CT_APPLICATION_FONT_WOFF2, {{\"application/font-woff2\"}, \"\"}},\n        {CT_APPLICATION_JAVA_ARCHIVE,\n         {{\"application/java-archive\", \"application/x-java-archive\"}, \"\"}},\n        {CT_APPLICATION_MSWORD, {{\"application/msword\"}, \"\"}},\n        {CT_APPLICATION_MSWORDX,\n         {{\"application/\"\n           \"vnd.openxmlformats-officedocument.wordprocessingml.document\"},\n          \"\"}},\n        {CT_APPLICATION_PDF, {{\"application/pdf\"}, \"\"}},\n        {CT_APPLICATION_VND_MS_FONTOBJ,\n         {{\"application/vnd.ms-fontobject\"}, \"\"}},\n        {CT_APPLICATION_VND_RAR, {{\"application/vnd.rar\"}, \"\"}},\n        {CT_APPLICATION_WASM, {{\"application/wasm\"}, \"\"}},\n        {CT_APPLICATION_X_BZIP, {{\"application/x-bzip\"}, \"\"}},\n        {CT_APPLICATION_X_BZIP2, {{\"application/x-bzip2\"}, \"\"}},\n        {CT_APPLICATION_X_7Z, {{\"application/x-7z-compressed\"}, \"\"}},\n        {CT_APPLICATION_X_HTTPD_PHP, {{\"application/x-httpd-php\"}, \"\"}},\n        {CT_APPLICATION_X_JAVASCRIPT,\n         {{\"application/x-javascript\"},\n          \"application/x-javascript; charset=utf-8\"}},\n        {CT_APPLICATION_X_FONT_OPENTYPE,\n         {{\"application/x-font-opentype\", \"font/otf\"}, \"\"}},\n        {CT_APPLICATION_X_FONT_TRUETYPE,\n         {{\"application/x-font-truetype\", \"font/ttf\"}, \"\"}},\n        {CT_APPLICATION_X_TAR, {{\"application/x-tar\"}, \"\"}},\n        {CT_APPLICATION_X_TGZ, {{\"application/x-tgz\"}, \"\"}},\n        {CT_APPLICATION_X_XZ, {{\"application/x-xz\", \"application/x-lzma\"}, \"\"}},\n        {CT_APPLICATION_XHTML,\n         {{\"application/xhtml+xml\", \"application/xhtml\"},\n          \"application/xhtml+xml; charset=utf-8\"}},\n        {CT_APPLICATION_XML,\n         {{\"application/xml\"}, \"application/xml; charset=utf-8\"}},\n        {CT_APPLICATION_ZIP, {{\"application/zip\"}, \"\"}},\n        {CT_AUDIO_AAC, {{\"audio/aac\", \"audio/aacp\"}, \"\"}},\n        {CT_AUDIO_AC3, {{\"audio/ac3\"}, \"\"}},\n        {CT_AUDIO_AIFF, {{\"audio/aiff\", \"audio/x-aiff\"}, \"\"}},\n        {CT_AUDIO_FLAC, {{\"audio/flac\"}, \"\"}},\n        {CT_AUDIO_MATROSKA, {{\"audio/matroska\", \"audio/x-matroska\"}, \"\"}},\n        {CT_AUDIO_MPEG, {{\"audio/mpeg\"}, \"\"}},\n        {CT_AUDIO_MPEG4, {{\"audio/mp4\", \"audio/x-m4a\"}, \"\"}},\n        {CT_AUDIO_OGG, {{\"audio/ogg\"}, \"\"}},\n        {CT_AUDIO_WAVE, {{\"audio/wav\", \"audio/x-wav\"}, \"\"}},\n        {CT_AUDIO_X_APE, {{\"audio/x-ape\"}, \"\"}},\n        {CT_AUDIO_X_MS_WMA, {{\"audio/x-ms-wma\"}, \"\"}},\n        {CT_AUDIO_X_TTA, {{\"audio/x-tta\"}, \"\"}},\n        {CT_AUDIO_X_WAVPACK, {{\"audio/x-wavpack\"}, \"\"}},\n        {CT_AUDIO_WEBM, {{\"audio/webm\"}, \"\"}},\n        {CT_IMAGE_APNG, {{\"image/apng\"}, \"\"}},\n        {CT_IMAGE_AVIF, {{\"image/avif\"}, \"\"}},\n        {CT_IMAGE_BMP, {{\"image/bmp\"}, \"\"}},\n        {CT_IMAGE_GIF, {{\"image/gif\"}, \"\"}},\n        {CT_IMAGE_ICNS, {{\"image/icns\"}, \"\"}},\n        {CT_IMAGE_JP2, {{\"image/jp2\", \"image/jpx\", \"image/jpm\"}, \"\"}},\n        {CT_IMAGE_JPG, {{\"image/jpeg\"}, \"\"}},\n        {CT_IMAGE_PNG, {{\"image/png\"}, \"\"}},\n        {CT_IMAGE_SVG_XML, {{\"image/svg+xml\"}, \"\"}},\n        {CT_IMAGE_TIFF, {{\"image/tiff\"}, \"\"}},\n        {CT_IMAGE_WEBP, {{\"image/webp\"}, \"\"}},\n        {CT_IMAGE_X_MNG, {{\"image/x-mng\"}, \"\"}},\n        {CT_IMAGE_X_TGA, {{\"image/x-tga\", \"image/x-targa\"}, \"\"}},\n        {CT_IMAGE_XICON, {{\"image/vnd.microsoft.icon\", \"image/x-icon\"}, \"\"}},\n        {CT_TEXT_CSS, {{\"text/css\"}, \"text/css; charset=utf-8\"}},\n        {CT_TEXT_CSV, {{\"text/csv\"}, \"text/csv; charset=utf-8\"}},\n        {CT_TEXT_HTML, {{\"text/html\"}, \"text/html; charset=utf-8\"}},\n        {CT_TEXT_JAVASCRIPT,\n         {{\"text/javascript\"}, \"text/javascript; charset=utf-8\"}},\n        {CT_TEXT_PLAIN, {{\"text/plain\"}, \"text/plain; charset=utf-8\"}},\n        {CT_TEXT_XML, {{\"text/xml\"}, \"text/xml; charset=utf-8\"}},\n        {CT_TEXT_XSL, {{\"text/xsl\"}, \"text/xsl; charset=utf-8\"}},\n        {CT_VIDEO_APG, {{\"video/apg\"}, \"\"}},\n        {CT_VIDEO_AV1, {{\"video/av01\", \"video/av1\"}, \"\"}},\n        {CT_VIDEO_QUICKTIME, {{\"video/quicktime\"}, \"\"}},\n        {CT_VIDEO_MPEG, {{\"video/mpeg\"}, \"\"}},\n        {CT_VIDEO_MPEG2TS, {{\"video/mp2t\"}, \"\"}},\n        {CT_VIDEO_MP4, {{\"video/mp4\"}, \"\"}},\n        {CT_VIDEO_OGG, {{\"video/ogg\"}, \"\"}},\n        {CT_VIDEO_WEBM, {{\"video/webm\"}, \"\"}},\n        {CT_VIDEO_X_M4V, {{\"video/x-m4v\"}, \"\"}},\n        {CT_VIDEO_MATROSKA, {{\"video/matroska\", \"video/x-matroska\"}, \"\"}},\n        {CT_VIDEO_X_MSVIDEO, {{\"video/x-msvideo\"}, \"\"}},\n    };\n\nstatic const std::unordered_map<std::string_view,\n                                std::pair<FileType, ContentType>>\n    fileTypeDatabase_{\n        {\"\", {FT_UNKNOWN, CT_CUSTOM}},\n        {\"aac\", {FT_AUDIO, CT_AUDIO_AAC}},\n        {\"ac3\", {FT_AUDIO, CT_AUDIO_AC3}},\n        {\"aif\", {FT_AUDIO, CT_AUDIO_AIFF}},\n        {\"aifc\", {FT_AUDIO, CT_AUDIO_AIFF}},\n        {\"aiff\", {FT_AUDIO, CT_AUDIO_AIFF}},\n        {\"apg\", {FT_AUDIO, CT_VIDEO_APG}},\n        {\"ape\", {FT_AUDIO, CT_AUDIO_X_APE}},\n        {\"apng\", {FT_IMAGE, CT_IMAGE_APNG}},\n        {\"av1\", {FT_MEDIA, CT_VIDEO_AV1}},\n        {\"avi\", {FT_MEDIA, CT_VIDEO_X_MSVIDEO}},\n        {\"avif\", {FT_IMAGE, CT_IMAGE_AVIF}},\n        {\"bmp\", {FT_IMAGE, CT_IMAGE_BMP}},\n        {\"bz\", {FT_ARCHIVE, CT_APPLICATION_X_BZIP}},\n        {\"bz2\", {FT_ARCHIVE, CT_APPLICATION_X_BZIP2}},\n        {\"css\", {FT_DOCUMENT, CT_TEXT_CSS}},\n        {\"csv\", {FT_DOCUMENT, CT_TEXT_CSV}},\n        {\"doc\", {FT_DOCUMENT, CT_APPLICATION_MSWORD}},\n        {\"docx\", {FT_DOCUMENT, CT_APPLICATION_MSWORDX}},\n        {\"eot\", {FT_DOCUMENT, CT_APPLICATION_VND_MS_FONTOBJ}},\n        {\"flac\", {FT_AUDIO, CT_AUDIO_FLAC}},\n        {\"gif\", {FT_MEDIA, CT_IMAGE_GIF}},\n        {\"gz\", {FT_ARCHIVE, CT_APPLICATION_GZIP}},\n        {\"htm\", {FT_DOCUMENT, CT_TEXT_HTML}},\n        {\"html\", {FT_DOCUMENT, CT_TEXT_HTML}},\n        {\"icns\", {FT_IMAGE, CT_IMAGE_ICNS}},\n        {\"ico\", {FT_IMAGE, CT_IMAGE_XICON}},\n        {\"j2k\", {FT_IMAGE, CT_IMAGE_JP2}},\n        {\"jar\", {FT_DOCUMENT, CT_APPLICATION_JAVA_ARCHIVE}},\n        {\"j2c\", {FT_IMAGE, CT_IMAGE_JP2}},\n        {\"jp2\", {FT_IMAGE, CT_IMAGE_JP2}},\n        {\"jpeg\", {FT_IMAGE, CT_IMAGE_JPG}},\n        {\"jpc\", {FT_IMAGE, CT_IMAGE_JP2}},\n        {\"jpf\", {FT_IMAGE, CT_IMAGE_JP2}},\n        {\"jpg\", {FT_IMAGE, CT_IMAGE_JPG}},\n        {\"jpg2\", {FT_IMAGE, CT_IMAGE_JP2}},\n        {\"jpm\", {FT_IMAGE, CT_IMAGE_JP2}},\n        {\"jpx\", {FT_IMAGE, CT_IMAGE_JP2}},\n        {\"js\", {FT_DOCUMENT, CT_TEXT_JAVASCRIPT}},\n        {\"json\", {FT_DOCUMENT, CT_APPLICATION_JSON}},\n        {\"lzma\", {FT_ARCHIVE, CT_APPLICATION_X_XZ}},\n        {\"m1a\", {FT_AUDIO, CT_AUDIO_MPEG}},\n        {\"m1v\", {FT_MEDIA, CT_VIDEO_MPEG}},\n        {\"m2a\", {FT_AUDIO, CT_AUDIO_MPEG}},\n        {\"m2ts\", {FT_MEDIA, CT_VIDEO_MPEG2TS}},\n        {\"m2v\", {FT_MEDIA, CT_VIDEO_MPEG}},\n        {\"m4a\", {FT_AUDIO, CT_AUDIO_MPEG4}},\n        {\"m4v\", {FT_MEDIA, CT_VIDEO_X_M4V}},\n        {\"mjs\", {FT_DOCUMENT, CT_TEXT_JAVASCRIPT}},\n        {\"mka\", {FT_AUDIO, CT_AUDIO_MATROSKA}},\n        {\"mkv\", {FT_MEDIA, CT_VIDEO_MATROSKA}},\n        {\"mng\", {FT_MEDIA, CT_IMAGE_X_MNG}},\n        {\"mov\", {FT_MEDIA, CT_VIDEO_QUICKTIME}},\n        {\"mp1\", {FT_AUDIO, CT_AUDIO_MPEG}},\n        {\"mp2\", {FT_AUDIO, CT_AUDIO_MPEG}},\n        {\"mp3\", {FT_AUDIO, CT_AUDIO_MPEG}},\n        {\"mp4\", {FT_MEDIA, CT_VIDEO_MP4}},\n        {\"mpa\", {FT_AUDIO, CT_AUDIO_MPEG}},\n        {\"mpe\", {FT_MEDIA, CT_VIDEO_MPEG}},\n        {\"mpeg\", {FT_MEDIA, CT_VIDEO_MPEG}},\n        {\"mpg\", {FT_MEDIA, CT_VIDEO_MPEG}},\n        {\"mpv\", {FT_MEDIA, CT_VIDEO_MPEG}},\n        {\"oga\", {FT_AUDIO, CT_AUDIO_OGG}},\n        {\"ogg\", {FT_AUDIO, CT_AUDIO_OGG}},\n        {\"ogv\", {FT_MEDIA, CT_VIDEO_OGG}},\n        {\"otf\", {FT_DOCUMENT, CT_APPLICATION_X_FONT_OPENTYPE}},\n        {\"pdf\", {FT_DOCUMENT, CT_APPLICATION_PDF}},\n        {\"php\", {FT_DOCUMENT, CT_APPLICATION_X_HTTPD_PHP}},\n        {\"png\", {FT_IMAGE, CT_IMAGE_PNG}},\n        {\"rar\", {FT_ARCHIVE, CT_APPLICATION_VND_RAR}},\n        {\"svg\", {FT_IMAGE, CT_IMAGE_SVG_XML}},\n        {\"tar\", {FT_ARCHIVE, CT_APPLICATION_X_TAR}},\n        {\"targa\", {FT_IMAGE, CT_IMAGE_X_TGA}},\n        {\"tif\", {FT_IMAGE, CT_IMAGE_TIFF}},\n        {\"tiff\", {FT_IMAGE, CT_IMAGE_TIFF}},\n        {\"tga\", {FT_IMAGE, CT_IMAGE_X_TGA}},\n        {\"tgz\", {FT_ARCHIVE, CT_APPLICATION_X_TGZ}},\n        {\"ts\", {FT_MEDIA, CT_VIDEO_MPEG2TS}},\n        {\"tta\", {FT_AUDIO, CT_AUDIO_X_TTA}},\n        {\"ttf\", {FT_DOCUMENT, CT_APPLICATION_X_FONT_TRUETYPE}},\n        {\"txt\", {FT_DOCUMENT, CT_TEXT_PLAIN}},\n        {\"w64\", {FT_AUDIO, CT_AUDIO_WAVE}},\n        {\"wav\", {FT_AUDIO, CT_AUDIO_WAVE}},\n        {\"wave\", {FT_AUDIO, CT_AUDIO_WAVE}},\n        {\"wasm\", {FT_DOCUMENT, CT_APPLICATION_WASM}},\n        {\"weba\", {FT_AUDIO, CT_AUDIO_WEBM}},\n        {\"webm\", {FT_MEDIA, CT_VIDEO_WEBM}},\n        {\"webp\", {FT_IMAGE, CT_IMAGE_WEBP}},\n        {\"wma\", {FT_AUDIO, CT_AUDIO_X_MS_WMA}},\n        {\"woff\", {FT_DOCUMENT, CT_APPLICATION_FONT_WOFF}},\n        {\"woff2\", {FT_DOCUMENT, CT_APPLICATION_FONT_WOFF2}},\n        {\"wv\", {FT_AUDIO, CT_AUDIO_X_WAVPACK}},\n        {\"xht\", {FT_DOCUMENT, CT_APPLICATION_XHTML}},\n        {\"xhtml\", {FT_DOCUMENT, CT_APPLICATION_XHTML}},\n        {\"xml\", {FT_DOCUMENT, CT_APPLICATION_XML}},\n        {\"xsl\", {FT_DOCUMENT, CT_TEXT_XSL}},\n        {\"xz\", {FT_ARCHIVE, CT_APPLICATION_X_XZ}},\n        {\"zip\", {FT_ARCHIVE, CT_APPLICATION_ZIP}},\n        {\"7z\", {FT_ARCHIVE, CT_APPLICATION_X_7Z}},\n    };\n\nconst std::string_view &statusCodeToString(int code)\n{\n    switch (code)\n    {\n        case 100:\n        {\n            static std::string_view sv = \"Continue\";\n            return sv;\n        }\n        case 101:\n        {\n            static std::string_view sv = \"Switching Protocols\";\n            return sv;\n        }\n        case 102:\n        {\n            static std::string_view sv = \"Processing\";\n            return sv;\n        }\n        case 103:\n        {\n            static std::string_view sv = \"Early Hints\";\n            return sv;\n        }\n        case 200:\n        {\n            static std::string_view sv = \"OK\";\n            return sv;\n        }\n        case 201:\n        {\n            static std::string_view sv = \"Created\";\n            return sv;\n        }\n        case 202:\n        {\n            static std::string_view sv = \"Accepted\";\n            return sv;\n        }\n        case 203:\n        {\n            static std::string_view sv = \"Non-Authoritative Information\";\n            return sv;\n        }\n        case 204:\n        {\n            static std::string_view sv = \"No Content\";\n            return sv;\n        }\n        case 205:\n        {\n            static std::string_view sv = \"Reset Content\";\n            return sv;\n        }\n        case 206:\n        {\n            static std::string_view sv = \"Partial Content\";\n            return sv;\n        }\n        case 207:\n        {\n            static std::string_view sv = \"Multi-Status\";\n            return sv;\n        }\n        case 208:\n        {\n            static std::string_view sv = \"Already Reported\";\n            return sv;\n        }\n        case 226:\n        {\n            static std::string_view sv = \"IM Used\";\n            return sv;\n        }\n        case 300:\n        {\n            static std::string_view sv = \"Multiple Choices\";\n            return sv;\n        }\n        case 301:\n        {\n            static std::string_view sv = \"Moved Permanently\";\n            return sv;\n        }\n        case 302:\n        {\n            static std::string_view sv = \"Found\";\n            return sv;\n        }\n        case 303:\n        {\n            static std::string_view sv = \"See Other\";\n            return sv;\n        }\n        case 304:\n        {\n            static std::string_view sv = \"Not Modified\";\n            return sv;\n        }\n        case 305:\n        {\n            static std::string_view sv = \"Use Proxy\";\n            return sv;\n        }\n        case 306:\n        {\n            static std::string_view sv = \"(Unused)\";\n            return sv;\n        }\n        case 307:\n        {\n            static std::string_view sv = \"Temporary Redirect\";\n            return sv;\n        }\n        case 308:\n        {\n            static std::string_view sv = \"Permanent Redirect\";\n            return sv;\n        }\n        case 400:\n        {\n            static std::string_view sv = \"Bad Request\";\n            return sv;\n        }\n        case 401:\n        {\n            static std::string_view sv = \"Unauthorized\";\n            return sv;\n        }\n        case 402:\n        {\n            static std::string_view sv = \"Payment Required\";\n            return sv;\n        }\n        case 403:\n        {\n            static std::string_view sv = \"Forbidden\";\n            return sv;\n        }\n        case 404:\n        {\n            static std::string_view sv = \"Not Found\";\n            return sv;\n        }\n        case 405:\n        {\n            static std::string_view sv = \"Method Not Allowed\";\n            return sv;\n        }\n        case 406:\n        {\n            static std::string_view sv = \"Not Acceptable\";\n            return sv;\n        }\n        case 407:\n        {\n            static std::string_view sv = \"Proxy Authentication Required\";\n            return sv;\n        }\n        case 408:\n        {\n            static std::string_view sv = \"Request Time-out\";\n            return sv;\n        }\n        case 409:\n        {\n            static std::string_view sv = \"Conflict\";\n            return sv;\n        }\n        case 410:\n        {\n            static std::string_view sv = \"Gone\";\n            return sv;\n        }\n        case 411:\n        {\n            static std::string_view sv = \"Length Required\";\n            return sv;\n        }\n        case 412:\n        {\n            static std::string_view sv = \"Precondition Failed\";\n            return sv;\n        }\n        case 413:\n        {\n            static std::string_view sv = \"Request Entity Too Large\";\n            return sv;\n        }\n        case 414:\n        {\n            static std::string_view sv = \"Request-URI Too Large\";\n            return sv;\n        }\n        case 415:\n        {\n            static std::string_view sv = \"Unsupported Media Type\";\n            return sv;\n        }\n        case 416:\n        {\n            static std::string_view sv = \"Requested Range Not Satisfiable\";\n            return sv;\n        }\n        case 417:\n        {\n            static std::string_view sv = \"Expectation Failed\";\n            return sv;\n        }\n        case 418:\n        {\n            static std::string_view sv = \"I'm a Teapot\";\n            return sv;\n        }\n        case 421:\n        {\n            static std::string_view sv = \"Misdirected Request\";\n            return sv;\n        }\n        case 422:\n        {\n            static std::string_view sv = \"Unprocessable Entity\";\n            return sv;\n        }\n        case 423:\n        {\n            static std::string_view sv = \"Locked\";\n            return sv;\n        }\n        case 424:\n        {\n            static std::string_view sv = \"Failed Dependency\";\n            return sv;\n        }\n        case 425:\n        {\n            static std::string_view sv = \"Too Early\";\n            return sv;\n        }\n        case 426:\n        {\n            static std::string_view sv = \"Upgrade Required\";\n            return sv;\n        }\n        case 428:\n        {\n            static std::string_view sv = \"Precondition Required\";\n            return sv;\n        }\n        case 429:\n        {\n            static std::string_view sv = \"Too Many Requests\";\n            return sv;\n        }\n        case 431:\n        {\n            static std::string_view sv = \"Request Header Fields Too Large\";\n            return sv;\n        }\n        case 451:\n        {\n            static std::string_view sv = \"Unavailable For Legal Reasons\";\n            return sv;\n        }\n        case 500:\n        {\n            static std::string_view sv = \"Internal Server Error\";\n            return sv;\n        }\n        case 501:\n        {\n            static std::string_view sv = \"Not Implemented\";\n            return sv;\n        }\n        case 502:\n        {\n            static std::string_view sv = \"Bad Gateway\";\n            return sv;\n        }\n        case 503:\n        {\n            static std::string_view sv = \"Service Unavailable\";\n            return sv;\n        }\n        case 504:\n        {\n            static std::string_view sv = \"Gateway Time-out\";\n            return sv;\n        }\n        case 505:\n        {\n            static std::string_view sv = \"HTTP Version Not Supported\";\n            return sv;\n        }\n        case 506:\n        {\n            static std::string_view sv = \"Variant Also Negotiates\";\n            return sv;\n        }\n        case 507:\n        {\n            static std::string_view sv = \"Insufficient Storage\";\n            return sv;\n        }\n        case 508:\n        {\n            static std::string_view sv = \"Loop Detected\";\n            return sv;\n        }\n        case 510:\n        {\n            static std::string_view sv = \"Not Extended\";\n            return sv;\n        }\n        case 511:\n        {\n            static std::string_view sv = \"Network Authentication Required\";\n            return sv;\n        }\n        default:\n            if (code >= 100 && code < 200)\n            {\n                static std::string_view sv = \"Informational\";\n                return sv;\n            }\n            else if (code >= 200 && code < 300)\n            {\n                static std::string_view sv = \"Successful\";\n                return sv;\n            }\n            else if (code >= 300 && code < 400)\n            {\n                static std::string_view sv = \"Redirection\";\n                return sv;\n            }\n            else if (code >= 400 && code < 500)\n            {\n                static std::string_view sv = \"Bad Request\";\n                return sv;\n            }\n            else if (code >= 500 && code < 600)\n            {\n                static std::string_view sv = \"Server Error\";\n                return sv;\n            }\n            else\n            {\n                static std::string_view sv = \"Undefined Error\";\n                return sv;\n            }\n    }\n}\n\nContentType getContentType(const std::string &fileName)\n{\n    std::string extName;\n    auto pos = fileName.rfind('.');\n    if (pos != std::string::npos)\n    {\n        extName = fileName.substr(pos + 1);\n        transform(extName.begin(),\n                  extName.end(),\n                  extName.begin(),\n                  [](unsigned char c) { return tolower(c); });\n    }\n    auto it = fileTypeDatabase_.find(extName);\n    return (it == fileTypeDatabase_.end()) ? CT_APPLICATION_OCTET_STREAM\n                                           : it->second.second;\n}\n\nContentType parseContentType(const std::string_view &contentType)\n{\n    // Generate map from database for faster query\n    static std::unordered_map<std::string_view, ContentType> contentTypeMap_;\n    // Thread safe initialization\n    static std::once_flag flag;\n    std::call_once(flag, []() {\n        for (const auto &e : mimeTypeDatabase_)\n        {\n            for (const auto &type : e.second.first)\n                contentTypeMap_[type] = e.first;\n        }\n    });\n    auto ext = contentType.find(';');\n    if (ext != std::string_view::npos)\n        return parseContentType(contentType.substr(0, ext));\n    if (contentType == \"application/x-www-form-urlencoded\")\n        return CT_APPLICATION_X_FORM;\n    if (contentType == \"multipart/form-data\")\n        return CT_MULTIPART_FORM_DATA;\n    auto it = contentTypeMap_.find(contentType);\n    return (it == contentTypeMap_.end()) ? CT_CUSTOM : it->second;\n}\n\nFileType parseFileType(const std::string_view &fileExtension)\n{\n    std::string extName(fileExtension);\n    transform(extName.begin(),\n              extName.end(),\n              extName.begin(),\n              [](unsigned char c) { return tolower(c); });\n    auto it = fileTypeDatabase_.find(extName);\n    return (it == fileTypeDatabase_.end()) ? FT_CUSTOM : it->second.first;\n}\n\nFileType getFileType(ContentType contentType)\n{\n    // Generate map from database for faster query\n    static std::unordered_map<ContentType, FileType> fileTypeMap_;\n    // Thread safe initialization\n    static std::once_flag flag;\n    std::call_once(flag, []() {\n        for (const auto &e : fileTypeDatabase_)\n            fileTypeMap_[e.second.second] = e.second.first;\n        fileTypeMap_[CT_NONE] = FT_UNKNOWN;\n        fileTypeMap_[CT_CUSTOM] = FT_CUSTOM;\n    });\n    auto it = fileTypeMap_.find(contentType);\n    return (it == fileTypeMap_.end()) ? FT_UNKNOWN : it->second;\n}\n\nconst std::string_view &contentTypeToMime(ContentType contentType)\n{\n    auto it = mimeTypeDatabase_.find(contentType);\n    return (it == mimeTypeDatabase_.end())\n               ? mimeTypeDatabase_.at(CT_APPLICATION_OCTET_STREAM).first.front()\n               : (it->second.second.empty() ? it->second.first.front()\n                                            : it->second.second);\n}\n\nvoid registerCustomExtensionMime(const std::string &ext,\n                                 const std::string &mime)\n{\n    if (ext.empty())\n        return;\n    auto &mimeStr = customMime[ext];\n    if (!mimeStr.empty())\n    {\n        LOG_WARN << ext << \" has already been registered as type \" << mime\n                 << \". Overwriting.\";\n    }\n    mimeStr = mime;\n}\n\nconst std::string_view fileNameToMime(const std::string &fileName)\n{\n    ContentType internalContentType = getContentType(fileName);\n    if (internalContentType != CT_APPLICATION_OCTET_STREAM)\n        return contentTypeToMime(internalContentType);\n\n    std::string extName;\n    auto pos = fileName.rfind('.');\n    if (pos != std::string::npos)\n    {\n        extName = fileName.substr(pos + 1);\n        transform(extName.begin(),\n                  extName.end(),\n                  extName.begin(),\n                  [](unsigned char c) { return tolower(c); });\n    }\n    auto it = customMime.find(extName);\n    if (it == customMime.end())\n        return \"\";\n    return it->second;\n}\n\nstd::pair<ContentType, const std::string_view> fileNameToContentTypeAndMime(\n    const std::string &fileName)\n{\n    ContentType internalContentType = getContentType(fileName);\n    if (internalContentType != CT_APPLICATION_OCTET_STREAM)\n        return {internalContentType, contentTypeToMime(internalContentType)};\n\n    std::string extName;\n    auto pos = fileName.rfind('.');\n    if (pos != std::string::npos)\n    {\n        extName = fileName.substr(pos + 1);\n        transform(extName.begin(),\n                  extName.end(),\n                  extName.begin(),\n                  [](unsigned char c) { return tolower(c); });\n    }\n    auto it = customMime.find(extName);\n    if (it == customMime.end())\n        return {CT_NONE, \"\"};\n    return {CT_CUSTOM, it->second};\n}\n\nconst std::vector<std::string_view> &getFileExtensions(ContentType contentType)\n{\n    // Generate map from database for faster query\n    static std::unordered_map<ContentType, std::vector<std::string_view>>\n        extensionMap_;\n    static std::vector<std::string_view> notFound_;\n    // Thread safe initialization\n    static std::once_flag flag;\n    std::call_once(flag, []() {\n        for (const auto &e : fileTypeDatabase_)\n            if (!e.first.empty())\n                extensionMap_[e.second.second].push_back(e.first);\n        // Add deprecated\n        extensionMap_[CT_APPLICATION_X_JAVASCRIPT] =\n            extensionMap_[CT_TEXT_JAVASCRIPT];\n        extensionMap_[CT_TEXT_XML] = extensionMap_[CT_APPLICATION_XML];\n    });\n    auto it = extensionMap_.find(contentType);\n    if (it == extensionMap_.end())\n        return notFound_;\n    return it->second;\n}\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpUtils.h",
    "content": "/**\n *\n *  @file HttpUtils.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <trantor/utils/MsgBuffer.h>\n#include <drogon/HttpTypes.h>\n#include <string>\n#include <string_view>\n\nnamespace drogon\n{\nconst std::string_view &contentTypeToMime(ContentType contentType);\nconst std::string_view &statusCodeToString(int code);\nContentType getContentType(const std::string &fileName);\nContentType parseContentType(const std::string_view &contentType);\nFileType parseFileType(const std::string_view &fileExtension);\nFileType getFileType(ContentType contentType);\nvoid registerCustomExtensionMime(const std::string &ext,\n                                 const std::string &mime);\nconst std::string_view fileNameToMime(const std::string &fileName);\nstd::pair<ContentType, const std::string_view> fileNameToContentTypeAndMime(\n    const std::string &filename);\n\ninline std::string_view getFileExtension(const std::string &fileName)\n{\n    auto pos = fileName.rfind('.');\n    if (pos == std::string::npos)\n        return \"\";\n    return std::string_view(&fileName[pos + 1], fileName.length() - pos - 1);\n}\n\nconst std::vector<std::string_view> &getFileExtensions(ContentType contentType);\n\ninline const std::vector<std::string_view> &getFileExtensions(\n    const std::string_view &contentType)\n{\n    return getFileExtensions(parseContentType(contentType));\n}\n\ntemplate <typename T>\ninline constexpr const char *contentLengthFormatString()\n{\n    return \"content-length: %d\\r\\n\";\n}\n\ntemplate <>\ninline constexpr const char *contentLengthFormatString<unsigned int>()\n{\n    return \"content-length: %u\\r\\n\";\n}\n\ntemplate <>\ninline constexpr const char *contentLengthFormatString<long>()\n{\n    return \"content-length: %ld\\r\\n\";\n}\n\ntemplate <>\ninline constexpr const char *contentLengthFormatString<unsigned long>()\n{\n    return \"content-length: %lu\\r\\n\";\n}\n\ntemplate <>\ninline constexpr const char *contentLengthFormatString<long long>()\n{\n    return \"content-length: %lld\\r\\n\";\n}\n\ntemplate <>\ninline constexpr const char *contentLengthFormatString<unsigned long long>()\n{\n    return \"content-length: %llu\\r\\n\";\n}\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/HttpViewData.cc",
    "content": "/**\n *\n *  HttpViewData.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/HttpViewData.h>\n\nusing namespace drogon;\n\nstd::string HttpViewData::htmlTranslate(const char *str, size_t length)\n{\n    std::string ret;\n    ret.reserve(length + 64);\n    auto end = str + length;\n    while (str != end)\n    {\n        switch (*str)\n        {\n            case '\"':\n                ret.append(\"&quot;\", 6);\n                break;\n            case '&':\n                ret.append(\"&amp;\", 5);\n                break;\n            case '<':\n                ret.append(\"&lt;\", 4);\n                break;\n            case '>':\n                ret.append(\"&gt;\", 4);\n                break;\n            default:\n                ret.push_back(*str);\n                break;\n        }\n        ++str;\n    }\n    return ret;\n}\n"
  },
  {
    "path": "lib/src/IntranetIpFilter.cc",
    "content": "/**\n *\n *  IntranetIpFilter.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpResponseImpl.h\"\n#include <drogon/IntranetIpFilter.h>\nusing namespace drogon;\n\nvoid IntranetIpFilter::doFilter(const HttpRequestPtr &req,\n                                FilterCallback &&fcb,\n                                FilterChainCallback &&fccb)\n{\n    if (req->peerAddr().isIntranetIp())\n    {\n        fccb();\n        return;\n    }\n    auto res = drogon::HttpResponse::newNotFoundResponse(req);\n    fcb(res);\n}\n"
  },
  {
    "path": "lib/src/JsonConfigAdapter.cc",
    "content": "#include \"JsonConfigAdapter.h\"\n#include <fstream>\n#include <mutex>\n\nusing namespace drogon;\n\nJson::Value JsonConfigAdapter::getJson(const std::string &content) const\n    noexcept(false)\n{\n    static std::once_flag once;\n    static Json::CharReaderBuilder builder;\n    std::call_once(once, []() { builder[\"collectComments\"] = false; });\n    JSONCPP_STRING errs;\n    std::unique_ptr<Json::CharReader> reader(builder.newCharReader());\n    Json::Value root;\n    if (!reader->parse(\n            content.c_str(), content.c_str() + content.size(), &root, &errs))\n    {\n        throw std::runtime_error(errs);\n    }\n    return root;\n}\n\nstd::vector<std::string> JsonConfigAdapter::getExtensions() const\n{\n    return {\"json\"};\n}\n"
  },
  {
    "path": "lib/src/JsonConfigAdapter.h",
    "content": "#pragma once\n#include \"ConfigAdapter.h\"\n\nnamespace drogon\n{\nclass JsonConfigAdapter : public ConfigAdapter\n{\n  public:\n    JsonConfigAdapter() = default;\n    ~JsonConfigAdapter() override = default;\n    Json::Value getJson(const std::string &content) const\n        noexcept(false) override;\n    std::vector<std::string> getExtensions() const override;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/ListenerManager.cc",
    "content": "/**\n *\n *  @file ListenerManager.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"ListenerManager.h\"\n#include <drogon/config.h>\n#include <fcntl.h>\n#include <trantor/utils/Logger.h>\n#include \"HttpAppFrameworkImpl.h\"\n#include \"HttpServer.h\"\n#ifndef _WIN32\n#include <sys/file.h>\n#include <unistd.h>\n#endif\n\nnamespace drogon\n{\n#ifndef _WIN32\nclass DrogonFileLocker : public trantor::NonCopyable\n{\n  public:\n    DrogonFileLocker()\n    {\n        fd_ = open(\"/tmp/drogon.lock\", O_TRUNC | O_CREAT, 0666);\n        flock(fd_, LOCK_EX);\n    }\n\n    ~DrogonFileLocker()\n    {\n        close(fd_);\n    }\n\n  private:\n    int fd_{0};\n};\n\n#endif\n}  // namespace drogon\n\nusing namespace trantor;\nusing namespace drogon;\n\nvoid ListenerManager::addListener(\n    const std::string &ip,\n    uint16_t port,\n    bool useSSL,\n    const std::string &certFile,\n    const std::string &keyFile,\n    bool useOldTLS,\n    const std::vector<std::pair<std::string, std::string>> &sslConfCmds)\n{\n    if (useSSL && !utils::supportsTls())\n        LOG_ERROR << \"Can't use SSL without OpenSSL found in your system\";\n    listeners_.emplace_back(\n        ip, port, useSSL, certFile, keyFile, useOldTLS, sslConfCmds);\n}\n\nstd::vector<trantor::InetAddress> ListenerManager::getListeners() const\n{\n    std::vector<trantor::InetAddress> listeners;\n    for (auto &server : servers_)\n    {\n        listeners.emplace_back(server->address());\n    }\n    return listeners;\n}\n\nvoid ListenerManager::createListeners(\n    const std::string &globalCertFile,\n    const std::string &globalKeyFile,\n    const std::vector<std::pair<std::string, std::string>> &sslConfCmds,\n    const std::vector<trantor::EventLoop *> &ioLoops)\n{\n    LOG_TRACE << \"thread num=\" << ioLoops.size();\n#ifdef __linux__\n    for (size_t i = 0; i < ioLoops.size(); ++i)\n    {\n        for (auto const &listener : listeners_)\n        {\n            auto const &ip = listener.ip_;\n            bool isIpv6 = (ip.find(':') != std::string::npos);\n            InetAddress listenAddress(ip, listener.port_, isIpv6);\n            if (listenAddress.isUnspecified())\n            {\n                LOG_FATAL << \"Failed to parse IP address '\" << ip\n                          << \"'. (Note: FQDN/domain names/hostnames are not \"\n                             \"supported. Including 'localhost')\";\n                abort();\n            }\n            if (i == 0 && !app().reusePort())\n            {\n                DrogonFileLocker lock;\n                // Check whether the port is in use.\n                TcpServer server(HttpAppFrameworkImpl::instance().getLoop(),\n                                 listenAddress,\n                                 \"drogonPortTest\",\n                                 true,\n                                 false);\n            }\n            std::shared_ptr<HttpServer> serverPtr =\n                std::make_shared<HttpServer>(ioLoops[i],\n                                             listenAddress,\n                                             \"drogon\");\n            if (beforeListenSetSockOptCallback_)\n            {\n                serverPtr->setBeforeListenSockOptCallback(\n                    beforeListenSetSockOptCallback_);\n            }\n            if (afterAcceptSetSockOptCallback_)\n            {\n                serverPtr->setAfterAcceptSockOptCallback(\n                    afterAcceptSetSockOptCallback_);\n            }\n            if (connectionCallback_)\n            {\n                serverPtr->setConnectionCallback(connectionCallback_);\n            }\n\n            if (listener.useSSL_ && utils::supportsTls())\n            {\n                auto cert = listener.certFile_;\n                auto key = listener.keyFile_;\n                if (cert.empty())\n                    cert = globalCertFile;\n                if (key.empty())\n                    key = globalKeyFile;\n                if (cert.empty() || key.empty())\n                {\n                    std::cerr\n                        << \"You can't use https without cert file or key file\"\n                        << std::endl;\n                    exit(1);\n                }\n                auto cmds = sslConfCmds;\n                std::copy(listener.sslConfCmds_.begin(),\n                          listener.sslConfCmds_.end(),\n                          std::back_inserter(cmds));\n                auto policy =\n                    trantor::TLSPolicy::defaultServerPolicy(cert, key);\n                policy->setConfCmds(cmds).setUseOldTLS(listener.useOldTLS_);\n                serverPtr->enableSSL(std::move(policy));\n            }\n            servers_.push_back(serverPtr);\n        }\n    }\n#else\n\n    if (!listeners_.empty())\n    {\n        listeningThread_ =\n            std::make_unique<EventLoopThread>(\"DrogonListeningLoop\");\n        listeningThread_->run();\n        for (auto const &listener : listeners_)\n        {\n            auto ip = listener.ip_;\n            bool isIpv6 = (ip.find(':') != std::string::npos);\n            auto serverPtr = std::make_shared<HttpServer>(\n                listeningThread_->getLoop(),\n                InetAddress(ip, listener.port_, isIpv6),\n                \"drogon\");\n            if (listener.useSSL_ && utils::supportsTls())\n            {\n                auto cert = listener.certFile_;\n                auto key = listener.keyFile_;\n                if (cert.empty())\n                    cert = globalCertFile;\n                if (key.empty())\n                    key = globalKeyFile;\n                if (cert.empty() || key.empty())\n                {\n                    std::cerr\n                        << \"You can't use https without cert file or key file\"\n                        << std::endl;\n                    exit(1);\n                }\n                auto cmds = sslConfCmds;\n                auto policy =\n                    trantor::TLSPolicy::defaultServerPolicy(cert, key);\n                policy->setConfCmds(cmds).setUseOldTLS(listener.useOldTLS_);\n                serverPtr->enableSSL(std::move(policy));\n            }\n            serverPtr->setIoLoops(ioLoops);\n            servers_.push_back(serverPtr);\n        }\n    }\n#endif\n}\n\nvoid ListenerManager::startListening()\n{\n    for (auto &server : servers_)\n    {\n        server->start();\n    }\n}\n\nvoid ListenerManager::stopListening()\n{\n    for (auto &serverPtr : servers_)\n    {\n        serverPtr->stop();\n    }\n    if (listeningThread_)\n    {\n        auto loop = listeningThread_->getLoop();\n        assert(!loop->isInLoopThread());\n        loop->quit();\n        listeningThread_->wait();\n    }\n}\n\nvoid ListenerManager::reloadSSLFiles()\n{\n    for (auto &server : servers_)\n    {\n        server->reloadSSL();\n    }\n}\n"
  },
  {
    "path": "lib/src/ListenerManager.h",
    "content": "/**\n *\n *  @file ListenerManager.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <trantor/net/EventLoopThreadPool.h>\n#include <trantor/net/callbacks.h>\n#include <trantor/utils/NonCopyable.h>\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n#include \"impl_forwards.h\"\n\nnamespace trantor\n{\nclass InetAddress;\n}\n\nnamespace drogon\n{\nclass ListenerManager : public trantor::NonCopyable\n{\n  public:\n    ~ListenerManager() = default;\n    void addListener(const std::string &ip,\n                     uint16_t port,\n                     bool useSSL = false,\n                     const std::string &certFile = \"\",\n                     const std::string &keyFile = \"\",\n                     bool useOldTLS = false,\n                     const std::vector<std::pair<std::string, std::string>>\n                         &sslConfCmds = {});\n    std::vector<trantor::InetAddress> getListeners() const;\n    void createListeners(\n        const std::string &globalCertFile,\n        const std::string &globalKeyFile,\n        const std::vector<std::pair<std::string, std::string>> &sslConfCmds,\n        const std::vector<trantor::EventLoop *> &ioLoops);\n    void startListening();\n    void stopListening();\n\n    void setBeforeListenSockOptCallback(std::function<void(int)> cb)\n    {\n        beforeListenSetSockOptCallback_ = std::move(cb);\n    }\n\n    void setAfterAcceptSockOptCallback(std::function<void(int)> cb)\n    {\n        afterAcceptSetSockOptCallback_ = std::move(cb);\n    }\n\n    void setConnectionCallback(\n        std::function<void(const trantor::TcpConnectionPtr &)> cb)\n    {\n        connectionCallback_ = std::move(cb);\n    }\n\n    void reloadSSLFiles();\n\n  private:\n    struct ListenerInfo\n    {\n        ListenerInfo(\n            std::string ip,\n            uint16_t port,\n            bool useSSL,\n            std::string certFile,\n            std::string keyFile,\n            bool useOldTLS,\n            std::vector<std::pair<std::string, std::string>> sslConfCmds)\n            : ip_(std::move(ip)),\n              port_(port),\n              useSSL_(useSSL),\n              certFile_(std::move(certFile)),\n              keyFile_(std::move(keyFile)),\n              useOldTLS_(useOldTLS),\n              sslConfCmds_(std::move(sslConfCmds))\n        {\n        }\n\n        std::string ip_;\n        uint16_t port_;\n        bool useSSL_;\n        std::string certFile_;\n        std::string keyFile_;\n        bool useOldTLS_;\n        std::vector<std::pair<std::string, std::string>> sslConfCmds_;\n    };\n\n    std::vector<ListenerInfo> listeners_;\n    std::vector<std::shared_ptr<HttpServer>> servers_;\n\n    // should have value when and only when on OS that one port can only be\n    // listened by one thread\n    std::unique_ptr<trantor::EventLoopThread> listeningThread_;\n    std::function<void(int)> beforeListenSetSockOptCallback_;\n    std::function<void(int)> afterAcceptSetSockOptCallback_;\n    std::function<void(const trantor::TcpConnectionPtr &)> connectionCallback_;\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/LocalHostFilter.cc",
    "content": "/**\n *\n *  LocalHostFilter.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpResponseImpl.h\"\n#include <drogon/LocalHostFilter.h>\nusing namespace drogon;\n\nvoid LocalHostFilter::doFilter(const HttpRequestPtr &req,\n                               FilterCallback &&fcb,\n                               FilterChainCallback &&fccb)\n{\n    if (req->peerAddr().isLoopbackIp())\n    {\n        fccb();\n        return;\n    }\n    auto res = drogon::HttpResponse::newNotFoundResponse(req);\n    fcb(res);\n}\n"
  },
  {
    "path": "lib/src/MiddlewaresFunction.cc",
    "content": "/**\n *\n *  @file MiddlewaresFunction.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"MiddlewaresFunction.h\"\n#include \"HttpRequestImpl.h\"\n#include \"HttpAppFrameworkImpl.h\"\n#include <drogon/HttpMiddleware.h>\n\n#include <queue>\n\nnamespace drogon\n{\nnamespace middlewares_function\n{\nstatic void doFilterChains(\n    const std::vector<std::shared_ptr<HttpFilterBase>> &filters,\n    size_t index,\n    const HttpRequestImplPtr &req,\n    std::shared_ptr<const std::function<void(const HttpResponsePtr &)>>\n        &&callbackPtr)\n{\n    if (index < filters.size())\n    {\n        auto &filter = filters[index];\n        filter->doFilter(\n            req,\n            [/*copy*/ callbackPtr](const HttpResponsePtr &resp) {\n                (*callbackPtr)(resp);\n            },\n            [index, req, callbackPtr, &filters]() mutable {\n                auto ioLoop = req->getLoop();\n                if (ioLoop && !ioLoop->isInLoopThread())\n                {\n                    ioLoop->queueInLoop(\n                        [&filters,\n                         index,\n                         req,\n                         callbackPtr = std::move(callbackPtr)]() mutable {\n                            doFilterChains(filters,\n                                           index + 1,\n                                           req,\n                                           std::move(callbackPtr));\n                        });\n                }\n                else\n                {\n                    doFilterChains(filters,\n                                   index + 1,\n                                   req,\n                                   std::move(callbackPtr));\n                }\n            });\n    }\n    else\n    {\n        (*callbackPtr)(nullptr);\n    }\n}\n\nvoid doFilters(const std::vector<std::shared_ptr<HttpFilterBase>> &filters,\n               const HttpRequestImplPtr &req,\n               std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    auto callbackPtr =\n        std::make_shared<std::decay_t<decltype(callback)>>(std::move(callback));\n    doFilterChains(filters, 0, req, std::move(callbackPtr));\n}\n\n/**\n * @brief\n * The middlewares are invoked according to the onion ring model.\n *\n * @param outerCallback The road back to the outer layer of the onion ring.\n * @param innermostHandler The innermost handler at the core of the onion ring.\n *\n * When going through each middleware, the `innermostHandler` is passed down as\n * is, while the `outerCallback` is passed to the user code. User code wraps the\n * outerCallback along with other post processing codes into `userPostCb`, and\n * passes it to the next middleware.\n *\n * When reaching the onion core, the `innermostHandler` is finally called. It's\n * parameter is a function that wraps the original `outerCallback` and all\n * `userPostCb`s.\n */\nstatic void passMiddlewareChains(\n    const std::vector<std::shared_ptr<HttpMiddlewareBase>> &middlewares,\n    size_t index,\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&outerCallback,\n    std::function<void(std::function<void(const HttpResponsePtr &)> &&)>\n        &&innermostHandler)\n{\n    if (index < middlewares.size())\n    {\n        auto &middleware = middlewares[index];\n        middleware->invoke(\n            req,\n            [index,\n             req,\n             innermostHandler = std::move(innermostHandler),\n             &middlewares](std::function<void(const HttpResponsePtr &)>\n                               &&userPostCb) mutable {\n                // call next middleware\n                auto ioLoop = req->getLoop();\n                if (ioLoop && !ioLoop->isInLoopThread())\n                {\n                    ioLoop->queueInLoop(\n                        [&middlewares,\n                         index,\n                         req,\n                         innermostHandler = std::move(innermostHandler),\n                         userPostCb = std::move(userPostCb)]() mutable {\n                            passMiddlewareChains(middlewares,\n                                                 index + 1,\n                                                 req,\n                                                 std::move(userPostCb),\n                                                 std::move(innermostHandler)\n\n                            );\n                        });\n                }\n                else\n                {\n                    passMiddlewareChains(middlewares,\n                                         index + 1,\n                                         req,\n                                         std::move(userPostCb),\n                                         std::move(innermostHandler));\n                }\n            },\n            std::move(outerCallback));\n    }\n    else\n    {\n        innermostHandler(std::move(outerCallback));\n    }\n}\n\nstd::vector<std::shared_ptr<HttpMiddlewareBase>> createMiddlewares(\n    const std::vector<std::string> &middlewareNames)\n{\n    std::vector<std::shared_ptr<HttpMiddlewareBase>> middlewares;\n    for (const auto &name : middlewareNames)\n    {\n        auto object_ = DrClassMap::getSingleInstance(name);\n        if (auto middleware =\n                std::dynamic_pointer_cast<HttpMiddlewareBase>(object_))\n        {\n            middlewares.push_back(middleware);\n        }\n        else\n        {\n            LOG_ERROR << \"middleware \" << name << \" not found\";\n        }\n    }\n    return middlewares;\n}\n\nvoid passMiddlewares(\n    const std::vector<std::shared_ptr<HttpMiddlewareBase>> &middlewares,\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&outermostCallback,\n    std::function<void(std::function<void(const HttpResponsePtr &)> &&)>\n        &&innermostHandler)\n{\n    passMiddlewareChains(middlewares,\n                         0,\n                         req,\n                         std::move(outermostCallback),\n                         std::move(innermostHandler));\n}\n\n}  // namespace middlewares_function\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/MiddlewaresFunction.h",
    "content": "/**\n *\n *  MiddlewaresFunction.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"impl_forwards.h\"\n#include <memory>\n#include <string>\n#include <vector>\n\nnamespace drogon\n{\nnamespace middlewares_function\n{\n// We can not remove old filters api. GlobalFilter still needs it.\n// GlobalFilter run filters in advice chains, which does not expose the outer\n// response handler, so HttpMiddleware is not suitable for it.\nvoid doFilters(const std::vector<std::shared_ptr<HttpFilterBase>> &filters,\n               const HttpRequestImplPtr &req,\n               std::function<void(const HttpResponsePtr &)> &&callback);\n\nstd::vector<std::shared_ptr<HttpMiddlewareBase>> createMiddlewares(\n    const std::vector<std::string> &middlewareNames);\n\nvoid passMiddlewares(\n    const std::vector<std::shared_ptr<HttpMiddlewareBase>> &middlewares,\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&outermostCallback,\n    std::function<void(std::function<void(const HttpResponsePtr &)> &&)>\n        &&innermostHandler);\n\n}  // namespace middlewares_function\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/MultiPart.cc",
    "content": "/**\n *\n *  @file MultiPart.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"HttpRequestImpl.h\"\n#include \"HttpUtils.h\"\n#include \"HttpAppFrameworkImpl.h\"\n#include \"HttpFileImpl.h\"\n#include <drogon/MultiPart.h>\n#include <drogon/utils/Utilities.h>\n#include <drogon/config.h>\n#include <algorithm>\n#include <fcntl.h>\n#include <fstream>\n#include <iostream>\n#include <sys/stat.h>\n#ifndef _WIN32\n#include <unistd.h>\n#endif\n\nusing namespace drogon;\n\nconst std::vector<HttpFile> &MultiPartParser::getFiles() const\n{\n    return files_;\n}\n\nstd::unordered_map<std::string, HttpFile> MultiPartParser::getFilesMap() const\n{\n    std::unordered_map<std::string, HttpFile> result;\n    for (auto &file : files_)\n    {\n        result.emplace(file.getItemName(), file);\n    }\n    return result;\n}\n\nconst SafeStringMap<std::string> &MultiPartParser::getParameters() const\n{\n    return parameters_;\n}\n\nint MultiPartParser::parse(const HttpRequestPtr &req)\n{\n    switch (req->method())\n    {\n        case Post:\n        case Put:\n        case Patch:\n            break;\n        default:\n            return -1;\n    }\n\n    const std::string &contentType =\n        static_cast<HttpRequestImpl *>(req.get())->getHeaderBy(\"content-type\");\n    if (contentType.empty())\n    {\n        return -1;\n    }\n    std::string::size_type pos = contentType.find(';');\n    if (pos == std::string::npos)\n        return -1;\n\n    std::string type = contentType.substr(0, pos);\n    std::transform(type.begin(), type.end(), type.begin(), [](unsigned char c) {\n        return tolower(c);\n    });\n    if (type != \"multipart/form-data\")\n        return -1;\n    pos = contentType.find(\"boundary=\");\n    if (pos == std::string::npos)\n        return -1;\n    auto pos2 = contentType.find(';', pos);\n    if (pos2 == std::string::npos)\n        pos2 = contentType.size();\n    return parse(req, contentType.data() + (pos + 9), pos2 - (pos + 9));\n}\n\nstatic std::pair<std::string_view, std::string_view> parseLine(\n    const char *begin,\n    const char *end)\n{\n    auto p = begin;\n    while (p != end)\n    {\n        if (*p == ':')\n        {\n            if (p + 1 != end && *(p + 1) == ' ')\n            {\n                return std::make_pair(std::string_view(begin, p - begin),\n                                      std::string_view(p + 2, end - p - 2));\n            }\n            else\n            {\n                return std::make_pair(std::string_view(begin, p - begin),\n                                      std::string_view(p + 1, end - p - 1));\n            }\n        }\n        ++p;\n    }\n    return std::make_pair(std::string_view(), std::string_view());\n}\n\nint MultiPartParser::parseEntity(const HttpRequestPtr &req,\n                                 const char *begin,\n                                 const char *end)\n{\n    static const char entityName[] = \"name=\";\n    static const char fileName[] = \"filename=\";\n    static const char CRLF[] = \"\\r\\n\\r\\n\";\n    auto headEnd = std::search(begin, end, CRLF, CRLF + 4);\n    if (headEnd == end)\n    {\n        return -1;\n    }\n    headEnd += 2;\n    auto pos = begin;\n    std::shared_ptr<HttpFileImpl> filePtr = std::make_shared<HttpFileImpl>();\n    while (pos != headEnd)\n    {\n        auto lineEnd = std::search(pos, headEnd, CRLF, CRLF + 2);\n        auto keyAndValue = parseLine(pos, lineEnd);\n        if (keyAndValue.first.empty() || keyAndValue.second.empty())\n        {\n            return -1;\n        }\n        pos = lineEnd + 2;\n        std::string key{keyAndValue.first.data(), keyAndValue.first.size()};\n        std::transform(key.begin(),\n                       key.end(),\n                       key.begin(),\n                       [](unsigned char c) { return tolower(c); });\n        if (key == \"content-disposition\")\n        {\n            auto value = keyAndValue.second;\n            auto valueEnd = value.data() + value.length();\n            auto namePos =\n                std::search(value.data(), valueEnd, entityName, entityName + 5);\n            if (namePos == valueEnd)\n            {\n                return -1;\n            }\n            namePos += 5;\n            const char *nameEnd;\n            if (*namePos == '\"')\n            {\n                ++namePos;\n                nameEnd = std::find(namePos, valueEnd, '\"');\n            }\n            else\n            {\n                nameEnd = std::find(namePos, valueEnd, ';');\n            }\n            std::string name(namePos, nameEnd);\n            auto fileNamePos =\n                std::search(nameEnd, valueEnd, fileName, fileName + 9);\n            if (fileNamePos == valueEnd)\n            {\n                parameters_.emplace(name, std::string(headEnd + 2, end));\n                return 0;\n            }\n            else\n            {\n                fileNamePos += 9;\n                const char *fileNameEnd;\n                if (*fileNamePos == '\"')\n                {\n                    ++fileNamePos;\n                    fileNameEnd = std::find(fileNamePos, valueEnd, '\"');\n                }\n                else\n                {\n                    fileNameEnd = std::find(fileNamePos, valueEnd, ';');\n                }\n                std::string fName{fileNamePos, fileNameEnd};\n                filePtr->setRequest(req);\n                filePtr->setItemName(std::move(name));\n                filePtr->setFileName(std::move(fName));\n                filePtr->setFile(headEnd + 2,\n                                 static_cast<size_t>(end - headEnd - 2));\n            }\n        }\n        else if (key == \"content-type\")\n        {\n            auto value = keyAndValue.second;\n            auto semiColonPos =\n                std::find(value.data(), value.data() + value.length(), ';');\n            std::string_view contentType(value.data(),\n                                         semiColonPos - value.data());\n            filePtr->setContentType(parseContentType(contentType));\n        }\n        else if (key == \"content-transfer-encoding\")\n        {\n            auto value = keyAndValue.second;\n            auto semiColonPos =\n                std::find(value.data(), value.data() + value.length(), ';');\n\n            filePtr->setContentTransferEncoding(\n                std::string{value.data(), semiColonPos});\n        }\n    }\n    if (!filePtr->getFileName().empty())\n    {\n        files_.emplace_back(std::move(filePtr));\n        return 0;\n    }\n    else\n    {\n        return -1;\n    }\n}\n\nint MultiPartParser::parse(const HttpRequestPtr &req,\n                           const char *boundaryData,\n                           size_t boundaryLen)\n{\n    std::string_view boundary{boundaryData, boundaryLen};\n    if (boundary.size() > 2 && boundary[0] == '\\\"')\n        boundary = boundary.substr(1, boundary.size() - 2);\n    std::string_view::size_type pos1, pos2;\n    pos1 = 0;\n    auto content = static_cast<HttpRequestImpl *>(req.get())->bodyView();\n    pos2 = content.find(boundary);\n    while (true)\n    {\n        pos1 = pos2;\n        if (pos1 == std::string_view::npos)\n            break;\n        pos1 += boundary.length();\n        if (content[pos1] == '\\r' && content[pos1 + 1] == '\\n')\n            pos1 += 2;\n        pos2 = content.find(boundary, pos1);\n        if (pos2 == std::string_view::npos)\n            break;\n        bool flag = false;\n        if (content[pos2 - 4] == '\\r' && content[pos2 - 3] == '\\n' &&\n            content[pos2 - 2] == '-' && content[pos2 - 1] == '-')\n        {\n            pos2 -= 4;\n            flag = true;\n        }\n        if (parseEntity(req, content.data() + pos1, content.data() + pos2) != 0)\n            return -1;\n        if (flag)\n            pos2 += 4;\n    }\n    return 0;\n}\n"
  },
  {
    "path": "lib/src/MultipartStreamParser.cc",
    "content": "/**\n *\n *  @file MultipartStreamParser.h\n *  @author Nitromelon\n *\n *  Copyright 2024, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"MultipartStreamParser.h\"\n#include <cassert>\n\nusing namespace drogon;\n\nstatic bool startsWith(const std::string_view &a, const std::string_view &b)\n{\n    if (a.size() < b.size())\n    {\n        return false;\n    }\n    for (size_t i = 0; i < b.size(); i++)\n    {\n        if (a[i] != b[i])\n        {\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic bool startsWithIgnoreCase(const std::string_view &a,\n                                 const std::string_view &b)\n{\n    if (a.size() < b.size())\n    {\n        return false;\n    }\n    for (size_t i = 0; i < b.size(); i++)\n    {\n        if (::tolower(a[i]) != ::tolower(b[i]))\n        {\n            return false;\n        }\n    }\n    return true;\n}\n\nMultipartStreamParser::MultipartStreamParser(const std::string &contentType)\n{\n    static const std::string_view multipart = \"multipart/form-data\";\n    static const std::string_view boundaryEq = \"boundary=\";\n\n    if (!startsWithIgnoreCase(contentType, multipart))\n    {\n        isValid_ = false;\n        return;\n    }\n    auto pos = contentType.find(boundaryEq, multipart.size());\n    if (pos == std::string::npos)\n    {\n        isValid_ = false;\n        return;\n    }\n\n    pos += boundaryEq.size();\n    size_t pos2;\n    if (contentType[pos] == '\"')\n    {\n        ++pos;\n        pos2 = contentType.find('\"', pos);\n    }\n    else\n    {\n        pos2 = contentType.find(';', pos);\n    }\n    if (pos2 == std::string::npos)\n        pos2 = contentType.size();\n\n    boundary_ = contentType.substr(pos, pos2 - pos);\n    dashBoundaryCrlf_ = dash_ + boundary_ + crlf_;\n    crlfDashBoundary_ = crlf_ + dash_ + boundary_;\n}\n\n// TODO: same function in HttpRequestParser.cc\nstatic std::pair<std::string_view, std::string_view> parseLine(\n    const char *begin,\n    const char *end)\n{\n    auto p = begin;\n    while (p != end)\n    {\n        if (*p == ':')\n        {\n            if (p + 1 != end && *(p + 1) == ' ')\n            {\n                return std::make_pair(std::string_view(begin, p - begin),\n                                      std::string_view(p + 2, end - p - 2));\n            }\n            else\n            {\n                return std::make_pair(std::string_view(begin, p - begin),\n                                      std::string_view(p + 1, end - p - 1));\n            }\n        }\n        ++p;\n    }\n    return std::make_pair(std::string_view(), std::string_view());\n}\n\nvoid drogon::MultipartStreamParser::parse(\n    const char *data,\n    size_t length,\n    const drogon::RequestStreamReader::MultipartHeaderCallback &headerCb,\n    const drogon::RequestStreamReader::StreamDataCallback &dataCb)\n{\n    buffer_.append(data, length);\n\n    while (buffer_.size() > 0)\n    {\n        switch (status_)\n        {\n            case Status::kExpectFirstBoundary:\n            {\n                if (buffer_.size() < dashBoundaryCrlf_.size())\n                {\n                    return;\n                }\n                std::string_view v = buffer_.view();\n                auto pos = v.find(dashBoundaryCrlf_);\n                // ignore everything before the first boundary\n                if (pos == std::string::npos)\n                {\n                    buffer_.eraseFront(buffer_.size() -\n                                       dashBoundaryCrlf_.size());\n                    return;\n                }\n                // found\n                buffer_.eraseFront(pos + dashBoundaryCrlf_.size());\n                status_ = Status::kExpectNewEntry;\n                continue;\n            }\n            case Status::kExpectNewEntry:\n            {\n                currentHeader_.name.clear();\n                currentHeader_.filename.clear();\n                currentHeader_.contentType.clear();\n                status_ = Status::kExpectHeader;\n                continue;\n            }\n            case Status::kExpectHeader:\n            {\n                std::string_view v = buffer_.view();\n                auto pos = v.find(crlf_);\n                if (pos == std::string::npos)\n                {\n                    // same magic number in HttpRequestParser::parseRequest()\n                    if (buffer_.size() > 60 * 1024)\n                    {\n                        isValid_ = false;\n                    }\n                    return;  // header incomplete, wait for more data\n                }\n                // empty line\n                if (pos == 0)\n                {\n                    buffer_.eraseFront(crlf_.size());\n                    status_ = Status::kExpectBody;\n                    headerCb(currentHeader_);\n                    continue;\n                }\n\n                // found header line\n                auto [keyView, valueView] = parseLine(v.data(), v.data() + pos);\n                if (keyView.empty() || valueView.empty())\n                {\n                    // Bad header\n                    isValid_ = false;\n                    return;\n                }\n\n                if (startsWithIgnoreCase(keyView, \"content-type\"))\n                {\n                    currentHeader_.contentType = valueView;\n                }\n                else if (startsWithIgnoreCase(keyView, \"content-disposition\"))\n                {\n                    static const std::string_view nameKey = \"name=\";\n                    static const std::string_view fileNameKey = \"filename=\";\n\n                    // Extract name\n                    auto namePos = valueView.find(nameKey);\n                    if (namePos == std::string::npos)\n                    {\n                        // name absent\n                        isValid_ = false;\n                        return;\n                    }\n                    namePos += nameKey.size();\n                    size_t nameEnd;\n                    if (valueView[namePos] == '\"')\n                    {\n                        ++namePos;\n                        nameEnd = valueView.find('\"', namePos);\n                    }\n                    else\n                    {\n                        nameEnd = valueView.find(';', namePos);\n                    }\n                    if (nameEnd == std::string::npos)\n                    {\n                        // name end not found\n                        isValid_ = false;\n                        return;\n                    }\n                    currentHeader_.name =\n                        valueView.substr(namePos, nameEnd - namePos);\n\n                    // Extract filename\n                    auto fileNamePos = valueView.find(fileNameKey, nameEnd);\n                    if (fileNamePos != std::string::npos)\n                    {\n                        fileNamePos += fileNameKey.size();\n                        size_t fileNameEnd;\n                        if (valueView[fileNamePos] == '\"')\n                        {\n                            ++fileNamePos;\n                            fileNameEnd = valueView.find('\"', fileNamePos);\n                        }\n                        else\n                        {\n                            fileNameEnd = valueView.find(';', fileNamePos);\n                        }\n                        currentHeader_.filename =\n                            valueView.substr(fileNamePos,\n                                             fileNameEnd - fileNamePos);\n                    }\n                }\n                // ignore other headers\n                buffer_.eraseFront(pos + crlf_.size());\n                continue;\n            }\n            case Status::kExpectBody:\n            {\n                if (buffer_.size() < crlfDashBoundary_.size())\n                {\n                    return;  // not enough data to check boundary\n                }\n                std::string_view v = buffer_.view();\n                auto pos = v.find(crlfDashBoundary_);\n                if (pos == std::string::npos)\n                {\n                    // boundary not found, leave potential partial boundary\n                    size_t len = v.size() - crlfDashBoundary_.size();\n                    if (len > 0)\n                    {\n                        dataCb(v.data(), len);\n                        buffer_.eraseFront(len);\n                    }\n                    return;\n                }\n                // found boundary\n                dataCb(v.data(), pos);\n                if (pos > 0)\n                {\n                    dataCb(v.data() + pos, 0);  // notify end of file\n                }\n                buffer_.eraseFront(pos + crlfDashBoundary_.size());\n                status_ = Status::kExpectEndOrNewEntry;\n                continue;\n            }\n            case Status::kExpectEndOrNewEntry:\n            {\n                std::string_view v = buffer_.view();\n                // Check new entry\n                if (v.size() < crlf_.size())\n                {\n                    return;\n                }\n                if (startsWith(v, crlf_))\n                {\n                    buffer_.eraseFront(crlf_.size());\n                    status_ = Status::kExpectNewEntry;\n                    continue;\n                }\n\n                // Check end\n                if (v.size() < dash_.size())\n                {\n                    return;\n                }\n                if (startsWith(v, dash_))\n                {\n                    isFinished_ = true;\n                    buffer_.clear();  // ignore epilogue\n                    return;\n                }\n                isValid_ = false;\n                return;\n            }\n        }\n    }\n}\n\nstd::string_view MultipartStreamParser::Buffer::view() const\n{\n    return {buffer_.data() + bufHead_, size()};\n}\n\nvoid MultipartStreamParser::Buffer::append(const char *data, size_t length)\n{\n    size_t remainSize = size();\n    // Move existing data to the front\n    if (remainSize > 0 && bufHead_ > 0)\n    {\n        for (size_t i = 0; i < remainSize; i++)\n        {\n            buffer_[i] = buffer_[bufHead_ + i];\n        }\n    }\n    bufHead_ = 0;\n    bufTail_ = remainSize;\n\n    if (remainSize + length > buffer_.size())\n    {\n        buffer_.resize(remainSize + length);\n    }\n\n    for (size_t i = 0; i < length; ++i)\n    {\n        buffer_[bufTail_ + i] = data[i];\n    }\n    bufTail_ += length;\n}\n\nsize_t MultipartStreamParser::Buffer::size() const\n{\n    return bufTail_ - bufHead_;\n}\n\nvoid MultipartStreamParser::Buffer::eraseFront(size_t length)\n{\n    assert(length <= size());\n    bufHead_ += length;\n}\n\nvoid MultipartStreamParser::Buffer::clear()\n{\n    buffer_.clear();\n    bufHead_ = 0;\n    bufTail_ = 0;\n}\n"
  },
  {
    "path": "lib/src/MultipartStreamParser.h",
    "content": "/**\n *\n *  @file MultipartStreamParser.h\n *  @author Nitromelon\n *\n *  Copyright 2024, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <drogon/exports.h>\n#include <drogon/RequestStream.h>\n#include <string>\n\nnamespace drogon\n{\nclass DROGON_EXPORT MultipartStreamParser\n{\n  public:\n    MultipartStreamParser(const std::string &contentType);\n\n    void parse(const char *data,\n               size_t length,\n               const RequestStreamReader::MultipartHeaderCallback &headerCb,\n               const RequestStreamReader::StreamDataCallback &dataCb);\n\n    bool isFinished() const\n    {\n        return isFinished_;\n    }\n\n    bool isValid() const\n    {\n        return isValid_;\n    }\n\n  private:\n    const std::string dash_ = \"--\";\n    const std::string crlf_ = \"\\r\\n\";\n    std::string boundary_;\n    std::string dashBoundaryCrlf_;\n    std::string crlfDashBoundary_;\n\n    struct Buffer\n    {\n      public:\n        std::string_view view() const;\n        void append(const char *data, size_t length);\n        size_t size() const;\n        void eraseFront(size_t length);\n        void clear();\n\n      private:\n        std::string buffer_;\n        size_t bufHead_{0};\n        size_t bufTail_{0};\n    } buffer_;\n\n    enum class Status\n    {\n        kExpectFirstBoundary = 0,\n        kExpectNewEntry = 1,\n        kExpectHeader = 2,\n        kExpectBody = 3,\n        kExpectEndOrNewEntry = 4,\n    } status_{Status::kExpectFirstBoundary};\n\n    MultipartHeader currentHeader_;\n    bool isValid_{true};\n    bool isFinished_{false};\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/NotFound.cc",
    "content": "// this file is generated by program automatically,don't modify it!\n\n/**\n *\n *  NotFound.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/NotFound.h>\n#include <map>\n#include <set>\n#include <sstream>\n#include <string>\n#include <vector>\n\nusing namespace drogon;\n\nstd::string NotFound::genText(const HttpViewData &NotFound_view_data)\n{\n    std::stringstream NotFound_tmp_stream;\n    NotFound_tmp_stream << \"<html>\\n\";\n    NotFound_tmp_stream << \"<head><title>404 Not Found</title></head>\\n\";\n    NotFound_tmp_stream << \"<body bgcolor=\\\"white\\\" text=\\\"black\\\">\\n\";\n    NotFound_tmp_stream << \"<center><h1>404 Not Found</h1></center>\\n\";\n    NotFound_tmp_stream << \"<hr><center>drogon/\";\n    NotFound_tmp_stream << NotFound_view_data.get<std::string>(\"version\");\n    NotFound_tmp_stream << \"</center>\\n\";\n    NotFound_tmp_stream << \"</body>\\n\";\n    NotFound_tmp_stream << \"</html>\\n\";\n    NotFound_tmp_stream << \"<!-- a padding to disable MSIE and Chrome friendly \"\n                           \"error page -->\\n\";\n    NotFound_tmp_stream << \"<!-- a padding to disable MSIE and Chrome friendly \"\n                           \"error page -->\\n\";\n    NotFound_tmp_stream << \"<!-- a padding to disable MSIE and Chrome friendly \"\n                           \"error page -->\\n\";\n    NotFound_tmp_stream << \"<!-- a padding to disable MSIE and Chrome friendly \"\n                           \"error page -->\\n\";\n    NotFound_tmp_stream << \"<!-- a padding to disable MSIE and Chrome friendly \"\n                           \"error page -->\\n\";\n    NotFound_tmp_stream << \"<!-- a padding to disable MSIE and Chrome friendly \"\n                           \"error page -->\\n\";\n    return NotFound_tmp_stream.str();\n}\n"
  },
  {
    "path": "lib/src/PluginsManager.cc",
    "content": "/**\n *\n *  PluginsManager.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"PluginsManager.h\"\n#include <trantor/utils/Logger.h>\n\nusing namespace drogon;\n\nPluginsManager::~PluginsManager()\n{\n    // Shut down all plugins in reverse order of initialization.\n    for (auto iter = initializedPlugins_.rbegin();\n         iter != initializedPlugins_.rend();\n         iter++)\n    {\n        (*iter)->shutdown();\n    }\n}\n\nvoid PluginsManager::initializeAllPlugins(\n    const Json::Value &configs,\n    const std::function<void(PluginBase *)> &forEachCallback)\n{\n    assert(configs.isArray());\n    std::vector<PluginBase *> plugins;\n    for (auto &config : configs)\n    {\n        auto name = config.get(\"name\", \"\").asString();\n        if (name.empty())\n            continue;\n        createPlugin(name);\n    }\n    for (auto &config : configs)\n    {\n        auto name = config.get(\"name\", \"\").asString();\n        if (name.empty())\n            continue;\n        auto pluginPtr = getPlugin(name);\n        if (!pluginPtr)\n        {\n            continue;\n        }\n        auto configuration = config[\"config\"];\n        auto dependencies = config[\"dependencies\"];\n        pluginPtr->setConfig(configuration);\n        assert(dependencies.isArray() || dependencies.isNull());\n        if (dependencies.isArray())\n        {\n            // Is not null and is an array\n            for (auto &depName : dependencies)\n            {\n                auto *dp = getPlugin(depName.asString());\n                if (dp)\n                {\n                    pluginPtr->addDependency(dp);\n                }\n                else\n                {\n                    LOG_FATAL << \"Dependent plugin \" << depName.asString()\n                              << \" is not loaded\";\n                    abort();\n                }\n            }\n        }\n        pluginPtr->setInitializedCallback([this](PluginBase *p) {\n            LOG_TRACE << \"Plugin \" << p->className() << \" initialized!\";\n            initializedPlugins_.push_back(p);\n        });\n        plugins.push_back(pluginPtr);\n    }\n    // Initialize them, Depth first\n    for (auto plugin : plugins)\n    {\n        plugin->initialize();\n        forEachCallback(plugin);\n    }\n}\n\nvoid PluginsManager::createPlugin(const std::string &pluginName)\n{\n    auto pluginPtr = std::dynamic_pointer_cast<PluginBase>(\n        DrClassMap::newSharedObject(pluginName));\n    if (!pluginPtr)\n    {\n        LOG_ERROR << \"Plugin \" << pluginName << \" undefined!\";\n        return;\n    }\n    pluginsMap_[pluginName] = pluginPtr;\n}\n\nPluginBase *PluginsManager::getPlugin(const std::string &pluginName)\n{\n    auto iter = pluginsMap_.find(pluginName);\n    if (iter != pluginsMap_.end())\n    {\n        return iter->second.get();\n    }\n    return nullptr;\n}\n\nstd::shared_ptr<PluginBase> PluginsManager::getSharedPlugin(\n    const std::string &pluginName)\n{\n    auto iter = pluginsMap_.find(pluginName);\n    if (iter != pluginsMap_.end())\n    {\n        return iter->second;\n    }\n    return nullptr;\n}\n"
  },
  {
    "path": "lib/src/PluginsManager.h",
    "content": "/**\n *\n *  PluginsManager.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <drogon/plugins/Plugin.h>\n#include <map>\n\nnamespace drogon\n{\nusing PluginBasePtr = std::shared_ptr<PluginBase>;\n\nclass PluginsManager : trantor::NonCopyable\n{\n  public:\n    void initializeAllPlugins(\n        const Json::Value &configs,\n        const std::function<void(PluginBase *)> &forEachCallback);\n\n    PluginBase *getPlugin(const std::string &pluginName);\n\n    std::shared_ptr<PluginBase> getSharedPlugin(const std::string &pluginName);\n\n    ~PluginsManager();\n\n  private:\n    void createPlugin(const std::string &pluginName);\n    std::map<std::string, PluginBasePtr> pluginsMap_;\n    std::vector<PluginBase *> initializedPlugins_;\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/PromExporter.cc",
    "content": "#include <drogon/plugins/PromExporter.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/utils/monitoring/Counter.h>\n#include <drogon/utils/monitoring/Gauge.h>\n#include <drogon/utils/monitoring/Histogram.h>\n#include <drogon/utils/monitoring/Collector.h>\n\nusing namespace drogon;\nusing namespace drogon::monitoring;\nusing namespace drogon::plugin;\n\nvoid PromExporter::initAndStart(const Json::Value &config)\n{\n    path_ = config.get(\"path\", path_).asString();\n    LOG_TRACE << path_;\n    auto &app = drogon::app();\n    std::weak_ptr<PromExporter> weakPtr = shared_from_this();\n    app.registerHandler(\n        path_,\n        [weakPtr](const HttpRequestPtr &req,\n                  std::function<void(const HttpResponsePtr &)> &&callback) {\n            auto thisPtr = weakPtr.lock();\n            if (!thisPtr)\n            {\n                auto resp = HttpResponse::newNotFoundResponse(req);\n                callback(resp);\n                return;\n            }\n            auto resp = HttpResponse::newHttpResponse();\n            resp->setBody(thisPtr->exportMetrics());\n            resp->setContentTypeCode(CT_TEXT_PLAIN);\n            resp->setExpiredTime(5);\n            callback(resp);\n        },\n        {Get, Options},\n        \"PromExporter\");\n    if (config.isMember(\"collectors\"))\n    {\n        std::lock_guard<std::mutex> guard(mutex_);\n        auto &collectors = config[\"collectors\"];\n        if (collectors.isArray())\n        {\n            for (auto const &collector : collectors)\n            {\n                if (collector.isObject())\n                {\n                    auto name = collector[\"name\"].asString();\n                    auto type = collector[\"type\"].asString();\n                    auto help = collector[\"help\"].asString();\n                    auto labels = collector[\"labels\"];\n                    if (labels.isArray())\n                    {\n                        std::vector<std::string> labelNames;\n                        for (auto const &label : labels)\n                        {\n                            if (label.isString())\n                            {\n                                labelNames.push_back(label.asString());\n                            }\n                            else\n                            {\n                                LOG_ERROR << \"label name must be a string!\";\n                            }\n                        }\n                        if (type == \"counter\")\n                        {\n                            auto counterCollector =\n                                std::make_shared<Collector<Counter>>(\n                                    name, help, labelNames);\n                            collectors_.insert(\n                                std::make_pair(name, counterCollector));\n                        }\n                        else if (type == \"gauge\")\n                        {\n                            auto gaugeCollector =\n                                std::make_shared<Collector<Gauge>>(name,\n                                                                   help,\n                                                                   labelNames);\n                            collectors_.insert(\n                                std::make_pair(name, gaugeCollector));\n                        }\n                        else if (type == \"histogram\")\n                        {\n                            auto histogramCollector =\n                                std::make_shared<Collector<Histogram>>(\n                                    name, help, labelNames);\n                            collectors_.insert(\n                                std::make_pair(name, histogramCollector));\n                        }\n                        else\n                        {\n                            LOG_ERROR << \"Unknown collector type: \" << type;\n                        }\n                    }\n                    else\n                    {\n                        LOG_ERROR << \"labels must be an array!\";\n                    }\n                }\n                else\n                {\n                    LOG_ERROR << \"collector must be an object!\";\n                }\n            }\n        }\n        else\n        {\n            LOG_ERROR << \"collectors must be an array!\";\n        }\n    }\n}\n\nstatic std::string exportCollector(\n    const std::shared_ptr<CollectorBase> &collector)\n{\n    auto sampleGroups = collector->collect();\n    std::string res;\n    res.append(\"# HELP \")\n        .append(collector->name())\n        .append(\" \")\n        .append(collector->help())\n        .append(\"\\n\");\n    res.append(\"# TYPE \")\n        .append(collector->name())\n        .append(\" \")\n        .append(collector->type())\n        .append(\"\\n\");\n    for (auto const &sampleGroup : sampleGroups)\n    {\n        auto const &metricPtr = sampleGroup.metric;\n        auto const &samples = sampleGroup.samples;\n        for (auto &sample : samples)\n        {\n            res.append(sample.name);\n            if (!sample.exLabels.empty() || !metricPtr->labels().empty())\n            {\n                res.append(\"{\");\n                for (auto const &label : metricPtr->labels())\n                {\n                    res.append(label.first)\n                        .append(\"=\\\"\")\n                        .append(label.second)\n                        .append(\"\\\",\");\n                }\n                for (auto const &label : sample.exLabels)\n                {\n                    res.append(label.first)\n                        .append(\"=\\\"\")\n                        .append(label.second)\n                        .append(\"\\\",\");\n                }\n                res.pop_back();\n                res.append(\"}\");\n            }\n            res.append(\" \").append(std::to_string(sample.value));\n            if (sample.timestamp.microSecondsSinceEpoch() > 0)\n            {\n                res.append(\" \")\n                    .append(std::to_string(\n                        sample.timestamp.microSecondsSinceEpoch() / 1000))\n                    .append(\"\\n\");\n            }\n            else\n            {\n                res.append(\"\\n\");\n            }\n        }\n    }\n    return res;\n}\n\nstd::string PromExporter::exportMetrics()\n{\n    std::lock_guard<std::mutex> guard(mutex_);\n    std::string result;\n    for (auto const &collector : collectors_)\n    {\n        result.append(exportCollector(collector.second));\n    }\n    return result;\n}\n\nvoid PromExporter::registerCollector(\n    const std::shared_ptr<drogon::monitoring::CollectorBase> &collector)\n{\n    std::lock_guard<std::mutex> guard(mutex_);\n    if (collectors_.find(collector->name()) != collectors_.end())\n    {\n        throw std::runtime_error(\"The collector named \" + collector->name() +\n                                 \" has been registered!\");\n    }\n    collectors_.insert(std::make_pair(collector->name(), collector));\n}\n\nstd::shared_ptr<drogon::monitoring::CollectorBase> PromExporter::getCollector(\n    const std::string &name) const noexcept(false)\n{\n    std::lock_guard<std::mutex> guard(mutex_);\n    auto iter = collectors_.find(name);\n    if (iter != collectors_.end())\n    {\n        return iter->second;\n    }\n    else\n    {\n        throw std::runtime_error(\"Can't find the collector named \" + name);\n    }\n}\n"
  },
  {
    "path": "lib/src/RangeParser.cc",
    "content": "/**\n *\n *  RangeParser.h\n *  He, Wanchen\n *\n *  Copyright 2021, He,Wanchen.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"RangeParser.h\"\n\n#include <limits>\n\nusing namespace drogon;\n\nstatic constexpr size_t MAX_SIZE = std::numeric_limits<size_t>::max();\nstatic constexpr size_t MAX_TEN = MAX_SIZE / 10;\nstatic constexpr size_t MAX_DIGIT = MAX_SIZE % 10;\n\n// clang-format off\n#define DR_SKIP_WHITESPACE(p) while (*p == ' ') { ++(p); }\n#define DR_ISDIGIT(p) ('0' <= *(p) && *(p) <= '9')\n#define DR_WOULD_OVERFLOW(base, digit) \\\n    (static_cast<size_t>(base) > MAX_TEN || \\\n    (static_cast<size_t>(base) >= MAX_TEN && \\\n    static_cast<size_t>(digit) - '0' > MAX_DIGIT))\n\n// clang-format on\n\n/** Following formats are valid range header according to rfc7233`\n * Range: <unit>=<start>-\n * Range: <unit>=<start>-<end>\n * Range: <unit>=<start>-<end>, <start>-<end>\n * Range: <unit>=<start>-<end>, <start>-<end>, <start>-<end>\n * Range: <unit>=-<suffix-length>\n */\n\nFileRangeParseResult drogon::parseRangeHeader(const std::string &rangeStr,\n                                              size_t contentLength,\n                                              std::vector<FileRange> &ranges)\n{\n    if (rangeStr.size() < 7 || rangeStr.compare(0, 6, \"bytes=\") != 0)\n    {\n        return InvalidRange;\n    }\n    const char *iter = rangeStr.c_str() + 6;\n\n    size_t totalSize = 0;\n    while (true)\n    {\n        size_t start = 0;\n        size_t end = 0;\n        // If this is a suffix range: <unit>=-<suffix-length>\n        bool isSuffix = false;\n\n        DR_SKIP_WHITESPACE(iter);\n\n        if (*iter == '-')\n        {\n            isSuffix = true;\n            ++iter;\n        }\n        // Parse start\n        else\n        {\n            if (!DR_ISDIGIT(iter))\n            {\n                return InvalidRange;\n            }\n            while (DR_ISDIGIT(iter))\n            {\n                // integer out of range\n                if (DR_WOULD_OVERFLOW(start, *iter))\n                {\n                    return NotSatisfiable;\n                }\n                start = start * 10 + (*iter++ - '0');\n            }\n            DR_SKIP_WHITESPACE(iter);\n            // should be separator now\n            if (*iter++ != '-')\n            {\n                return InvalidRange;\n            }\n            DR_SKIP_WHITESPACE(iter);\n            // If this is a prefix range <unit>=<range-start>-\n            if (*iter == ',' || *iter == '\\0')\n            {\n                end = contentLength;\n                // Handle found\n                if (start < end)\n                {\n                    if (totalSize > MAX_SIZE - (end - start))\n                    {\n                        return NotSatisfiable;\n                    }\n                    totalSize += end - start;\n                    ranges.push_back({start, end});\n                }\n                if (*iter++ != ',')\n                {\n                    break;\n                }\n                continue;\n            }\n        }\n\n        // Parse end\n        if (!DR_ISDIGIT(iter))\n        {\n            return InvalidRange;\n        }\n        while (DR_ISDIGIT(iter))\n        {\n            if (DR_WOULD_OVERFLOW(end, *iter))\n            {\n                return NotSatisfiable;\n            }\n            end = end * 10 + (*iter++ - '0');\n        }\n        DR_SKIP_WHITESPACE(iter);\n\n        if (*iter != ',' && *iter != '\\0')\n        {\n            return InvalidRange;\n        }\n        if (isSuffix)\n        {\n            start = (end < contentLength) ? contentLength - end : 0;\n            end = contentLength - 1;\n        }\n        // [start, end)\n        if (end >= contentLength)\n        {\n            end = contentLength;\n        }\n        else\n        {\n            ++end;\n        }\n\n        // handle found\n        if (start < end)\n        {\n            ranges.push_back({start, end});\n            if (totalSize > MAX_SIZE - (end - start))\n            {\n                return NotSatisfiable;\n            }\n            totalSize += end - start;\n            // We restrict the number to be under 100, to avoid malicious\n            // requests.\n            // Though rfc does not say anything about max number of ranges,\n            // it does mention that server can ignore range header freely.\n            if (ranges.size() > 100)\n            {\n                return InvalidRange;\n            }\n        }\n        if (*iter++ != ',')\n        {\n            break;\n        }\n    }\n\n    if (ranges.size() == 0 || totalSize > contentLength)\n    {\n        return NotSatisfiable;\n    }\n    return ranges.size() == 1 ? SinglePart : MultiPart;\n}\n\n#undef DR_SKIP_WHITESPACE\n#undef DR_ISDIGIT\n#undef DR_WOULD_OVERFLOW\n"
  },
  {
    "path": "lib/src/RangeParser.h",
    "content": "/**\n *\n *  RangeParser.h\n *  He, Wanchen\n *\n *  Copyright 2021, He,Wanchen.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include <string>\n#include <vector>\n#include <sys/types.h>\n\nnamespace drogon\n{\n// [start, end)\nstruct FileRange\n{\n    size_t start;\n    size_t end;\n};\n\nenum FileRangeParseResult\n{\n    InvalidRange = -1,\n    NotSatisfiable = 0,\n    SinglePart = 1,\n    MultiPart = 2\n};\n\nFileRangeParseResult parseRangeHeader(const std::string &rangeStr,\n                                      size_t contentLength,\n                                      std::vector<FileRange> &ranges);\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/RateLimiter.cc",
    "content": "#include <drogon/RateLimiter.h>\n#include \"FixedWindowRateLimiter.h\"\n#include \"SlidingWindowRateLimiter.h\"\n#include \"TokenBucketRateLimiter.h\"\n\nusing namespace drogon;\n\nRateLimiterPtr RateLimiter::newRateLimiter(\n    RateLimiterType type,\n    size_t capacity,\n    std::chrono::duration<double> timeUnit)\n{\n    switch (type)\n    {\n        case RateLimiterType::kFixedWindow:\n            return std::make_shared<FixedWindowRateLimiter>(capacity, timeUnit);\n        case RateLimiterType::kSlidingWindow:\n            return std::make_shared<SlidingWindowRateLimiter>(capacity,\n                                                              timeUnit);\n        case RateLimiterType::kTokenBucket:\n            return std::make_shared<TokenBucketRateLimiter>(capacity, timeUnit);\n    }\n    return std::make_shared<TokenBucketRateLimiter>(capacity, timeUnit);\n}\n"
  },
  {
    "path": "lib/src/RealIpResolver.cc",
    "content": "/**\n *\n *  @file RealIpResolver.cc\n *  @author Nitromelon\n *\n *  Copyright 2022, Nitromelon. All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/drogon.h>\n#include <trantor/utils/Logger.h>\n#include <drogon/plugins/RealIpResolver.h>\n\nusing namespace drogon;\nusing namespace drogon::plugin;\n\nstruct XForwardedForParser : public trantor::NonCopyable\n{\n    explicit XForwardedForParser(std::string value)\n        : value_(std::move(value)), start_(value_.c_str()), len_(value_.size())\n    {\n    }\n\n    std::string getNext()\n    {\n        if (len_ == 0)\n        {\n            return {};\n        }\n        // Skip trailing separators\n        const char *cur;\n        for (cur = start_ + len_ - 1; cur > start_; --cur, --len_)\n        {\n            if (*cur != ' ' && *cur != ',')\n            {\n                break;\n            }\n        }\n        for (; cur > start_; --cur)\n        {\n            if (*cur == ' ' || *cur == ',')\n            {\n                ++cur;\n                break;\n            }\n        }\n        std::string ip{cur, len_ - (cur - start_)};\n        len_ = cur == start_ ? 0 : cur - start_ - 1;\n        return ip;\n    }\n\n  private:\n    std::string value_;\n    const char *start_;\n    size_t len_;\n};\n\nstatic trantor::InetAddress parseAddress(const std::string &addr)\n{\n    auto pos = addr.find(':');\n    uint16_t port = 0;\n    if (pos == std::string::npos)\n    {\n        return trantor::InetAddress(addr, 0);\n    }\n    try\n    {\n        port = std::stoi(addr.substr(pos + 1));\n    }\n    catch (const std::exception &ex)\n    {\n        (void)ex;\n        LOG_ERROR << \"Error in ipv4 address: \" + addr;\n        port = 0;\n    }\n    return trantor::InetAddress(addr.substr(0, pos), port);\n}\n\nvoid RealIpResolver::initAndStart(const Json::Value &config)\n{\n    fromHeader_ = config.get(\"from_header\", \"x-forwarded-for\").asString();\n    attributeKey_ = config.get(\"attribute_key\", \"real-ip\").asString();\n\n    std::transform(fromHeader_.begin(),\n                   fromHeader_.end(),\n                   fromHeader_.begin(),\n                   [](unsigned char c) { return tolower(c); });\n    if (fromHeader_ == \"x-forwarded-for\")\n    {\n        useXForwardedFor_ = true;\n    }\n\n    const Json::Value &trustIps = config[\"trust_ips\"];\n    if (!trustIps.isNull() && !trustIps.isArray())\n    {\n        throw std::runtime_error(\"Invalid trusted_ips. Should be array.\");\n    }\n    for (const auto &ipOrCidr : trustIps)\n    {\n        trustCIDRs_.emplace_back(ipOrCidr.asString());\n    }\n\n    drogon::app().registerPreRoutingAdvice([this](const HttpRequestPtr &req) {\n        const auto &headers = req->headers();\n        auto ipHeaderFind = headers.find(fromHeader_);\n        const trantor::InetAddress &peerAddr = req->getPeerAddr();\n        if (ipHeaderFind == headers.end() || !matchCidr(peerAddr, trustCIDRs_))\n        {\n            // Target header is empty, or\n            // direct peer is already a non-proxy\n            req->attributes()->insert(attributeKey_, peerAddr);\n            return;\n        }\n        const std::string &ipHeader = ipHeaderFind->second;\n        // Use a header field which contains a single ip\n        if (!useXForwardedFor_)\n        {\n            trantor::InetAddress addr = parseAddress(ipHeader);\n            if (addr.isUnspecified())\n            {\n                req->attributes()->insert(attributeKey_, peerAddr);\n            }\n            else\n            {\n                req->attributes()->insert(attributeKey_, addr);\n            }\n            return;\n        }\n        // Use x-forwarded-for header, which may contains multiple ip address,\n        // separated by comma\n        XForwardedForParser parser(ipHeader);\n        std::string ip;\n        while (!(ip = parser.getNext()).empty())\n        {\n            trantor::InetAddress addr = parseAddress(ip);\n            if (addr.isUnspecified() || matchCidr(addr, trustCIDRs_))\n            {\n                continue;\n            }\n            req->attributes()->insert(attributeKey_, addr);\n            return;\n        }\n        // No match, use peerAddr\n        req->attributes()->insert(attributeKey_, peerAddr);\n    });\n}\n\nvoid RealIpResolver::shutdown()\n{\n}\n\nconst trantor::InetAddress &RealIpResolver::GetRealAddr(\n    const HttpRequestPtr &req)\n{\n    auto *plugin = app().getPlugin<drogon::plugin::RealIpResolver>();\n    if (!plugin)\n    {\n        return req->getPeerAddr();\n    }\n    return plugin->getRealAddr(req);\n}\n\nconst trantor::InetAddress &RealIpResolver::getRealAddr(\n    const HttpRequestPtr &req) const\n{\n    const std::shared_ptr<Attributes> &attributesPtr = req->getAttributes();\n    if (!attributesPtr->find(attributeKey_))\n    {\n        return req->getPeerAddr();\n    }\n    return attributesPtr->get<trantor::InetAddress>(attributeKey_);\n}\n\nbool RealIpResolver::matchCidr(const trantor::InetAddress &addr,\n                               const CIDRs &trustCIDRs)\n{\n    for (const auto &cidr : trustCIDRs)\n    {\n        if ((addr.ipNetEndian() & cidr.mask_) == cidr.addr_)\n        {\n            return true;\n        }\n    }\n    return false;\n}\n\nRealIpResolver::CIDR::CIDR(const std::string &ipOrCidr)\n{\n    // Find CIDR slash\n    auto pos = ipOrCidr.find('/');\n    std::string ipv4;\n    if (pos != std::string::npos)\n    {\n        // parameter is a CIDR block\n        std::string prefixLen = ipOrCidr.substr(pos + 1);\n        ipv4 = ipOrCidr.substr(0, pos);\n        uint16_t prefix = std::stoi(prefixLen);\n        if (prefix > 32)\n        {\n            throw std::runtime_error(\"Bad CIDR block: \" + ipOrCidr);\n        }\n        mask_ = htonl(0xffffffffu << (32 - prefix));\n    }\n    else\n    {\n        // parameter is an IP\n        ipv4 = ipOrCidr;\n        mask_ = 0xffffffffu;\n    }\n\n    trantor::InetAddress addr(ipv4, 0);\n    if (addr.isIpV6())\n    {\n        throw std::runtime_error(\"Ipv6 is not supported by RealIpResolver.\");\n    }\n    if (addr.isUnspecified())\n    {\n        throw std::runtime_error(\"Bad ipv4 address: \" + ipv4);\n    }\n    addr_ = addr.ipNetEndian() & mask_;\n}\n"
  },
  {
    "path": "lib/src/Redirector.cc",
    "content": "/**\n *\n *  @file Redirector.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/drogon.h>\n#include <drogon/plugins/Redirector.h>\n\nusing namespace drogon;\nusing namespace drogon::plugin;\n\nvoid Redirector::initAndStart(const Json::Value &config)\n{\n    auto weakPtr = std::weak_ptr<Redirector>(shared_from_this());\n    drogon::app().registerSyncAdvice(\n        [weakPtr](const HttpRequestPtr &req) -> HttpResponsePtr {\n            auto thisPtr = weakPtr.lock();\n            if (!thisPtr)\n            {\n                return HttpResponsePtr{};\n            }\n            std::string protocol, host;\n            bool pathChanged{false};\n            for (auto &handler : thisPtr->pathRewriteHandlers_)\n            {\n                pathChanged |= handler(req);\n            }\n            for (auto &handler : thisPtr->handlers_)\n            {\n                if (!handler(req, protocol, host, pathChanged))\n                {\n                    return HttpResponse::newNotFoundResponse(req);\n                }\n            }\n            if (!protocol.empty() || !host.empty() || pathChanged)\n            {\n                std::string url;\n                if (protocol.empty())\n                {\n                    if (!host.empty())\n                    {\n                        url = req->isOnSecureConnection() ? \"https://\"\n                                                          : \"http://\";\n                        url.append(host);\n                    }\n                }\n                else\n                {\n                    url = std::move(protocol);\n                    if (!host.empty())\n                    {\n                        url.append(host);\n                    }\n                    else\n                    {\n                        url.append(req->getHeader(\"host\"));\n                    }\n                }\n                url.append(req->path());\n                auto &query = req->query();\n                if (!query.empty())\n                {\n                    url.append(\"?\").append(query);\n                }\n                return HttpResponse::newRedirectionResponse(url);\n            }\n            for (auto &handler : thisPtr->forwardHandlers_)\n            {\n                handler(req);\n            }\n            return HttpResponsePtr{};\n        });\n}\n\nvoid Redirector::shutdown()\n{\n    LOG_TRACE << \"Redirector plugin is shutdown!\";\n}\n"
  },
  {
    "path": "lib/src/RedisClientManager.h",
    "content": "/**\n *\n *  RedisClientManager.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/nosql/RedisClient.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/IOThreadStorage.h>\n#include <trantor/utils/NonCopyable.h>\n#include <trantor/net/EventLoop.h>\n#include <string>\n#include <memory>\n\nnamespace drogon\n{\nnamespace nosql\n{\nclass RedisClientManager : public trantor::NonCopyable\n{\n  public:\n    void createRedisClients(const std::vector<trantor::EventLoop *> &ioLoops);\n\n    RedisClientPtr getRedisClient(const std::string &name)\n    {\n        assert(redisClientsMap_.find(name) != redisClientsMap_.end());\n        return redisClientsMap_[name];\n    }\n\n    RedisClientPtr getFastRedisClient(const std::string &name)\n    {\n        auto iter = redisFastClientsMap_.find(name);\n        assert(iter != redisFastClientsMap_.end());\n        return iter->second.getThreadData();\n    }\n\n    void createRedisClient(const std::string &name,\n                           const std::string &host,\n                           unsigned short port,\n                           const std::string &username,\n                           const std::string &password,\n                           size_t connectionNum,\n                           bool isFast,\n                           double timeout,\n                           unsigned int db);\n    // bool areAllRedisClientsAvailable() const noexcept;\n\n    ~RedisClientManager();\n\n  private:\n    std::map<std::string, RedisClientPtr> redisClientsMap_;\n    std::map<std::string, IOThreadStorage<RedisClientPtr>> redisFastClientsMap_;\n\n    struct RedisInfo\n    {\n        std::string name_;\n        std::string addr_;\n        std::string username_;\n        std::string password_;\n        unsigned short port_;\n        bool isFast_;\n        size_t connectionNumber_;\n        double timeout_;\n        unsigned int db_;\n    };\n\n    std::vector<RedisInfo> redisInfos_;\n};\n}  // namespace nosql\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/RedisClientManagerSkipped.cc",
    "content": "/**\n *\n *  RedisClientManagerSkipped.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"RedisClientManager.h\"\n#include <drogon/config.h>\n#include <drogon/utils/Utilities.h>\n#include <algorithm>\n#include <cstdlib>\n\nusing namespace drogon::nosql;\nusing namespace drogon;\n\nvoid RedisClientManager::createRedisClients(\n    const std::vector<trantor::EventLoop *> & /*ioloops*/)\n{\n    return;\n}\n\nvoid RedisClientManager::createRedisClient(const std::string & /*name*/,\n                                           const std::string & /*host*/,\n                                           unsigned short /*port*/,\n                                           const std::string & /*username*/,\n                                           const std::string & /*password*/,\n                                           size_t /*connectionNum*/,\n                                           bool /*isFast*/,\n                                           double /*timeout*/,\n                                           unsigned int /*db*/)\n{\n    LOG_FATAL << \"Redis is not supported by drogon, please install the \"\n                 \"hiredis library first.\";\n    abort();\n}\n\n// bool RedisClientManager::areAllRedisClientsAvailable() const noexcept\n// {\n//     LOG_FATAL << \"Redis is supported by drogon, please install the \"\n//                  \"hiredis library first.\";\n//     abort();\n// }\n\nRedisClientManager::~RedisClientManager()\n{\n}\n"
  },
  {
    "path": "lib/src/RedisClientSkipped.cc",
    "content": "/**\n *\n *  RedisClientSkipped.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"drogon/nosql/RedisClient.h\"\n\nnamespace drogon\n{\nnamespace nosql\n{\nstd::shared_ptr<RedisClient> RedisClient::newRedisClient(\n    const trantor::InetAddress & /*serverAddress*/,\n    size_t /*numberOfConnections*/,\n    const std::string & /*password*/,\n    const unsigned int /*db*/,\n    const std::string & /*username*/)\n{\n    LOG_FATAL << \"Redis is not supported by drogon, please install the \"\n                 \"hiredis library first.\";\n    abort();\n}\n}  // namespace nosql\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/RedisResultSkipped.cc",
    "content": "/**\n *\n *  RedisClientSkipped.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"drogon/nosql/RedisResult.h\"\n#include \"trantor/utils/Logger.h\"\n\nnamespace drogon\n{\nnamespace nosql\n{\nstd::string RedisResult::getStringForDisplaying() const noexcept\n{\n    LOG_FATAL << \"Redis is not supported by drogon, please install the \"\n                 \"hiredis library first.\";\n    abort();\n}\n\nstd::string RedisResult::getStringForDisplayingWithIndent(\n    size_t /*indent*/) const noexcept\n{\n    LOG_FATAL << \"Redis is not supported by drogon, please install the \"\n                 \"hiredis library first.\";\n    abort();\n}\n\nstd::string RedisResult::asString() const noexcept(false)\n{\n    LOG_FATAL << \"Redis is not supported by drogon, please install the \"\n                 \"hiredis library first.\";\n    abort();\n}\n\nRedisResultType RedisResult::type() const noexcept\n{\n    LOG_FATAL << \"Redis is not supported by drogon, please install the \"\n                 \"hiredis library first.\";\n    abort();\n}\n\nstd::vector<RedisResult> RedisResult::asArray() const noexcept(false)\n{\n    LOG_FATAL << \"Redis is not supported by drogon, please install the \"\n                 \"hiredis library first.\";\n    abort();\n}\n\nlong long RedisResult::asInteger() const noexcept(false)\n{\n    LOG_FATAL << \"Redis is not supported by drogon, please install the \"\n                 \"hiredis library first.\";\n    abort();\n}\n\nbool RedisResult::isNil() const noexcept\n{\n    LOG_FATAL << \"Redis is not supported by drogon, please install the \"\n                 \"hiredis library first.\";\n    abort();\n}\n}  // namespace nosql\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/RequestStream.cc",
    "content": "/**\n *\n *  @file RequestStream.cc\n *  @author Nitromelon\n *\n *  Copyright 2024, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"MultipartStreamParser.h\"\n#include \"HttpRequestImpl.h\"\n\n#include <drogon/RequestStream.h>\n#include <variant>\n\nnamespace drogon\n{\nclass RequestStreamImpl : public RequestStream\n{\n  public:\n    RequestStreamImpl(const HttpRequestImplPtr &req) : weakReq_(req)\n    {\n    }\n\n    ~RequestStreamImpl() override\n    {\n        if (isSet_.exchange(true))\n        {\n            return;\n        }\n\n        // Drop all data if no reader is set\n        if (auto req = weakReq_.lock())\n        {\n            setHandlerInLoop(req, RequestStreamReader::newNullReader());\n        }\n    }\n\n    void setStreamReader(RequestStreamReaderPtr reader) override\n    {\n        if (isSet_.exchange(true))\n        {\n            return;\n        }\n\n        if (auto req = weakReq_.lock())\n        {\n            setHandlerInLoop(req, std::move(reader));\n        }\n    }\n\n    void setHandlerInLoop(const HttpRequestImplPtr &req,\n                          RequestStreamReaderPtr reader)\n    {\n        if (!req->isStreamMode())\n        {\n            return;\n        }\n        auto loop = req->getLoop();\n        if (loop->isInLoopThread())\n        {\n            req->setStreamReader(std::move(reader));\n        }\n        else\n        {\n            loop->queueInLoop([req, reader = std::move(reader)]() mutable {\n                req->setStreamReader(std::move(reader));\n            });\n        }\n    }\n\n  private:\n    std::weak_ptr<HttpRequestImpl> weakReq_;\n    std::atomic_bool isSet_{false};\n};\n\nnamespace internal\n{\nRequestStreamPtr createRequestStream(const HttpRequestPtr &req)\n{\n    auto reqImpl = std::static_pointer_cast<HttpRequestImpl>(req);\n    if (!reqImpl->isStreamMode())\n    {\n        return nullptr;\n    }\n    return std::make_shared<RequestStreamImpl>(\n        std::static_pointer_cast<HttpRequestImpl>(req));\n}\n}  // namespace internal\n\n/**\n * A default implementation for convenience\n */\nclass DefaultStreamReader : public RequestStreamReader\n{\n  public:\n    DefaultStreamReader(StreamDataCallback dataCb,\n                        StreamFinishCallback finishCb)\n        : dataCb_(std::move(dataCb)), finishCb_(std::move(finishCb))\n    {\n    }\n\n    void onStreamData(const char *data, size_t length) override\n    {\n        dataCb_(data, length);\n    }\n\n    void onStreamFinish(std::exception_ptr ex) override\n    {\n        finishCb_(std::move(ex));\n    }\n\n  private:\n    StreamDataCallback dataCb_;\n    StreamFinishCallback finishCb_;\n};\n\n/**\n * Drops all data\n */\nclass NullStreamReader : public RequestStreamReader\n{\n  public:\n    void onStreamData(const char *, size_t length) override\n    {\n    }\n\n    void onStreamFinish(std::exception_ptr) override\n    {\n    }\n};\n\n/**\n * Parse multipart data and return actual content\n */\nclass MultipartStreamReader : public RequestStreamReader\n{\n  public:\n    MultipartStreamReader(const std::string &contentType,\n                          MultipartHeaderCallback headerCb,\n                          StreamDataCallback dataCb,\n                          StreamFinishCallback finishCb)\n        : parser_(contentType),\n          headerCb_(std::move(headerCb)),\n          dataCb_(std::move(dataCb)),\n          finishCb_(std::move(finishCb))\n    {\n    }\n\n    void onStreamData(const char *data, size_t length) override\n    {\n        if (!parser_.isValid() || parser_.isFinished())\n        {\n            return;\n        }\n        parser_.parse(data, length, headerCb_, dataCb_);\n        if (!parser_.isValid())\n        {\n            // TODO: should we mix stream error and user error?\n            finishCb_(std::make_exception_ptr(\n                std::runtime_error(\"invalid multipart data\")));\n        }\n        else if (parser_.isFinished())\n        {\n            finishCb_({});\n        }\n    }\n\n    void onStreamFinish(std::exception_ptr ex) override\n    {\n        if (!parser_.isValid() || parser_.isFinished())\n        {\n            return;\n        }\n\n        if (!ex)\n        {\n            finishCb_(std::make_exception_ptr(\n                std::runtime_error(\"incomplete multipart data\")));\n        }\n        else\n        {\n            finishCb_(std::move(ex));\n        }\n    }\n\n  private:\n    MultipartStreamParser parser_;\n    MultipartHeaderCallback headerCb_;\n    StreamDataCallback dataCb_;\n    StreamFinishCallback finishCb_;\n};\n\nRequestStreamReaderPtr RequestStreamReader::newReader(\n    StreamDataCallback dataCb,\n    StreamFinishCallback finishCb)\n{\n    return std::make_shared<DefaultStreamReader>(std::move(dataCb),\n                                                 std::move(finishCb));\n}\n\nRequestStreamReaderPtr RequestStreamReader::newNullReader()\n{\n    return std::make_shared<NullStreamReader>();\n}\n\nRequestStreamReaderPtr RequestStreamReader::newMultipartReader(\n    const HttpRequestPtr &req,\n    MultipartHeaderCallback headerCb,\n    StreamDataCallback dataCb,\n    StreamFinishCallback finishCb)\n{\n    return std::make_shared<MultipartStreamReader>(req->getHeader(\n                                                       \"content-type\"),\n                                                   std::move(headerCb),\n                                                   std::move(dataCb),\n                                                   std::move(finishCb));\n}\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/SecureSSLRedirector.cc",
    "content": "/**\n *\n *  drogon_plugin_SecureSSLRedirector.cc\n *\n */\n#include <drogon/drogon.h>\n#include <drogon/plugins/SecureSSLRedirector.h>\n#include <drogon/plugins/Redirector.h>\n#include <cstddef>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::plugin;\n\nvoid SecureSSLRedirector::initAndStart(const Json::Value &config)\n{\n    if (config.isMember(\"ssl_redirect_exempt\"))\n    {\n        if (config[\"ssl_redirect_exempt\"].isArray())\n        {\n            const auto &exempts = config[\"ssl_redirect_exempt\"];\n            size_t exemptsCount = exempts.size();\n            if (exemptsCount)\n            {\n                std::string regexString;\n                size_t len = 0;\n                for (const auto &exempt : exempts)\n                {\n                    assert(exempt.isString());\n                    len += exempt.size();\n                }\n                regexString.reserve((exemptsCount * (1 + 2)) - 1 + len);\n\n                const auto last = --exempts.end();\n                for (auto exempt = exempts.begin(); exempt != last; ++exempt)\n                    regexString.append(\"(\")\n                        .append(exempt->asString())\n                        .append(\")|\");\n                regexString.append(\"(\").append(last->asString()).append(\")\");\n\n                exemptRegex_ = std::regex(regexString);\n                regexFlag_ = true;\n            }\n        }\n        else if (config[\"ssl_redirect_exempt\"].isString())\n        {\n            exemptRegex_ = std::regex(config[\"ssl_redirect_exempt\"].asString());\n            regexFlag_ = true;\n        }\n        else\n        {\n            LOG_ERROR\n                << \"ssl_redirect_exempt must be a string or string array!\";\n        }\n    }\n    secureHost_ = config.get(\"secure_ssl_host\", \"\").asString();\n    std::weak_ptr<SecureSSLRedirector> weakPtr = shared_from_this();\n    auto redirector = drogon::app().getPlugin<Redirector>();\n    if (!redirector)\n    {\n        LOG_ERROR << \"Redirector plugin is not found!\";\n        return;\n    }\n    redirector->registerRedirectHandler(\n        [weakPtr](const drogon::HttpRequestPtr &req,\n                  std::string &protocol,\n                  std::string &host,\n                  bool &) -> bool {\n            auto thisPtr = weakPtr.lock();\n            if (!thisPtr)\n            {\n                return false;\n            }\n            return thisPtr->redirectingAdvice(req, protocol, host);\n        });\n}\n\nvoid SecureSSLRedirector::shutdown()\n{\n    /// Shutdown the plugin\n}\n\nbool SecureSSLRedirector::redirectingAdvice(const HttpRequestPtr &req,\n                                            std::string &protocol,\n                                            std::string &host) const\n{\n    if (req->isOnSecureConnection() || protocol == \"https://\")\n    {\n        return true;\n    }\n    else if (regexFlag_)\n    {\n        std::smatch regexResult;\n        if (std::regex_match(req->path(), regexResult, exemptRegex_))\n        {\n            return true;\n        }\n        else\n        {\n            return redirectToSSL(req, protocol, host);\n        }\n    }\n    else\n    {\n        return redirectToSSL(req, protocol, host);\n    }\n}\n\nbool SecureSSLRedirector::redirectToSSL(const HttpRequestPtr &req,\n                                        std::string &protocol,\n                                        std::string &host) const\n{\n    if (!secureHost_.empty())\n    {\n        host = secureHost_;\n        protocol = \"https://\";\n        return true;\n    }\n    else if (host.empty())\n    {\n        const auto &reqHost = req->getHeader(\"host\");\n        if (!reqHost.empty())\n        {\n            protocol = \"https://\";\n            return true;\n        }\n        else\n        {\n            return false;\n        }\n    }\n    else\n    {\n        protocol = \"https://\";\n        return true;\n    }\n}\n"
  },
  {
    "path": "lib/src/SessionManager.cc",
    "content": "/**\n *\n *  @file SessionManager.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"SessionManager.h\"\n\nusing namespace drogon;\n\nSessionManager::SessionManager(\n    trantor::EventLoop *loop,\n    size_t timeout,\n    const std::vector<AdviceStartSessionCallback> &startAdvices,\n    const std::vector<AdviceDestroySessionCallback> &destroyAdvices,\n    IdGeneratorCallback idGeneratorCallback)\n    : loop_(loop),\n      timeout_(timeout),\n      sessionStartAdvices_(startAdvices),\n      sessionDestroyAdvices_(destroyAdvices),\n      idGeneratorCallback_(idGeneratorCallback)\n{\n    if (timeout_ > 0)\n    {\n        size_t wheelNum = 1;\n        size_t bucketNum = 0;\n        if (timeout_ < 500)\n        {\n            bucketNum = timeout_ + 1;\n        }\n        else\n        {\n            auto tmpTimeout = timeout_;\n            bucketNum = 100;\n            while (tmpTimeout > 100)\n            {\n                ++wheelNum;\n                tmpTimeout = tmpTimeout / 100;\n            }\n        }\n\n        sessionMapPtr_ = std::unique_ptr<CacheMap<std::string, SessionPtr>>(\n            new CacheMap<std::string, SessionPtr>(\n                loop_,\n                1.0,\n                wheelNum,\n                bucketNum,\n                [this](const std::string &key) {\n                    for (auto &advice : sessionStartAdvices_)\n                    {\n                        advice(key);\n                    }\n                },\n                [this](const std::string &key) {\n                    for (auto &advice : sessionDestroyAdvices_)\n                    {\n                        advice(key);\n                    }\n                }));\n    }\n    else if (timeout_ == 0)\n    {\n        sessionMapPtr_ = std::unique_ptr<CacheMap<std::string, SessionPtr>>(\n            new CacheMap<std::string, SessionPtr>(\n                loop_,\n                0,\n                0,\n                0,\n                [this](const std::string &key) {\n                    for (auto &advice : sessionStartAdvices_)\n                    {\n                        advice(key);\n                    }\n                },\n                [this](const std::string &key) {\n                    for (auto &advice : sessionDestroyAdvices_)\n                    {\n                        advice(key);\n                    }\n                }));\n    }\n}\n\nSessionPtr SessionManager::getSession(const std::string &sessionID,\n                                      bool needToSet)\n{\n    assert(!sessionID.empty());\n    SessionPtr sessionPtr;\n    sessionMapPtr_->modify(\n        sessionID,\n        [&sessionPtr, &sessionID, needToSet](SessionPtr &sessionInCache) {\n            if (sessionInCache)\n            {\n                sessionPtr = sessionInCache;\n            }\n            else\n            {\n                sessionPtr =\n                    std::shared_ptr<Session>(new Session(sessionID, needToSet));\n                sessionInCache = sessionPtr;\n            }\n        },\n        timeout_);\n\n    return sessionPtr;\n}\n\nvoid SessionManager::changeSessionId(const SessionPtr &sessionPtr)\n{\n    auto oldId = sessionPtr->sessionId();\n    auto newId = idGeneratorCallback_();\n    sessionPtr->setSessionId(newId);\n    sessionMapPtr_->insert(newId, sessionPtr, timeout_);\n    // For requests sent before setting the new session ID to the client, we\n    // reserve the old session slot for a period of time.\n    sessionMapPtr_->runAfter(10, [this, oldId = std::move(oldId)]() {\n        LOG_TRACE << \"remove the old slot of the session\";\n        sessionMapPtr_->erase(oldId);\n    });\n}\n"
  },
  {
    "path": "lib/src/SessionManager.h",
    "content": "/**\n *\n *  @file SessionManager.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/Session.h>\n#include <drogon/drogon_callbacks.h>\n#include <drogon/CacheMap.h>\n#include <trantor/utils/NonCopyable.h>\n#include <trantor/net/EventLoop.h>\n#include <functional>\n#include <memory>\n#include <string>\n#include <mutex>\n#include <vector>\n\nnamespace drogon\n{\nclass SessionManager : public trantor::NonCopyable\n{\n  public:\n    using IdGeneratorCallback = std::function<std::string()>;\n\n    SessionManager(\n        trantor::EventLoop *loop,\n        size_t timeout,\n        const std::vector<AdviceStartSessionCallback> &startAdvices,\n        const std::vector<AdviceDestroySessionCallback> &destroyAdvices,\n        IdGeneratorCallback idGeneratorCallback);\n\n    ~SessionManager()\n    {\n        sessionMapPtr_.reset();\n    }\n\n    SessionPtr getSession(const std::string &sessionID, bool needToSet);\n    void changeSessionId(const SessionPtr &sessionPtr);\n\n  private:\n    std::unique_ptr<CacheMap<std::string, SessionPtr>> sessionMapPtr_;\n    trantor::EventLoop *loop_;\n    size_t timeout_;\n    const std::vector<AdviceStartSessionCallback> &sessionStartAdvices_;\n    const std::vector<AdviceDestroySessionCallback> &sessionDestroyAdvices_;\n    IdGeneratorCallback idGeneratorCallback_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/SharedLibManager.cc",
    "content": "/**\n *\n *  @file SharedLibManager.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"SharedLibManager.h\"\n#include <drogon/config.h>\n#include <dirent.h>\n#include <dlfcn.h>\n#include <fstream>\n#include <sstream>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <trantor/utils/Logger.h>\n#include <unistd.h>\n\n// Safe exec helper: runs a program with explicit argv, no shell involved.\n// Returns the exit status, or -1 on fork/exec failure.\nstatic int safeExec(const std::vector<std::string> &args)\n{\n    if (args.empty())\n        return -1;\n\n    std::vector<char *> argv;\n    argv.reserve(args.size() + 1);\n    for (auto &a : args)\n        argv.push_back(const_cast<char *>(a.c_str()));\n    argv.push_back(nullptr);\n\n    pid_t pid = fork();\n    if (pid == -1)\n    {\n        perror(\"fork\");\n        return -1;\n    }\n    if (pid == 0)\n    {\n        // Child: replace image with the target program.\n        execvp(argv[0], argv.data());\n        // execvp only returns on error.\n        perror(\"execvp\");\n        _exit(127);\n    }\n    // Parent: wait for child.\n    int status = 0;\n    if (waitpid(pid, &status, 0) == -1)\n    {\n        perror(\"waitpid\");\n        return -1;\n    }\n    return WIFEXITED(status) ? WEXITSTATUS(status) : -1;\n}\n\nstatic void forEachFileIn(\n    const std::string &path,\n    const std::function<void(const std::string &, const struct stat &)> &cb)\n{\n    DIR *dp;\n    struct dirent *dirp;\n    struct stat st;\n\n    /* open dirent directory */\n    if ((dp = opendir(path.c_str())) == NULL)\n    {\n        // perror(\"opendir:\");\n        LOG_ERROR << \"can't open dir,path:\" << path;\n        return;\n    }\n\n    /**\n     * read all files in this dir\n     **/\n    while ((dirp = readdir(dp)) != NULL)\n    {\n        /* ignore hidden files */\n        if (dirp->d_name[0] == '.')\n            continue;\n        /* get dirent status */\n        std::string filename = dirp->d_name;\n        std::string fullname = path;\n        fullname.append(\"/\").append(filename);\n        if (stat(fullname.c_str(), &st) == -1)\n        {\n            perror(\"stat\");\n            closedir(dp);\n            return;\n        }\n\n        /* if dirent is a directory, find files recursively */\n        if (S_ISDIR(st.st_mode))\n        {\n            forEachFileIn(fullname, cb);\n        }\n        else\n        {\n            cb(fullname, st);\n        }\n    }\n    closedir(dp);\n    return;\n}\n\nusing namespace drogon;\n\nSharedLibManager::SharedLibManager(const std::vector<std::string> &libPaths,\n                                   const std::string &outputPath)\n    : libPaths_(libPaths), outputPath_(outputPath)\n{\n    workingThread_.run();\n    timeId_ =\n        workingThread_.getLoop()->runEvery(5.0, [this]() { managerLibs(); });\n}\n\nSharedLibManager::~SharedLibManager()\n{\n    workingThread_.getLoop()->invalidateTimer(timeId_);\n}\n\nvoid SharedLibManager::managerLibs()\n{\n    for (auto const &libPath : libPaths_)\n    {\n        forEachFileIn(\n            libPath,\n            [this, libPath](const std::string &filename,\n                            const struct stat &st) {\n                auto pos = filename.rfind('.');\n                if (pos != std::string::npos)\n                {\n                    auto exName = filename.substr(pos + 1);\n                    if (exName == \"csp\")\n                    {\n                        // compile\n                        auto lockFile = filename + \".lock\";\n                        std::ifstream fin(lockFile);\n                        if (fin)\n                        {\n                            return;\n                        }\n\n                        void *oldHandle = nullptr;\n                        if (dlMap_.find(filename) != dlMap_.end())\n                        {\n#if defined __linux__ || defined __HAIKU__\n                            if (st.st_mtim.tv_sec >\n                                dlMap_[filename].mTime.tv_sec)\n#elif defined _WIN32\n                            if (st.st_mtime > dlMap_[filename].mTime.tv_sec)\n#else\n                            if (st.st_mtimespec.tv_sec >\n                                dlMap_[filename].mTime.tv_sec)\n#endif\n                            {\n                                LOG_TRACE << \"new csp file:\" << filename;\n                                oldHandle = dlMap_[filename].handle;\n                            }\n                            else\n                                return;\n                        }\n\n                        {\n                            std::ofstream fout(lockFile);\n                        }\n\n                        auto srcFile = filename.substr(0, pos);\n                        if (!outputPath_.empty())\n                        {\n                            pos = srcFile.rfind(\"/\");\n                            if (pos != std::string::npos)\n                            {\n                                srcFile = srcFile.substr(pos + 1);\n                            }\n                            srcFile = outputPath_ + \"/\" + srcFile;\n                        }\n                        auto soFile = srcFile + \".so\";\n                        DLStat dlStat;\n                        if (!shouldCompileLib(soFile, st))\n                        {\n                            LOG_TRACE << \"Using already compiled library:\"\n                                      << soFile;\n                            dlStat.handle = loadLib(soFile, oldHandle);\n                        }\n                        else\n                        {\n                            // generate source code and compile it.\n                            const std::string &outDir =\n                                !outputPath_.empty() ? outputPath_ : libPath;\n                            std::vector<std::string> genArgs = {\"drogon_ctl\",\n                                                                \"create\",\n                                                                \"view\",\n                                                                filename,\n                                                                \"-o\",\n                                                                outDir};\n                            srcFile.append(\".cc\");\n                            LOG_TRACE << \"drogon_ctl create view \" << filename\n                                      << \" -o \" << outDir;\n                            auto r = safeExec(genArgs);\n                            // TODO: handle r\n                            (void)(r);\n                            dlStat.handle =\n                                compileAndLoadLib(srcFile, oldHandle);\n                        }\n#if defined __linux__ || defined __HAIKU__\n                        dlStat.mTime = st.st_mtim;\n#elif defined _WIN32\n                        dlStat.mTime.tv_sec = st.st_mtime;\n#else\n                        dlStat.mTime = st.st_mtimespec;\n#endif\n                        if (dlStat.handle)\n                        {\n                            dlMap_[filename] = dlStat;\n                        }\n                        else\n                        {\n                            dlStat.handle = dlMap_[filename].handle;\n                            dlMap_[filename] = dlStat;\n                        }\n                        workingThread_.getLoop()->runAfter(3.5, [lockFile]() {\n                            LOG_TRACE << \"remove file \" << lockFile;\n                            if (unlink(lockFile.c_str()) == -1)\n                                perror(\"\");\n                        });\n                    }\n                }\n            });\n    }\n}\n\nvoid *SharedLibManager::compileAndLoadLib(const std::string &sourceFile,\n                                          void *oldHld)\n{\n    LOG_TRACE << \"src:\" << sourceFile;\n    auto pos = sourceFile.rfind('.');\n    auto soFile = sourceFile.substr(0, pos);\n    soFile.append(\".so\");\n\n    // Build argv without invoking a shell so that metacharacters in\n    // sourceFile or soFile cannot be interpreted by /bin/sh.\n    std::vector<std::string> compileArgs;\n    compileArgs.push_back(COMPILER_COMMAND);\n\n    // COMPILATION_FLAGS and INCLUDING_DIRS are baked in at build time from\n    // trusted CMake variables; split them on whitespace into separate tokens.\n    auto splitIntoArgs = [&](const std::string &s) {\n        std::istringstream iss(s);\n        std::string token;\n        while (iss >> token)\n            compileArgs.push_back(token);\n    };\n    compileArgs.push_back(sourceFile);\n    splitIntoArgs(COMPILATION_FLAGS);\n    splitIntoArgs(INCLUDING_DIRS);\n    if (std::string(COMPILER_ID).find(\"Clang\") != std::string::npos)\n    {\n        compileArgs.push_back(\"-shared\");\n        compileArgs.push_back(\"-fPIC\");\n        compileArgs.push_back(\"-undefined\");\n        compileArgs.push_back(\"dynamic_lookup\");\n    }\n    else\n    {\n        compileArgs.push_back(\"-shared\");\n        compileArgs.push_back(\"-fPIC\");\n        compileArgs.push_back(\"--no-gnu-unique\");\n    }\n    compileArgs.push_back(\"-o\");\n    compileArgs.push_back(soFile);\n\n    LOG_TRACE << COMPILER_COMMAND << \" \" << sourceFile << \" ... -o \" << soFile;\n\n    if (safeExec(compileArgs) == 0)\n    {\n        LOG_TRACE << \"Compiled successfully:\" << soFile;\n        return loadLib(soFile, oldHld);\n    }\n    else\n    {\n        LOG_DEBUG << \"Could not compile library.\";\n        return nullptr;\n    }\n}\n\nbool SharedLibManager::shouldCompileLib(const std::string &soFile,\n                                        const struct stat &sourceStat)\n{\n#if defined __linux__ || defined __HAIKU__\n    auto sourceModifiedTime = sourceStat.st_mtim.tv_sec;\n#elif defined _WIN32\n    auto sourceModifiedTime = sourceStat.st_mtime;\n#else\n    auto sourceModifiedTime = sourceStat.st_mtimespec.tv_sec;\n#endif\n\n    struct stat soStat;\n    if (stat(soFile.c_str(), &soStat) == -1)\n    {\n        LOG_TRACE << \"Cannot determine modification time for:\" << soFile;\n        return true;\n    }\n\n#if defined __linux__ || defined __HAIKU__\n    auto soModifiedTime = soStat.st_mtim.tv_sec;\n#elif defined _WIN32\n    auto soModifiedTime = soStat.st_mtime;\n#else\n    auto soModifiedTime = soStat.st_mtimespec.tv_sec;\n#endif\n\n    return (sourceModifiedTime > soModifiedTime);\n}\n\nvoid *SharedLibManager::loadLib(const std::string &soFile, void *oldHld)\n{\n    if (oldHld)\n    {\n        if (dlclose(oldHld) == 0)\n        {\n            LOG_TRACE << \"Successfully closed dynamic library:\" << oldHld;\n        }\n        else\n        {\n            LOG_TRACE << dlerror();\n        }\n    }\n    auto Handle = dlopen(soFile.c_str(), RTLD_LAZY);\n    if (!Handle)\n    {\n        LOG_ERROR << \"load \" << soFile << \" error!\";\n        LOG_ERROR << dlerror();\n    }\n    else\n    {\n        LOG_TRACE << \"Successfully loaded library file \" << soFile;\n    }\n\n    return Handle;\n}\n"
  },
  {
    "path": "lib/src/SharedLibManager.h",
    "content": "/**\n *\n *  SharedLibManager.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <trantor/net/EventLoopThread.h>\n#include <trantor/utils/NonCopyable.h>\n#include <unordered_map>\n#include <vector>\n#include <sys/stat.h>\n\nnamespace drogon\n{\nclass SharedLibManager : public trantor::NonCopyable\n{\n  public:\n    SharedLibManager(const std::vector<std::string> &libPaths,\n                     const std::string &outputPath);\n    ~SharedLibManager();\n\n  private:\n    void managerLibs();\n    std::vector<std::string> libPaths_;\n    std::string outputPath_;\n\n    struct DLStat\n    {\n        void *handle{nullptr};\n        struct timespec mTime = {0, 0};\n    };\n\n    std::unordered_map<std::string, DLStat> dlMap_;\n    void *compileAndLoadLib(const std::string &sourceFile, void *oldHld);\n    void *loadLib(const std::string &soFile, void *oldHld);\n    bool shouldCompileLib(const std::string &soFile,\n                          const struct stat &sourceStat);\n    trantor::TimerId timeId_;\n    trantor::EventLoopThread workingThread_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/SlashRemover.cc",
    "content": "#include <drogon/plugins/SlashRemover.h>\n#include <drogon/plugins/Redirector.h>\n#include <drogon/HttpAppFramework.h>\n#include \"drogon/utils/FunctionTraits.h\"\n#include <cstddef>\n#include <cstdint>\n#include <functional>\n#include <string>\n#include <string_view>\n#include <utility>\n\nusing namespace drogon;\nusing namespace drogon::plugin;\nusing std::string;\nusing std::string_view;\n\nenum removeSlashMode : uint8_t\n{\n    trailing = 1 << 0,\n    duplicate = 1 << 1,\n    both = trailing | duplicate,\n};\n\n/// Returns the index before the trailing slashes,\n/// or 0 if only contains slashes\nstatic inline size_t findTrailingSlashes(string_view url)\n{\n    auto len = url.size();\n    // Must be at least 2 chars and end with a slash\n    if (len < 2 || url.back() != '/')\n        return string::npos;\n\n    size_t a = len - 1;  // We already know the last char is '/',\n                         // we will use pre-decrement to account for this\n    while (--a > 0 && url[a] == '/')\n        ;  // We know the first char is '/', so don't check for 0\n    return a;\n}\n\nstatic inline void removeTrailingSlashes(string &url,\n                                         size_t start,\n                                         string_view originalUrl)\n{\n    url = originalUrl.substr(0, start + 1);\n}\n\n/// Returns the index of the 2nd duplicate slash\nstatic inline size_t findDuplicateSlashes(string_view url)\n{\n    size_t len = url.size();\n    if (len < 2)\n        return string::npos;\n\n    bool startedPair = true;  // Always starts with a slash\n    for (size_t a = 1; a < len; ++a)\n    {\n        if (url[a] != '/')  // Broken pair\n        {\n            startedPair = false;\n            continue;\n        }\n        if (startedPair)  // Matching pair\n            return a;\n        startedPair = true;\n    }\n\n    return string::npos;\n}\n\nstatic inline void removeDuplicateSlashes(string &url, size_t start)\n{\n    // +1 because we don't need to look at the same character again,\n    // which was found by `findDuplicateSlashes`, it saves one iteration\n    for (size_t b = (start--) + 1, len = url.size(); b < len; ++b)\n    {\n        const char c = url[b];\n        if (c != '/' || url[start] != '/')\n        {\n            ++start;\n            url[start] = c;\n        }\n    }\n    url.resize(start + 1);\n}\n\nstatic inline std::pair<size_t, size_t> findExcessiveSlashes(string_view url)\n{\n    size_t len = url.size();\n    if (len < 2)  // Must have at least 2 characters to count as either trailing\n                  // or duplicate slash\n        return {string::npos, string::npos};\n\n    // Trail finder\n    size_t trailIdx = len;  // The pre-decrement will put it on last char\n    while (--trailIdx > 0 && url[trailIdx] == '/')\n        ;  // We know first char is '/', no need to check it\n\n    // Filled with '/'\n    if (trailIdx == 0)\n        return {\n            0,             // Only keep first slash\n            string::npos,  // No duplicate\n        };\n\n    // Look for a duplicate pair\n    size_t dupIdx = 1;\n    for (bool startedPair = true; dupIdx < trailIdx;\n         ++dupIdx)  // Always starts with a slash\n    {\n        if (url[dupIdx] != '/')  // Broken pair\n        {\n            startedPair = false;\n            continue;\n        }\n        if (startedPair)  // Matching pair\n            break;\n        startedPair = true;\n    }\n\n    // Found no duplicate\n    if (dupIdx == trailIdx)\n        return {\n            trailIdx != len - 1\n                ?  // If has gone past last char, then there is a trailing slash\n                trailIdx\n                : string::npos,  // No trail\n            string::npos,        // No duplicate\n        };\n\n    // Duplicate found\n    return {\n        trailIdx != len - 1\n            ?  // If has gone past last char, then there is a trailing slash\n            trailIdx\n            : string::npos,  // No trail\n        dupIdx,\n    };\n}\n\nstatic inline void removeExcessiveSlashes(string &url,\n                                          std::pair<size_t, size_t> start,\n                                          string_view originalUrl)\n{\n    if (start.first != string::npos)\n        removeTrailingSlashes(url, start.first, originalUrl);\n    else\n        url = originalUrl;\n\n    if (start.second != string::npos)\n        removeDuplicateSlashes(url, start.second);\n}\n\nstatic inline bool handleReq(const drogon::HttpRequestPtr &req,\n                             uint8_t removeMode)\n{\n    switch (removeMode)\n    {\n        case trailing:\n        {\n            auto find = findTrailingSlashes(req->path());\n            if (find == string::npos)\n                return false;\n\n            string newPath;\n            removeTrailingSlashes(newPath, find, req->path());\n            req->setPath(std::move(newPath));\n            break;\n        }\n        case duplicate:\n        {\n            auto find = findDuplicateSlashes(req->path());\n            if (find == string::npos)\n                return false;\n\n            string newPath = req->path();\n            removeDuplicateSlashes(newPath, find);\n            req->setPath(std::move(newPath));\n            break;\n        }\n        case both:\n        default:\n        {\n            auto find = findExcessiveSlashes(req->path());\n            if (find.first == string::npos && find.second == string::npos)\n                return false;\n\n            string newPath;\n            removeExcessiveSlashes(newPath, find, req->path());\n            req->setPath(std::move(newPath));\n            break;\n        }\n    }\n    return true;\n}\n\nvoid SlashRemover::initAndStart(const Json::Value &config)\n{\n    trailingSlashes_ = config.get(\"remove_trailing_slashes\", true).asBool();\n    duplicateSlashes_ = config.get(\"remove_duplicate_slashes\", true).asBool();\n    redirect_ = config.get(\"redirect\", true).asBool();\n    const uint8_t removeMode =\n        (trailingSlashes_ * trailing) | (duplicateSlashes_ * duplicate);\n    if (!removeMode)\n        return;\n    auto redirector = app().getPlugin<Redirector>();\n    if (!redirector)\n    {\n        LOG_ERROR << \"Redirector plugin is not found!\";\n        return;\n    }\n    auto func = [removeMode](const HttpRequestPtr &req) -> bool {\n        return handleReq(req, removeMode);\n    };\n    if (redirect_)\n    {\n        redirector->registerPathRewriteHandler(std::move(func));\n    }\n    else\n    {\n        redirector->registerForwardHandler(std::move(func));\n    }\n}\n\nvoid SlashRemover::shutdown()\n{\n    LOG_TRACE << \"SlashRemover plugin is shutdown!\";\n}\n"
  },
  {
    "path": "lib/src/SlidingWindowRateLimiter.cc",
    "content": "#include \"SlidingWindowRateLimiter.h\"\n#include <assert.h>\n\nusing namespace drogon;\n\nSlidingWindowRateLimiter::SlidingWindowRateLimiter(\n    size_t capacity,\n    std::chrono::duration<double> timeUnit)\n    : capacity_(capacity),\n      unitStartTime_(std::chrono::steady_clock::now()),\n      lastTime_(unitStartTime_),\n      timeUnit_(timeUnit)\n{\n}\n\n// implementation of the sliding window algorithm\nbool SlidingWindowRateLimiter::isAllowed()\n{\n    auto now = std::chrono::steady_clock::now();\n    unitStartTime_ =\n        unitStartTime_ +\n        std::chrono::duration_cast<decltype(unitStartTime_)::duration>(\n            std::chrono::duration<double>(\n                static_cast<double>(\n                    (uint64_t)(std::chrono::duration_cast<\n                                   std::chrono::duration<double>>(\n                                   now - unitStartTime_)\n                                   .count() /\n                               timeUnit_.count())) *\n                timeUnit_.count()));\n\n    if (unitStartTime_ > lastTime_)\n    {\n        auto duration =\n            std::chrono::duration_cast<std::chrono::duration<double>>(\n                unitStartTime_ - lastTime_);\n        if (duration >= timeUnit_)\n        {\n            previousRequests_ = 0;\n        }\n        else\n        {\n            previousRequests_ = currentRequests_;\n        }\n        currentRequests_ = 0;\n    }\n    auto coef = std::chrono::duration_cast<std::chrono::duration<double>>(\n                    now - unitStartTime_) /\n                timeUnit_;\n    assert(coef <= 1.0);\n    auto count = previousRequests_ * (1.0 - coef) + currentRequests_;\n    if (count < capacity_)\n    {\n        currentRequests_++;\n        lastTime_ = now;\n        return true;\n    }\n    return false;\n}\n"
  },
  {
    "path": "lib/src/SlidingWindowRateLimiter.h",
    "content": "#pragma once\n#include <drogon/RateLimiter.h>\n#include <chrono>\n\nnamespace drogon\n{\nclass SlidingWindowRateLimiter : public RateLimiter\n{\n  public:\n    SlidingWindowRateLimiter(size_t capacity,\n                             std::chrono::duration<double> timeUnit);\n    bool isAllowed() override;\n    ~SlidingWindowRateLimiter() noexcept override = default;\n\n  private:\n    size_t capacity_;\n    size_t currentRequests_{0};\n    size_t previousRequests_{0};\n    std::chrono::steady_clock::time_point unitStartTime_;\n    std::chrono::steady_clock::time_point lastTime_;\n    std::chrono::duration<double> timeUnit_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/SpinLock.h",
    "content": "/**\n *  SpinLock.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <atomic>\n#include <emmintrin.h>\n#include <thread>\n\n#define LOCK_SPIN 2048\n\nnamespace drogon\n{\nclass SpinLock\n{\n  public:\n    inline SpinLock(std::atomic<bool> &flag) : flag_(flag)\n    {\n        static const int cpu = std::thread::hardware_concurrency();\n        int n, i;\n        while (1)\n        {\n            if (!flag_.load() &&\n                !flag_.exchange(true, std::memory_order_acquire))\n            {\n                return;\n            }\n            if (cpu > 1)\n            {\n                for (n = 1; n < LOCK_SPIN; n <<= 1)\n                {\n                    for (i = 0; i < n; ++i)\n                    {\n                        //__asm__ __volatile__(\"rep; nop\" ::: \"memory\"); //pause\n                        _mm_pause();\n                    }\n                    if (!flag_.load() &&\n                        !flag_.exchange(true, std::memory_order_acquire))\n                    {\n                        return;\n                    }\n                }\n            }\n            std::this_thread::yield();\n        }\n    }\n\n    inline ~SpinLock()\n    {\n        flag_.store(false, std::memory_order_release);\n    }\n\n  private:\n    std::atomic<bool> &flag_;\n};\n\nclass SimpleSpinLock\n{\n  public:\n    inline SimpleSpinLock(std::atomic_flag &flag) : flag_(flag)\n    {\n        while (flag_.test_and_set(std::memory_order_acquire))\n        {\n            //__asm__ __volatile__(\"rep; nop\" ::: \"memory\"); //pause\n            _mm_pause();\n        }\n    }\n\n    inline ~SimpleSpinLock()\n    {\n        flag_.clear(std::memory_order_release);\n    }\n\n  private:\n    std::atomic_flag &flag_;\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/StaticFileRouter.cc",
    "content": "/**\n *\n *  StaticFileRouter.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"StaticFileRouter.h\"\n#include \"HttpAppFrameworkImpl.h\"\n#include \"HttpRequestImpl.h\"\n#include \"HttpResponseImpl.h\"\n#include \"RangeParser.h\"\n#include <fstream>\n#include <iostream>\n#include <algorithm>\n#include <memory>\n#include <fcntl.h>\n#ifndef _WIN32\n#include <sys/file.h>\n#elif !defined(__MINGW32__)\n#define stat _wstati64\n#define S_ISREG(m) (((m) & 0170000) == (0100000))\n#define S_ISDIR(m) (((m) & 0170000) == (0040000))\n#endif\n#include <sys/stat.h>\n#include <filesystem>\n\nusing namespace drogon;\n\nvoid StaticFileRouter::init(const std::vector<trantor::EventLoop *> &ioLoops)\n{\n    // Max timeout up to about 70 days;\n    staticFilesCacheMap_ = std::make_unique<\n        IOThreadStorage<std::unique_ptr<CacheMap<std::string, char>>>>();\n    staticFilesCacheMap_->init(\n        [&ioLoops](std::unique_ptr<CacheMap<std::string, char>> &mapPtr,\n                   size_t i) {\n            assert(i == ioLoops[i]->index());\n            mapPtr = std::make_unique<CacheMap<std::string, char>>(ioLoops[i],\n                                                                   1.0f,\n                                                                   4,\n                                                                   50);\n        });\n    staticFilesCache_ = std::make_unique<\n        IOThreadStorage<std::unordered_map<std::string, HttpResponsePtr>>>();\n    ioLocationsPtr_ =\n        std::make_shared<IOThreadStorage<std::vector<Location>>>();\n    for (auto *loop : ioLoops)\n    {\n        loop->queueInLoop(\n            [ioLocationsPtr = ioLocationsPtr_, locations = locations_] {\n                **ioLocationsPtr = locations;\n            });\n    }\n}\n\nvoid StaticFileRouter::reset()\n{\n    staticFilesCacheMap_.reset();\n    staticFilesCache_.reset();\n    ioLocationsPtr_.reset();\n    locations_.clear();\n}\n\nvoid StaticFileRouter::route(\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    const std::string &path = req->path();\n    if (path.find(\"..\") != std::string::npos)\n    {\n        auto directories = utils::splitString(path, \"/\");\n        int traversalDepth = 0;\n        for (const auto &dir : directories)\n        {\n            if (dir == \"..\")\n            {\n                traversalDepth--;\n            }\n            else if (dir != \".\")\n            {\n                traversalDepth++;\n            }\n\n            if (traversalDepth < 0)\n            {\n                // Downloading files from the parent folder is forbidden.\n                callback(app().getCustomErrorHandler()(k403Forbidden, req));\n                return;\n            }\n        }\n    }\n\n    auto lPath = path;\n    std::transform(lPath.begin(),\n                   lPath.end(),\n                   lPath.begin(),\n                   [](unsigned char c) { return tolower(c); });\n\n    for (auto &location : **ioLocationsPtr_)\n    {\n        auto &URI = location.uriPrefix_;\n        if (location.realLocation_.empty())\n        {\n            if (!location.alias_.empty())\n            {\n                if (location.alias_[0] == '/')\n                {\n                    location.realLocation_ = location.alias_;\n                }\n                else\n                {\n                    location.realLocation_ =\n                        HttpAppFrameworkImpl::instance().getDocumentRoot() +\n                        location.alias_;\n                }\n            }\n            else\n            {\n                location.realLocation_ =\n                    HttpAppFrameworkImpl::instance().getDocumentRoot() +\n                    location.uriPrefix_;\n            }\n            if (location.realLocation_[location.realLocation_.length() - 1] !=\n                '/')\n            {\n                location.realLocation_.append(1, '/');\n            }\n            if (!location.isCaseSensitive_)\n            {\n                std::transform(URI.begin(),\n                               URI.end(),\n                               URI.begin(),\n                               [](unsigned char c) { return tolower(c); });\n            }\n        }\n        auto &tmpPath = location.isCaseSensitive_ ? path : lPath;\n        if (tmpPath.length() >= URI.length() &&\n            std::equal(tmpPath.begin(),\n                       tmpPath.begin() + URI.length(),\n                       URI.begin()))\n        {\n            std::string_view restOfThePath{path.data() + URI.length(),\n                                           path.length() - URI.length()};\n            auto pos = restOfThePath.rfind('/');\n            if (pos != 0 && pos != std::string_view::npos &&\n                !location.isRecursive_)\n            {\n                callback(app().getCustomErrorHandler()(k403Forbidden, req));\n                return;\n            }\n            std::string filePath =\n                location.realLocation_ +\n                std::string{restOfThePath.data(), restOfThePath.length()};\n            std::filesystem::path fsFilePath(utils::toNativePath(filePath));\n            std::error_code err;\n            if (!std::filesystem::exists(fsFilePath, err))\n            {\n                defaultHandler_(req, std::move(callback));\n                return;\n            }\n            if (std::filesystem::is_directory(fsFilePath, err))\n            {\n                // Check if path is eligible for an implicit index.html\n                if (implicitPageEnable_)\n                {\n                    filePath = filePath + \"/\" + implicitPage_;\n                }\n                else\n                {\n                    callback(app().getCustomErrorHandler()(k403Forbidden, req));\n                    return;\n                }\n            }\n            else\n            {\n                if (!location.allowAll_)\n                {\n                    pos = restOfThePath.rfind('.');\n                    if (pos == std::string_view::npos)\n                    {\n                        callback(\n                            app().getCustomErrorHandler()(k403Forbidden, req));\n                        return;\n                    }\n                    std::string extension{restOfThePath.data() + pos + 1,\n                                          restOfThePath.length() - pos - 1};\n                    std::transform(extension.begin(),\n                                   extension.end(),\n                                   extension.begin(),\n                                   [](unsigned char c) { return tolower(c); });\n                    if (fileTypeSet_.find(extension) == fileTypeSet_.end())\n                    {\n                        callback(\n                            app().getCustomErrorHandler()(k403Forbidden, req));\n                        return;\n                    }\n                }\n            }\n\n            if (location.middlewares_.empty())\n            {\n                sendStaticFileResponse(filePath,\n                                       req,\n                                       std::move(callback),\n                                       std::string_view{\n                                           location.defaultContentType_});\n            }\n            else\n            {\n                middlewares_function::passMiddlewares(\n                    location.middlewares_,\n                    req,\n                    std::move(callback),\n                    [this,\n                     req,\n                     filePath = std::move(filePath),\n                     contentType =\n                         std::string_view{location.defaultContentType_}](\n                        std::function<void(const HttpResponsePtr &)>\n                            &&middlewarePostCb) mutable {\n                        sendStaticFileResponse(filePath,\n                                               req,\n                                               std::move(middlewarePostCb),\n                                               contentType);\n                    });\n            }\n            return;\n        }\n    }\n    std::string directoryPath =\n        HttpAppFrameworkImpl::instance().getDocumentRoot() + path;\n    std::filesystem::path fsDirectoryPath(utils::toNativePath(directoryPath));\n    std::error_code err;\n    if (std::filesystem::exists(fsDirectoryPath, err))\n    {\n        if (std::filesystem::is_directory(fsDirectoryPath, err))\n        {\n            // Check if path is eligible for an implicit index.html\n            if (implicitPageEnable_)\n            {\n                std::string filePath = directoryPath + \"/\" + implicitPage_;\n                sendStaticFileResponse(filePath, req, std::move(callback), \"\");\n                return;\n            }\n            else\n            {\n                callback(app().getCustomErrorHandler()(k403Forbidden, req));\n                return;\n            }\n        }\n        else\n        {\n            // This is a normal page\n            auto pos = path.rfind('.');\n            if (pos == std::string::npos)\n            {\n                callback(app().getCustomErrorHandler()(k403Forbidden, req));\n                return;\n            }\n            std::string filetype = lPath.substr(pos + 1);\n            if (fileTypeSet_.find(filetype) != fileTypeSet_.end())\n            {\n                // LOG_INFO << \"file query!\" << path;\n                std::string filePath = directoryPath;\n                sendStaticFileResponse(filePath, req, std::move(callback), \"\");\n                return;\n            }\n        }\n    }\n    defaultHandler_(req, std::move(callback));\n}\n\n// Expand this struct as you need, nothing to worry about\nstruct FileStat\n{\n    size_t fileSize_;\n    struct tm modifiedTime_;\n    std::string modifiedTimeStr_;\n};\n\n// A wrapper to call stat()\n// std::filesystem::file_time_type::clock::to_time_t still not\n// implemented by M$, even in c++20, so keep calls to stat()\nstatic bool getFileStat(const std::string &filePath, FileStat &myStat)\n{\n#if defined(_WIN32) && !defined(__MINGW32__)\n    struct _stati64 fileStat;\n#else   // _WIN32\n    struct stat fileStat;\n#endif  // _WIN32\n    if (stat(utils::toNativePath(filePath).c_str(), &fileStat) == 0 &&\n        S_ISREG(fileStat.st_mode))\n    {\n        LOG_TRACE << \"last modify time:\" << fileStat.st_mtime;\n#ifdef _WIN32\n        gmtime_s(&myStat.modifiedTime_, &fileStat.st_mtime);\n#else\n        gmtime_r(&fileStat.st_mtime, &myStat.modifiedTime_);\n#endif\n        std::string &timeStr = myStat.modifiedTimeStr_;\n        timeStr.resize(64);\n        size_t len = strftime((char *)timeStr.data(),\n                              timeStr.size(),\n                              \"%a, %d %b %Y %H:%M:%S GMT\",\n                              &myStat.modifiedTime_);\n        timeStr.resize(len);\n\n        myStat.fileSize_ = fileStat.st_size;\n        return true;\n    }\n\n    return false;\n}\n\nvoid StaticFileRouter::sendStaticFileResponse(\n    const std::string &filePath,\n    const HttpRequestImplPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback,\n    const std::string_view &defaultContentType)\n{\n    if (req->method() != Get)\n    {\n        callback(app().getCustomErrorHandler()(k405MethodNotAllowed, req));\n        return;\n    }\n\n    FileStat fileStat;\n    bool fileExists = false;\n    const std::string &rangeStr = req->getHeaderBy(\"range\");\n    if (enableRange_ && !rangeStr.empty())\n    {\n        if (!getFileStat(filePath, fileStat))\n        {\n            defaultHandler_(req, std::move(callback));\n            return;\n        }\n        fileExists = true;\n        // Check last modified time, rfc2616-14.25\n        // If-Modified-Since: Mon, 15 Oct 2018 06:26:33 GMT\n        // According to rfc 7233-3.1, preconditions must be evaluated before\n        const std::string &modiStr = req->getHeaderBy(\"if-modified-since\");\n        if (enableLastModify_ && modiStr == fileStat.modifiedTimeStr_)\n        {\n            LOG_TRACE << \"Not modified!\";\n            std::shared_ptr<HttpResponseImpl> resp =\n                std::make_shared<HttpResponseImpl>();\n            resp->setStatusCode(k304NotModified);\n            resp->setContentTypeCode(CT_NONE);\n            callback(resp);\n            return;\n        }\n        // Check If-Range precondition\n        const std::string &ifRange = req->getHeaderBy(\"if-range\");\n        if (ifRange.empty() || ifRange == fileStat.modifiedTimeStr_)\n        {\n            std::vector<FileRange> ranges;\n            switch (parseRangeHeader(rangeStr, fileStat.fileSize_, ranges))\n            {\n                // TODO: support only single range now\n                // Contributions are welcomed.\n                case FileRangeParseResult::SinglePart:\n                case FileRangeParseResult::MultiPart:\n                {\n                    auto firstRange = ranges.front();\n                    auto ct = fileNameToContentTypeAndMime(filePath);\n                    auto resp =\n                        HttpResponse::newFileResponse(filePath,\n                                                      firstRange.start,\n                                                      firstRange.end -\n                                                          firstRange.start,\n                                                      true,\n                                                      \"\",\n                                                      ct.first,\n                                                      std::string(ct.second),\n                                                      req);\n                    if (!fileStat.modifiedTimeStr_.empty())\n                    {\n                        resp->addHeader(\"Last-Modified\",\n                                        fileStat.modifiedTimeStr_);\n                        resp->addHeader(\"Expires\",\n                                        \"Thu, 01 Jan 1970 00:00:00 GMT\");\n                    }\n                    callback(resp);\n                    return;\n                }\n                case FileRangeParseResult::NotSatisfiable:\n                {\n                    auto resp = HttpResponse::newHttpResponse();\n                    resp->setStatusCode(k416RequestedRangeNotSatisfiable);\n                    char buf[64];\n                    snprintf(buf,\n                             sizeof(buf),\n                             \"bytes */%zu\",\n                             fileStat.fileSize_);\n                    resp->addHeader(\"Content-Range\", std::string(buf));\n                    callback(resp);\n                    return;\n                }\n                /** rfc7233 4.4.\n                 * > Note: Because servers are free to ignore Range, many\n                 * implementations will simply respond with the entire selected\n                 * representation in a 200 (OK) response.  That is partly\n                 * because most clients are prepared to receive a 200 (OK) to\n                 * complete the task (albeit less efficiently) and partly\n                 * because clients might not stop making an invalid partial\n                 * request until they have received a complete representation.\n                 * Thus, clients cannot depend on receiving a 416 (Range Not\n                 * Satisfiable) response even when it is most appropriate.\n                 */\n                default:\n                    break;\n            }\n        }\n    }\n\n    // find cached response\n    HttpResponsePtr cachedResp;\n    auto &cacheMap = staticFilesCache_->getThreadData();\n    auto iter = cacheMap.find(filePath);\n    if (iter != cacheMap.end())\n    {\n        cachedResp = iter->second;\n    }\n\n    if (enableLastModify_)\n    {\n        if (cachedResp)\n        {\n            if (static_cast<HttpResponseImpl *>(cachedResp.get())\n                    ->getHeaderBy(\"last-modified\") ==\n                req->getHeaderBy(\"if-modified-since\"))\n            {\n                std::shared_ptr<HttpResponseImpl> resp =\n                    std::make_shared<HttpResponseImpl>();\n                resp->setStatusCode(k304NotModified);\n                resp->setContentTypeCode(CT_NONE);\n                callback(resp);\n                return;\n            }\n        }\n        else\n        {\n            LOG_TRACE << \"enabled LastModify\";\n            if (!fileExists && !getFileStat(filePath, fileStat))\n            {\n                defaultHandler_(req, std::move(callback));\n                return;\n            }\n            fileExists = true;\n            const std::string &modiStr = req->getHeaderBy(\"if-modified-since\");\n            if (modiStr == fileStat.modifiedTimeStr_)\n            {\n                LOG_TRACE << \"not Modified!\";\n                std::shared_ptr<HttpResponseImpl> resp =\n                    std::make_shared<HttpResponseImpl>();\n                resp->setStatusCode(k304NotModified);\n                resp->setContentTypeCode(CT_NONE);\n                callback(resp);\n                return;\n            }\n        }\n    }\n    if (cachedResp)\n    {\n        LOG_TRACE << \"Using file cache\";\n        callback(cachedResp);\n        return;\n    }\n    // Check existence\n    if (!fileExists)\n    {\n        std::filesystem::path fsFilePath(utils::toNativePath(filePath));\n        std::error_code err;\n        if (!std::filesystem::exists(fsFilePath, err) ||\n            !std::filesystem::is_regular_file(fsFilePath, err))\n        {\n            defaultHandler_(req, std::move(callback));\n            return;\n        }\n    }\n\n    HttpResponsePtr resp;\n    auto &acceptEncoding = req->getHeaderBy(\"accept-encoding\");\n\n    if (brStaticFlag_ && acceptEncoding.find(\"br\") != std::string::npos)\n    {\n        // Find compressed file first.\n        auto brFileName = filePath + \".br\";\n        std::filesystem::path fsBrFile(utils::toNativePath(brFileName));\n        std::error_code err;\n        if (std::filesystem::exists(fsBrFile, err) &&\n            std::filesystem::is_regular_file(fsBrFile, err))\n        {\n            auto ct = fileNameToContentTypeAndMime(filePath);\n            resp = HttpResponse::newFileResponse(\n                brFileName, \"\", ct.first, std::string(ct.second), req);\n            resp->addHeader(\"Content-Encoding\", \"br\");\n        }\n    }\n    if (!resp && gzipStaticFlag_ &&\n        acceptEncoding.find(\"gzip\") != std::string::npos)\n    {\n        // Find compressed file first.\n        auto gzipFileName = filePath + \".gz\";\n        std::filesystem::path fsGzipFile(utils::toNativePath(gzipFileName));\n        std::error_code err;\n        if (std::filesystem::exists(fsGzipFile, err) &&\n            std::filesystem::is_regular_file(fsGzipFile, err))\n        {\n            auto ct = fileNameToContentTypeAndMime(filePath);\n            resp = HttpResponse::newFileResponse(\n                gzipFileName, \"\", ct.first, std::string(ct.second), req);\n            resp->addHeader(\"Content-Encoding\", \"gzip\");\n        }\n    }\n    if (!resp)\n    {\n        auto ct = fileNameToContentTypeAndMime(filePath);\n        resp = HttpResponse::newFileResponse(\n            filePath, \"\", ct.first, std::string(ct.second), req);\n    }\n    if (resp->statusCode() != k404NotFound)\n    {\n        if (resp->getContentType() == CT_APPLICATION_OCTET_STREAM &&\n            !defaultContentType.empty())\n        {\n            resp->setContentTypeCodeAndCustomString(CT_CUSTOM,\n                                                    defaultContentType);\n        }\n        if (!fileStat.modifiedTimeStr_.empty())\n        {\n            resp->addHeader(\"Last-Modified\", fileStat.modifiedTimeStr_);\n            resp->addHeader(\"Expires\", \"Thu, 01 Jan 1970 00:00:00 GMT\");\n        }\n        if (enableRange_)\n        {\n            resp->addHeader(\"accept-range\", \"bytes\");\n        }\n        if (!headers_.empty())\n        {\n            for (auto &header : headers_)\n            {\n                resp->addHeader(header.first, header.second);\n            }\n        }\n        // cache the response for 5 seconds by default\n        if (staticFilesCacheTime_ >= 0)\n        {\n            LOG_TRACE << \"Save in cache for \" << staticFilesCacheTime_\n                      << \" seconds\";\n            resp->setExpiredTime(staticFilesCacheTime_);\n            staticFilesCache_->getThreadData()[filePath] = resp;\n            staticFilesCacheMap_->getThreadData()->insert(\n                filePath, 0, staticFilesCacheTime_, [this, filePath]() {\n                    LOG_TRACE << \"Erase cache\";\n                    assert(staticFilesCache_->getThreadData().find(filePath) !=\n                           staticFilesCache_->getThreadData().end());\n                    staticFilesCache_->getThreadData().erase(filePath);\n                });\n        }\n        callback(resp);\n        return;\n    }\n    callback(resp);\n}\n\nvoid StaticFileRouter::setFileTypes(const std::vector<std::string> &types)\n{\n    fileTypeSet_.clear();\n    for (auto const &type : types)\n    {\n        fileTypeSet_.insert(type);\n    }\n}\n\nvoid StaticFileRouter::defaultHandler(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    callback(HttpResponse::newNotFoundResponse(req));\n}\n"
  },
  {
    "path": "lib/src/StaticFileRouter.h",
    "content": "/**\n *\n *  StaticFileRouter.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"impl_forwards.h\"\n#include \"MiddlewaresFunction.h\"\n#include <drogon/CacheMap.h>\n#include <drogon/IOThreadStorage.h>\n#include <functional>\n#include <set>\n#include <string>\n#include <memory>\n\nnamespace drogon\n{\nclass StaticFileRouter\n{\n  public:\n    static StaticFileRouter &instance()\n    {\n        static StaticFileRouter inst;\n        return inst;\n    }\n\n    void route(const HttpRequestImplPtr &req,\n               std::function<void(const HttpResponsePtr &)> &&callback);\n    void setFileTypes(const std::vector<std::string> &types);\n\n    void setStaticFilesCacheTime(int cacheTime)\n    {\n        staticFilesCacheTime_ = cacheTime;\n    }\n\n    int staticFilesCacheTime() const\n    {\n        return staticFilesCacheTime_;\n    }\n\n    void setGzipStatic(bool useGzipStatic)\n    {\n        gzipStaticFlag_ = useGzipStatic;\n    }\n\n    void setBrStatic(bool useBrStatic)\n    {\n        brStaticFlag_ = useBrStatic;\n    }\n\n    void init(const std::vector<trantor::EventLoop *> &ioLoops);\n    void reset();\n\n    void sendStaticFileResponse(\n        const std::string &filePath,\n        const HttpRequestImplPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback,\n        const std::string_view &defaultContentType);\n\n    void addALocation(const std::string &uriPrefix,\n                      const std::string &defaultContentType,\n                      const std::string &alias,\n                      bool isCaseSensitive,\n                      bool allowAll,\n                      bool isRecursive,\n                      const std::vector<std::string> &middlewareNames)\n    {\n        locations_.emplace_back(uriPrefix,\n                                defaultContentType,\n                                alias,\n                                isCaseSensitive,\n                                allowAll,\n                                isRecursive,\n                                middlewareNames);\n    }\n\n    void setStaticFileHeaders(\n        const std::vector<std::pair<std::string, std::string>> &headers)\n    {\n        headers_ = headers;\n    }\n\n    void setImplicitPageEnable(bool useImplicitPage)\n    {\n        implicitPageEnable_ = useImplicitPage;\n    }\n\n    bool isImplicitPageEnabled() const\n    {\n        return implicitPageEnable_;\n    }\n\n    void setImplicitPage(const std::string &implicitPageFile)\n    {\n        implicitPage_ = implicitPageFile;\n    }\n\n    const std::string &getImplicitPage() const\n    {\n        return implicitPage_;\n    }\n\n    void setDefaultHandler(DefaultHandler &&handler)\n    {\n        defaultHandler_ = std::move(handler);\n    }\n\n  private:\n    static void defaultHandler(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback);\n\n    std::set<std::string> fileTypeSet_{\"html\",\n                                       \"js\",\n                                       \"css\",\n                                       \"xml\",\n                                       \"xsl\",\n                                       \"txt\",\n                                       \"svg\",\n                                       \"ttf\",\n                                       \"otf\",\n                                       \"woff2\",\n                                       \"woff\",\n                                       \"eot\",\n                                       \"png\",\n                                       \"jpg\",\n                                       \"jpeg\",\n                                       \"gif\",\n                                       \"bmp\",\n                                       \"ico\",\n                                       \"icns\"};\n\n    int staticFilesCacheTime_{5};\n    bool enableLastModify_{true};\n    bool enableRange_{true};\n    bool gzipStaticFlag_{true};\n    bool brStaticFlag_{true};\n    std::unique_ptr<\n        IOThreadStorage<std::unique_ptr<CacheMap<std::string, char>>>>\n        staticFilesCacheMap_;\n    std::unique_ptr<\n        IOThreadStorage<std::unordered_map<std::string, HttpResponsePtr>>>\n        staticFilesCache_;\n    std::vector<std::pair<std::string, std::string>> headers_;\n    bool implicitPageEnable_{true};\n    std::string implicitPage_{\"index.html\"};\n    DefaultHandler defaultHandler_ = StaticFileRouter::defaultHandler;\n\n    struct Location\n    {\n        std::string uriPrefix_;\n        std::string defaultContentType_;\n        std::string alias_;\n        std::string realLocation_;\n        bool isCaseSensitive_;\n        bool allowAll_;\n        bool isRecursive_;\n        std::vector<std::shared_ptr<drogon::HttpMiddlewareBase>> middlewares_;\n\n        Location(const std::string &uriPrefix,\n                 const std::string &defaultContentType,\n                 const std::string &alias,\n                 bool isCaseSensitive,\n                 bool allowAll,\n                 bool isRecursive,\n                 const std::vector<std::string> &middlewares)\n            : uriPrefix_(uriPrefix),\n              alias_(alias),\n              isCaseSensitive_(isCaseSensitive),\n              allowAll_(allowAll),\n              isRecursive_(isRecursive),\n              middlewares_(middlewares_function::createMiddlewares(middlewares))\n        {\n            if (!defaultContentType.empty())\n            {\n                defaultContentType_ =\n                    std::string{\"content-type: \"} + defaultContentType + \"\\r\\n\";\n            }\n        }\n    };\n\n    std::shared_ptr<IOThreadStorage<std::vector<Location>>> ioLocationsPtr_;\n    std::vector<Location> locations_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/TaskTimeoutFlag.cc",
    "content": "/**\n *\n *  @file TaskTimeoutFlag.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"TaskTimeoutFlag.h\"\nusing namespace drogon;\n\nTaskTimeoutFlag::TaskTimeoutFlag(trantor::EventLoop *loop,\n                                 const std::chrono::duration<double> &timeout,\n                                 std::function<void()> timeoutCallback)\n    : loop_(loop), timeout_(timeout), timeoutFunc_(timeoutCallback)\n{\n}\n\nvoid TaskTimeoutFlag::runTimer()\n{\n    std::weak_ptr<TaskTimeoutFlag> weakPtr = shared_from_this();\n    loop_->runAfter(timeout_, [weakPtr]() {\n        auto thisPtr = weakPtr.lock();\n        if (!thisPtr)\n            return;\n        if (thisPtr->done())\n            return;\n        thisPtr->timeoutFunc_();\n    });\n}\n\nbool TaskTimeoutFlag::done()\n{\n    return isDone_.exchange(true);\n}\n"
  },
  {
    "path": "lib/src/TaskTimeoutFlag.h",
    "content": "/**\n *\n *  @file TaskTimeoutFlag.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include <trantor/utils/NonCopyable.h>\n#include <trantor/net/EventLoop.h>\n#include <chrono>\n#include <functional>\n#include <atomic>\n#include <memory>\n\nnamespace drogon\n{\nclass TaskTimeoutFlag : public trantor::NonCopyable,\n                        public std::enable_shared_from_this<TaskTimeoutFlag>\n{\n  public:\n    TaskTimeoutFlag(trantor::EventLoop *loop,\n                    const std::chrono::duration<double> &timeout,\n                    std::function<void()> timeoutCallback);\n    bool done();\n    void runTimer();\n\n  private:\n    std::atomic<bool> isDone_{false};\n    trantor::EventLoop *loop_;\n    std::chrono::duration<double> timeout_;\n    std::function<void()> timeoutFunc_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/TokenBucketRateLimiter.cc",
    "content": "#include \"TokenBucketRateLimiter.h\"\n\nusing namespace drogon;\n\nTokenBucketRateLimiter::TokenBucketRateLimiter(\n    size_t capacity,\n    std::chrono::duration<double> timeUnit)\n    : capacity_(capacity),\n      lastTime_(std::chrono::steady_clock::now()),\n      timeUnit_(timeUnit),\n      tokens_((double)capacity_)\n{\n}\n\n// implementation of the token bucket algorithm\nbool TokenBucketRateLimiter::isAllowed()\n{\n    auto now = std::chrono::steady_clock::now();\n    auto duration = std::chrono::duration_cast<std::chrono::duration<double>>(\n        now - lastTime_);\n    tokens_ += capacity_ * (duration / timeUnit_);\n    if (tokens_ > capacity_)\n        tokens_ = (double)capacity_;\n    lastTime_ = now;\n    if (tokens_ > 1.0)\n    {\n        tokens_ -= 1.0;\n        return true;\n    }\n    return false;\n}\n"
  },
  {
    "path": "lib/src/TokenBucketRateLimiter.h",
    "content": "#pragma once\n\n#include <drogon/RateLimiter.h>\n\nnamespace drogon\n{\nclass TokenBucketRateLimiter : public RateLimiter\n{\n  public:\n    TokenBucketRateLimiter(size_t capacity,\n                           std::chrono::duration<double> timeUnit);\n    bool isAllowed() override;\n    ~TokenBucketRateLimiter() noexcept override = default;\n\n  private:\n    size_t capacity_;\n    std::chrono::steady_clock::time_point lastTime_;\n    std::chrono::duration<double> timeUnit_;\n    double tokens_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/Utilities.cc",
    "content": "/**\n *\n *  @file Utilities.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/utils/Utilities.h>\n#include <trantor/utils/Logger.h>\n#include <trantor/utils/Utilities.h>\n#include <drogon/config.h>\n#ifdef USE_BROTLI\n#include <brotli/decode.h>\n#include <brotli/encode.h>\n#endif\n#ifdef _WIN32\n#include <rpc.h>\n#include <direct.h>\n#include <io.h>\n#include <iomanip>\n#else\n#include <uuid.h>\n#include <unistd.h>\n#endif\n#include <zlib.h>\n#include <sstream>\n#include <string>\n#include <mutex>\n#include <random>\n#include <algorithm>\n#include <array>\n#include <locale>\n#include <clocale>\n#include <cctype>\n#include <cstdlib>\n#include <filesystem>\n#include <stdio.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <stdarg.h>\n\n#ifdef _WIN32\n\nchar *strptime(const char *s, const char *f, struct tm *tm)\n{\n    // std::get_time is defined such that its\n    // format parameters are the exact same as strptime.\n    std::istringstream input(s);\n    input.imbue(std::locale(setlocale(LC_ALL, nullptr)));\n    input >> std::get_time(tm, f);\n    if (input.fail())\n    {\n        return nullptr;\n    }\n    return (char *)(s + input.tellg());\n}\n\ntime_t timegm(struct tm *tm)\n{\n    struct tm my_tm;\n\n    memcpy(&my_tm, tm, sizeof(struct tm));\n\n    /* _mkgmtime() changes the value of the struct tm* you pass in, so\n     * use a copy\n     */\n    return _mkgmtime(&my_tm);\n}\n#endif\n\n#ifdef __HAIKU__\n// HACK: Haiku has a timegm implementation. But it is not exposed\nextern \"C\" time_t timegm(struct tm *tm);\n#endif\n\nnamespace drogon\n{\nnamespace utils\n{\nstatic constexpr std::string_view base64Chars =\n    \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n    \"abcdefghijklmnopqrstuvwxyz\"\n    \"0123456789+/\";\n\nstatic constexpr std::string_view urlBase64Chars =\n    \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n    \"abcdefghijklmnopqrstuvwxyz\"\n    \"0123456789-_\";\n\nclass Base64CharMap\n{\n  public:\n    Base64CharMap()\n    {\n        char index = 0;\n        for (int c = 'A'; c <= 'Z'; ++c)\n        {\n            charMap_[c] = index++;\n        }\n        for (int c = 'a'; c <= 'z'; ++c)\n        {\n            charMap_[c] = index++;\n        }\n        for (int c = '0'; c <= '9'; ++c)\n        {\n            charMap_[c] = index++;\n        }\n        charMap_[static_cast<int>('+')] = charMap_[static_cast<int>('-')] =\n            index++;\n        charMap_[static_cast<int>('/')] = charMap_[static_cast<int>('_')] =\n            index;\n        charMap_[0] = char(0xff);\n    }\n\n    char getIndex(const char c) const noexcept\n    {\n        return charMap_[static_cast<int>(c)];\n    }\n\n  private:\n    char charMap_[256]{0};\n};\n\nstatic const Base64CharMap base64CharMap;\n\nstatic inline bool isBase64(unsigned char c)\n{\n    if (isalnum(c))\n        return true;\n    switch (c)\n    {\n        case '+':\n        case '/':\n        case '-':\n        case '_':\n            return true;\n    }\n    return false;\n}\n\nbool isInteger(std::string_view str)\n{\n    for (auto c : str)\n        if (c < '0' || c > '9')\n            return false;\n    return true;\n}\n\nbool isBase64(std::string_view str)\n{\n    for (auto c : str)\n        if (!isBase64(c))\n            return false;\n    return true;\n}\n\nstd::string genRandomString(int length)\n{\n    static const std::string_view char_space =\n        \"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n    std::uniform_int_distribution<size_t> dist(0, char_space.size() - 1);\n    thread_local std::mt19937 rng(std::random_device{}());\n\n    std::string str;\n    str.resize(length);\n    for (char &ch : str)\n    {\n        ch = char_space[dist(rng)];\n    }\n\n    return str;\n}\n\nstd::vector<char> hexToBinaryVector(const char *ptr, size_t length)\n{\n    assert(length % 2 == 0);\n    std::vector<char> ret(length / 2, '\\0');\n    for (size_t i = 0; i < ret.size(); ++i)\n    {\n        auto p = i * 2;\n        char c1 = ptr[p];\n        if (c1 >= '0' && c1 <= '9')\n        {\n            c1 -= '0';\n        }\n        else if (c1 >= 'a' && c1 <= 'f')\n        {\n            c1 -= 'a';\n            c1 += 10;\n        }\n        else if (c1 >= 'A' && c1 <= 'F')\n        {\n            c1 -= 'A';\n            c1 += 10;\n        }\n        else\n        {\n            return std::vector<char>();\n        }\n        char c2 = ptr[p + 1];\n        if (c2 >= '0' && c2 <= '9')\n        {\n            c2 -= '0';\n        }\n        else if (c2 >= 'a' && c2 <= 'f')\n        {\n            c2 -= 'a';\n            c2 += 10;\n        }\n        else if (c2 >= 'A' && c2 <= 'F')\n        {\n            c2 -= 'A';\n            c2 += 10;\n        }\n        else\n        {\n            return std::vector<char>();\n        }\n        ret[i] = c1 * 16 + c2;\n    }\n    return ret;\n}\n\nstd::string hexToBinaryString(const char *ptr, size_t length)\n{\n    assert(length % 2 == 0);\n    std::string ret(length / 2, '\\0');\n    for (size_t i = 0; i < ret.length(); ++i)\n    {\n        auto p = i * 2;\n        char c1 = ptr[p];\n        if (c1 >= '0' && c1 <= '9')\n        {\n            c1 -= '0';\n        }\n        else if (c1 >= 'a' && c1 <= 'f')\n        {\n            c1 -= 'a';\n            c1 += 10;\n        }\n        else if (c1 >= 'A' && c1 <= 'F')\n        {\n            c1 -= 'A';\n            c1 += 10;\n        }\n        else\n        {\n            return \"\";\n        }\n        char c2 = ptr[p + 1];\n        if (c2 >= '0' && c2 <= '9')\n        {\n            c2 -= '0';\n        }\n        else if (c2 >= 'a' && c2 <= 'f')\n        {\n            c2 -= 'a';\n            c2 += 10;\n        }\n        else if (c2 >= 'A' && c2 <= 'F')\n        {\n            c2 -= 'A';\n            c2 += 10;\n        }\n        else\n        {\n            return \"\";\n        }\n        ret[i] = c1 * 16 + c2;\n    }\n    return ret;\n}\n\nDROGON_EXPORT void binaryStringToHex(const char *ptr,\n                                     size_t length,\n                                     char *out,\n                                     bool lowerCase)\n{\n    for (size_t i = 0; i < length; ++i)\n    {\n        int value = (ptr[i] & 0xf0) >> 4;\n        if (value < 10)\n        {\n            out[i * 2] = char(value + 48);\n        }\n        else\n        {\n            if (!lowerCase)\n            {\n                out[i * 2] = char(value + 55);\n            }\n            else\n            {\n                out[i * 2] = char(value + 87);\n            }\n        }\n\n        value = (ptr[i] & 0x0f);\n        if (value < 10)\n        {\n            out[i * 2 + 1] = char(value + 48);\n        }\n        else\n        {\n            if (!lowerCase)\n            {\n                out[i * 2 + 1] = char(value + 55);\n            }\n            else\n            {\n                out[i * 2 + 1] = char(value + 87);\n            }\n        }\n    }\n}\n\nstd::string binaryStringToHex(const unsigned char *ptr,\n                              size_t length,\n                              bool lowercase)\n{\n    std::string idString(length * 2, '\\0');\n    binaryStringToHex((const char *)ptr, length, &idString[0], lowercase);\n    return idString;\n}\n\nstd::set<std::string> splitStringToSet(const std::string &str,\n                                       const std::string &separator)\n{\n    std::set<std::string> ret;\n    std::string::size_type pos1, pos2;\n    pos2 = 0;\n    pos1 = str.find(separator);\n    while (pos1 != std::string::npos)\n    {\n        if (pos1 != 0)\n        {\n            std::string item = str.substr(pos2, pos1 - pos2);\n            ret.insert(item);\n        }\n        pos2 = pos1 + separator.length();\n        while (pos2 < str.length() &&\n               str.substr(pos2, separator.length()) == separator)\n            pos2 += separator.length();\n        pos1 = str.find(separator, pos2);\n    }\n    if (pos2 < str.length())\n        ret.insert(str.substr(pos2));\n    return ret;\n}\n\ninline std::string createUuidString(const char *str, size_t len, bool lowercase)\n{\n    assert(len == 16);\n    std::string uuid(36, '\\0');\n    binaryStringToHex(str, 4, &uuid[0], lowercase);\n    uuid[8] = '-';\n    binaryStringToHex(str + 4, 2, &uuid[9], lowercase);\n    uuid[13] = '-';\n    binaryStringToHex(str + 6, 2, &uuid[14], lowercase);\n    uuid[18] = '-';\n    binaryStringToHex(str + 8, 2, &uuid[19], lowercase);\n    uuid[23] = '-';\n    binaryStringToHex(str + 10, 6, &uuid[24], lowercase);\n    return uuid;\n}\n\nstd::string getUuid(bool lowercase)\n{\n#if USE_OSSP_UUID\n    uuid_t *uuid;\n    uuid_create(&uuid);\n    uuid_make(uuid, UUID_MAKE_V4);\n    char *str{nullptr};\n    size_t len{0};\n    uuid_export(uuid, UUID_FMT_BIN, &str, &len);\n    uuid_destroy(uuid);\n    auto ret = createUuidString(str, len, lowercase);\n    free(str);\n    return ret;\n#elif defined __FreeBSD__ || defined __OpenBSD__\n    uuid_t *uuid = new uuid_t;\n    char *binstr = (char *)malloc(16);\n#if defined __FreeBSD__\n    uuidgen(uuid, 1);\n#else\n    uint32_t status;\n    uuid_create(uuid, &status);\n#endif\n#if _BYTE_ORDER == _LITTLE_ENDIAN\n    uuid_enc_le(binstr, uuid);\n#else  /* _BYTE_ORDER != _LITTLE_ENDIAN */\n    uuid_enc_be(binstr, uuid);\n#endif /* _BYTE_ORDER == _LITTLE_ENDIAN */\n    delete uuid;\n    auto ret = createUuidString(binstr, 16, lowercase);\n    free(binstr);\n    return ret;\n#elif defined _WIN32\n    uuid_t uu;\n    UuidCreate(&uu);\n    char tempStr[100];\n    auto len = snprintf(tempStr,\n                        sizeof(tempStr),\n                        \"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\",\n                        uu.Data1,\n                        uu.Data2,\n                        uu.Data3,\n                        uu.Data4[0],\n                        uu.Data4[1],\n                        uu.Data4[2],\n                        uu.Data4[3],\n                        uu.Data4[4],\n                        uu.Data4[5],\n                        uu.Data4[6],\n                        uu.Data4[7]);\n    return std::string{tempStr, static_cast<size_t>(len)};\n#else\n    uuid_t uu;\n    uuid_generate(uu);\n    auto uuid = createUuidString((const char *)uu, 16, lowercase);\n    return uuid;\n#endif\n}\n\nvoid base64Encode(const unsigned char *bytesToEncode,\n                  size_t inLen,\n                  unsigned char *outputBuffer,\n                  bool urlSafe,\n                  bool padded)\n{\n    int i = 0;\n    unsigned char charArray3[3];\n    unsigned char charArray4[4];\n\n    const std::string_view charSet = urlSafe ? urlBase64Chars : base64Chars;\n\n    size_t a = 0;\n    while (inLen--)\n    {\n        charArray3[i++] = *(bytesToEncode++);\n        if (i == 3)\n        {\n            charArray4[0] = (charArray3[0] & 0xfc) >> 2;\n            charArray4[1] =\n                ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);\n            charArray4[2] =\n                ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);\n            charArray4[3] = charArray3[2] & 0x3f;\n\n            for (i = 0; (i < 4); ++i, ++a)\n                outputBuffer[a] = charSet[charArray4[i]];\n            i = 0;\n        }\n    }\n\n    if (i)\n    {\n        for (int j = i; j < 3; ++j)\n            charArray3[j] = '\\0';\n\n        charArray4[0] = (charArray3[0] & 0xfc) >> 2;\n        charArray4[1] =\n            ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);\n        charArray4[2] =\n            ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);\n        charArray4[3] = charArray3[2] & 0x3f;\n\n        for (int j = 0; (j <= i); ++j, ++a)\n            outputBuffer[a] = charSet[charArray4[j]];\n\n        if (padded)\n            while ((++i < 4))\n            {\n                outputBuffer[a] = '=';\n                ++a;\n            }\n    }\n}\n\nstd::vector<char> base64DecodeToVector(std::string_view encodedString)\n{\n    auto inLen = encodedString.size();\n    int i = 0;\n    int in_{0};\n    char charArray4[4], charArray3[3];\n    std::vector<char> ret;\n    ret.reserve(base64DecodedLength(inLen));\n\n    while (inLen-- && (encodedString[in_] != '='))\n    {\n        if (!isBase64(encodedString[in_]))\n        {\n            ++in_;\n            continue;\n        }\n\n        charArray4[i++] = encodedString[in_];\n        ++in_;\n        if (i == 4)\n        {\n            for (i = 0; i < 4; ++i)\n            {\n                charArray4[i] = base64CharMap.getIndex(charArray4[i]);\n            }\n\n            charArray3[0] =\n                (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4);\n            charArray3[1] =\n                ((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2);\n            charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];\n\n            for (i = 0; (i < 3); ++i)\n                ret.push_back(charArray3[i]);\n            i = 0;\n        }\n    }\n\n    if (i)\n    {\n        for (int j = i; j < 4; ++j)\n            charArray4[j] = 0;\n\n        for (int j = 0; j < 4; ++j)\n        {\n            charArray4[j] = base64CharMap.getIndex(charArray4[j]);\n        }\n\n        charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4);\n        charArray3[1] =\n            ((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2);\n        charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];\n\n        --i;\n        for (int j = 0; (j < i); ++j)\n            ret.push_back(charArray3[j]);\n    }\n\n    return ret;\n}\n\nsize_t base64Decode(const char *encodedString,\n                    size_t inLen,\n                    unsigned char *outputBuffer)\n{\n    int i = 0;\n    int in_{0};\n    unsigned char charArray4[4], charArray3[3];\n\n    size_t a = 0;\n    while (inLen-- && (encodedString[in_] != '='))\n    {\n        if (!isBase64(encodedString[in_]))\n        {\n            ++in_;\n            continue;\n        }\n\n        charArray4[i++] = encodedString[in_];\n        ++in_;\n        if (i == 4)\n        {\n            for (i = 0; i < 4; ++i)\n            {\n                charArray4[i] = base64CharMap.getIndex(charArray4[i]);\n            }\n            charArray3[0] =\n                (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4);\n            charArray3[1] =\n                ((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2);\n            charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];\n\n            for (i = 0; (i < 3); ++i, ++a)\n                outputBuffer[a] = charArray3[i];\n            i = 0;\n        }\n    }\n\n    if (i)\n    {\n        for (int j = i; j < 4; ++j)\n            charArray4[j] = 0;\n\n        for (int j = 0; j < 4; ++j)\n        {\n            charArray4[j] = base64CharMap.getIndex(charArray4[j]);\n        }\n\n        charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4);\n        charArray3[1] =\n            ((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2);\n        charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];\n\n        --i;\n        for (int j = 0; (j < i); ++j, ++a)\n            outputBuffer[a] = charArray3[j];\n    }\n\n    return a;\n}\n\nstatic std::string charToHex(char c)\n{\n    std::string result;\n    char first, second;\n\n    first = (c & 0xF0) / 16;\n    first += first > 9 ? 'A' - 10 : '0';\n    second = c & 0x0F;\n    second += second > 9 ? 'A' - 10 : '0';\n\n    result.append(1, first);\n    result.append(1, second);\n\n    return result;\n}\n\nstd::string urlEncodeComponent(const std::string &src)\n{\n    std::string result;\n    std::string::const_iterator iter;\n\n    for (iter = src.begin(); iter != src.end(); ++iter)\n    {\n        switch (*iter)\n        {\n            case ' ':\n                result.append(1, '+');\n                break;\n            // alnum\n            case 'A':\n            case 'B':\n            case 'C':\n            case 'D':\n            case 'E':\n            case 'F':\n            case 'G':\n            case 'H':\n            case 'I':\n            case 'J':\n            case 'K':\n            case 'L':\n            case 'M':\n            case 'N':\n            case 'O':\n            case 'P':\n            case 'Q':\n            case 'R':\n            case 'S':\n            case 'T':\n            case 'U':\n            case 'V':\n            case 'W':\n            case 'X':\n            case 'Y':\n            case 'Z':\n            case 'a':\n            case 'b':\n            case 'c':\n            case 'd':\n            case 'e':\n            case 'f':\n            case 'g':\n            case 'h':\n            case 'i':\n            case 'j':\n            case 'k':\n            case 'l':\n            case 'm':\n            case 'n':\n            case 'o':\n            case 'p':\n            case 'q':\n            case 'r':\n            case 's':\n            case 't':\n            case 'u':\n            case 'v':\n            case 'w':\n            case 'x':\n            case 'y':\n            case 'z':\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            // mark\n            case '-':\n            case '_':\n            case '.':\n            case '!':\n            case '~':\n            case '*':\n            case '(':\n            case ')':\n                result.append(1, *iter);\n                break;\n            // escape\n            default:\n                result.append(1, '%');\n                result.append(charToHex(*iter));\n                break;\n        }\n    }\n\n    return result;\n}\n\nstd::string urlEncode(const std::string &src)\n{\n    std::string result;\n    std::string::const_iterator iter;\n\n    for (iter = src.begin(); iter != src.end(); ++iter)\n    {\n        switch (*iter)\n        {\n            case ' ':\n                result.append(1, '+');\n                break;\n            // alnum\n            case 'A':\n            case 'B':\n            case 'C':\n            case 'D':\n            case 'E':\n            case 'F':\n            case 'G':\n            case 'H':\n            case 'I':\n            case 'J':\n            case 'K':\n            case 'L':\n            case 'M':\n            case 'N':\n            case 'O':\n            case 'P':\n            case 'Q':\n            case 'R':\n            case 'S':\n            case 'T':\n            case 'U':\n            case 'V':\n            case 'W':\n            case 'X':\n            case 'Y':\n            case 'Z':\n            case 'a':\n            case 'b':\n            case 'c':\n            case 'd':\n            case 'e':\n            case 'f':\n            case 'g':\n            case 'h':\n            case 'i':\n            case 'j':\n            case 'k':\n            case 'l':\n            case 'm':\n            case 'n':\n            case 'o':\n            case 'p':\n            case 'q':\n            case 'r':\n            case 's':\n            case 't':\n            case 'u':\n            case 'v':\n            case 'w':\n            case 'x':\n            case 'y':\n            case 'z':\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            // mark\n            case '-':\n            case '_':\n            case '.':\n            case '!':\n            case '~':\n            case '*':\n            case '\\'':\n            case '(':\n            case ')':\n            case '&':\n            case '=':\n            case '/':\n            case '\\\\':\n            case '?':\n                result.append(1, *iter);\n                break;\n            // escape\n            default:\n                result.append(1, '%');\n                result.append(charToHex(*iter));\n                break;\n        }\n    }\n\n    return result;\n}\n\nbool needUrlDecoding(const char *begin, const char *end)\n{\n    return std::find_if(begin, end, [](const char c) {\n               return c == '+' || c == '%';\n           }) != end;\n}\n\nstd::string urlDecode(const char *begin, const char *end)\n{\n    std::string result;\n    size_t len = end - begin;\n    result.reserve(len * 2);\n    int hex = 0;\n    for (size_t i = 0; i < len; ++i)\n    {\n        switch (begin[i])\n        {\n            case '+':\n                result += ' ';\n                break;\n            case '%':\n                if ((i + 2) < len && isxdigit(begin[i + 1]) &&\n                    isxdigit(begin[i + 2]))\n                {\n                    unsigned int x1 = begin[i + 1];\n                    if (x1 >= '0' && x1 <= '9')\n                    {\n                        x1 -= '0';\n                    }\n                    else if (x1 >= 'a' && x1 <= 'f')\n                    {\n                        x1 = x1 - 'a' + 10;\n                    }\n                    else if (x1 >= 'A' && x1 <= 'F')\n                    {\n                        x1 = x1 - 'A' + 10;\n                    }\n                    unsigned int x2 = begin[i + 2];\n                    if (x2 >= '0' && x2 <= '9')\n                    {\n                        x2 -= '0';\n                    }\n                    else if (x2 >= 'a' && x2 <= 'f')\n                    {\n                        x2 = x2 - 'a' + 10;\n                    }\n                    else if (x2 >= 'A' && x2 <= 'F')\n                    {\n                        x2 = x2 - 'A' + 10;\n                    }\n                    hex = x1 * 16 + x2;\n\n                    result += char(hex);\n                    i += 2;\n                }\n                else\n                {\n                    result += '%';\n                }\n                break;\n            default:\n                result += begin[i];\n                break;\n        }\n    }\n    return result;\n}\n\n/* Compress gzip data */\nstd::string gzipCompress(const char *data, const size_t ndata)\n{\n    z_stream strm = {nullptr,\n                     0,\n                     0,\n                     nullptr,\n                     0,\n                     0,\n                     nullptr,\n                     nullptr,\n                     nullptr,\n                     nullptr,\n                     nullptr,\n                     0,\n                     0,\n                     0};\n    if (data && ndata > 0)\n    {\n        if (deflateInit2(&strm,\n                         Z_DEFAULT_COMPRESSION,\n                         Z_DEFLATED,\n                         MAX_WBITS + 16,\n                         8,\n                         Z_DEFAULT_STRATEGY) != Z_OK)\n        {\n            LOG_ERROR << \"deflateInit2 error!\";\n            return std::string{};\n        }\n        std::string outstr;\n        outstr.resize(compressBound(static_cast<uLong>(ndata)));\n        strm.next_in = (Bytef *)data;\n        strm.avail_in = static_cast<uInt>(ndata);\n        int ret;\n        do\n        {\n            if (strm.total_out >= outstr.size())\n            {\n                outstr.resize(strm.total_out * 2);\n            }\n            assert(outstr.size() >= strm.total_out);\n            strm.avail_out = static_cast<uInt>(outstr.size() - strm.total_out);\n            strm.next_out = (Bytef *)outstr.data() + strm.total_out;\n            ret = deflate(&strm, Z_FINISH); /* no bad return value */\n            if (ret == Z_STREAM_ERROR)\n            {\n                (void)deflateEnd(&strm);\n                return std::string{};\n            }\n        } while (strm.avail_out == 0);\n        assert(strm.avail_in == 0);\n        assert(ret == Z_STREAM_END); /* stream will be complete */\n        outstr.resize(strm.total_out);\n        /* clean up and return */\n        (void)deflateEnd(&strm);\n        return outstr;\n    }\n    return std::string{};\n}\n\nstd::string gzipDecompress(const char *data, const size_t ndata)\n{\n    if (ndata == 0)\n        return std::string(data, ndata);\n\n    auto full_length = ndata;\n\n    auto decompressed = std::string(full_length * 2, 0);\n    bool done = false;\n\n    z_stream strm = {nullptr,\n                     0,\n                     0,\n                     nullptr,\n                     0,\n                     0,\n                     nullptr,\n                     nullptr,\n                     nullptr,\n                     nullptr,\n                     nullptr,\n                     0,\n                     0,\n                     0};\n    strm.next_in = (Bytef *)data;\n    strm.avail_in = static_cast<uInt>(ndata);\n    strm.total_out = 0;\n    strm.zalloc = Z_NULL;\n    strm.zfree = Z_NULL;\n    if (inflateInit2(&strm, (15 + 32)) != Z_OK)\n    {\n        LOG_ERROR << \"inflateInit2 error!\";\n        return std::string{};\n    }\n    while (!done)\n    {\n        // Make sure we have enough room and reset the lengths.\n        if (strm.total_out >= decompressed.length())\n        {\n            decompressed.resize(decompressed.length() * 2);\n        }\n        strm.next_out = (Bytef *)decompressed.data() + strm.total_out;\n        strm.avail_out =\n            static_cast<uInt>(decompressed.length() - strm.total_out);\n        // Inflate another chunk.\n        int status = inflate(&strm, Z_SYNC_FLUSH);\n        if (status == Z_STREAM_END)\n        {\n            done = true;\n        }\n        else if (status != Z_OK)\n        {\n            break;\n        }\n    }\n    if (inflateEnd(&strm) != Z_OK)\n        return std::string{};\n    // Set real length.\n    if (done)\n    {\n        decompressed.resize(strm.total_out);\n        return decompressed;\n    }\n    else\n    {\n        return std::string{};\n    }\n}\n\nchar *getHttpFullDate(const trantor::Date &date)\n{\n    static thread_local int64_t lastSecond = 0;\n    static thread_local char lastTimeString[128] = {0};\n    auto nowSecond =\n        date.microSecondsSinceEpoch() / trantor::Date::MICRO_SECONDS_PER_SEC;\n    if (nowSecond == lastSecond)\n    {\n        return lastTimeString;\n    }\n    lastSecond = nowSecond;\n    date.toCustomFormattedString(\"%a, %d %b %Y %H:%M:%S GMT\",\n                                 lastTimeString,\n                                 sizeof(lastTimeString));\n    return lastTimeString;\n}\n\nvoid dateToCustomFormattedString(const std::string &fmtStr,\n                                 std::string &str,\n                                 const trantor::Date &date)\n{\n    auto nowSecond =\n        date.microSecondsSinceEpoch() / trantor::Date::MICRO_SECONDS_PER_SEC;\n    struct tm tm_LValue = date.tmStruct();\n    std::stringstream Out;\n    Out.imbue(std::locale{\"C\"});\n    Out << std::put_time(&tm_LValue, fmtStr.c_str());\n    str = Out.str();\n}\n\nconst std::string &getHttpFullDateStr(const trantor::Date &date)\n{\n    static thread_local int64_t lastSecond = 0;\n    static thread_local std::string lastTimeString(128, 0);\n    auto nowSecond =\n        date.microSecondsSinceEpoch() / trantor::Date::MICRO_SECONDS_PER_SEC;\n    if (nowSecond == lastSecond)\n    {\n        return lastTimeString;\n    }\n    lastSecond = nowSecond;\n    dateToCustomFormattedString(\"%a, %d %b %Y %H:%M:%S GMT\",\n                                lastTimeString,\n                                date);\n    return lastTimeString;\n}\n\ntrantor::Date getHttpDate(const std::string &httpFullDateString)\n{\n    static const std::array<const char *, 4> formats = {\n        // RFC822 (default)\n        \"%a, %d %b %Y %H:%M:%S\",\n        // RFC 850 (deprecated)\n        \"%a, %d-%b-%y %H:%M:%S\",\n        // ansi asctime format\n        \"%a %b %d %H:%M:%S %Y\",\n        // weird RFC 850-hybrid thing that reddit uses\n        \"%a, %d-%b-%Y %H:%M:%S\",\n    };\n    struct tm tmptm;\n    for (const char *format : formats)\n    {\n        if (strptime(httpFullDateString.c_str(), format, &tmptm) != NULL)\n        {\n            auto epoch = timegm(&tmptm);\n            return trantor::Date(epoch * trantor::Date::MICRO_SECONDS_PER_SEC);\n        }\n    }\n    LOG_WARN << \"invalid datetime format: '\" << httpFullDateString << \"'\";\n    return trantor::Date((std::numeric_limits<int64_t>::max)());\n}\n\nstd::string formattedString(const char *format, ...)\n{\n    std::string strBuffer(128, 0);\n    va_list ap, backup_ap;\n    va_start(ap, format);\n    va_copy(backup_ap, ap);\n    auto result = vsnprintf((char *)strBuffer.data(),\n                            strBuffer.size(),\n                            format,\n                            backup_ap);\n    va_end(backup_ap);\n    if ((result >= 0) && ((std::string::size_type)result < strBuffer.size()))\n    {\n        strBuffer.resize(result);\n    }\n    else\n    {\n        while (true)\n        {\n            if (result < 0)\n            {\n                // Older snprintf() behavior. Just try doubling the buffer size\n                strBuffer.resize(strBuffer.size() * 2);\n            }\n            else\n            {\n                strBuffer.resize(result + 1);\n            }\n\n            va_copy(backup_ap, ap);\n            auto result = vsnprintf((char *)strBuffer.data(),\n                                    strBuffer.size(),\n                                    format,\n                                    backup_ap);\n            va_end(backup_ap);\n\n            if ((result >= 0) &&\n                ((std::string::size_type)result < strBuffer.size()))\n            {\n                strBuffer.resize(result);\n                break;\n            }\n        }\n    }\n    va_end(ap);\n    return strBuffer;\n}\n\nint createPath(const std::string &path)\n{\n    if (path.empty())\n        return 0;\n    auto osPath{toNativePath(path)};\n    if (osPath.back() != std::filesystem::path::preferred_separator)\n        osPath.push_back(std::filesystem::path::preferred_separator);\n    std::filesystem::path fsPath(osPath);\n    std::error_code err;\n    std::filesystem::create_directories(fsPath, err);\n    if (err)\n    {\n        LOG_ERROR << \"Error \" << err.value() << \" creating path \" << osPath\n                  << \": \" << err.message();\n        return -1;\n    }\n    return 0;\n}\n#ifdef USE_BROTLI\nstd::string brotliCompress(const char *data, const size_t ndata)\n{\n    std::string ret;\n    if (ndata == 0)\n        return ret;\n    ret.resize(BrotliEncoderMaxCompressedSize(ndata));\n    size_t encodedSize{ret.size()};\n    auto r = BrotliEncoderCompress(5,\n                                   BROTLI_DEFAULT_WINDOW,\n                                   BROTLI_DEFAULT_MODE,\n                                   ndata,\n                                   (const uint8_t *)(data),\n                                   &encodedSize,\n                                   (uint8_t *)(ret.data()));\n    if (r == BROTLI_FALSE)\n        ret.resize(0);\n    else\n        ret.resize(encodedSize);\n    return ret;\n}\n\nstd::string brotliDecompress(const char *data, const size_t ndata)\n{\n    if (ndata == 0)\n        return std::string(data, ndata);\n\n    size_t availableIn = ndata;\n    auto nextIn = (const uint8_t *)(data);\n    auto decompressed = std::string(availableIn * 3, 0);\n    size_t availableOut = decompressed.size();\n    auto nextOut = (uint8_t *)(decompressed.data());\n    size_t totalOut{0};\n    bool done = false;\n    auto s = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);\n    while (!done)\n    {\n        auto result = BrotliDecoderDecompressStream(\n            s, &availableIn, &nextIn, &availableOut, &nextOut, &totalOut);\n        if (result == BROTLI_DECODER_RESULT_SUCCESS)\n        {\n            decompressed.resize(totalOut);\n            done = true;\n        }\n        else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT)\n        {\n            assert(totalOut == decompressed.size());\n            decompressed.resize(totalOut * 2);\n            nextOut = (uint8_t *)(decompressed.data() + totalOut);\n            availableOut = totalOut;\n        }\n        else\n        {\n            decompressed.resize(0);\n            done = true;\n        }\n    }\n    BrotliDecoderDestroyInstance(s);\n    return decompressed;\n}\n#else\nstd::string brotliCompress(const char * /*data*/, const size_t /*ndata*/)\n{\n    LOG_ERROR << \"If you do not have the brotli package installed, you cannot \"\n                 \"use brotliCompress()\";\n    abort();\n}\n\nstd::string brotliDecompress(const char * /*data*/, const size_t /*ndata*/)\n{\n    LOG_ERROR << \"If you do not have the brotli package installed, you cannot \"\n                 \"use brotliDecompress()\";\n    abort();\n}\n#endif\n\nstd::string getMd5(const char *data, const size_t dataLen)\n{\n    return trantor::utils::toHexString(trantor::utils::md5(data, dataLen));\n}\n\nstd::string getSha1(const char *data, const size_t dataLen)\n{\n    return trantor::utils::toHexString(trantor::utils::sha1(data, dataLen));\n}\n\nstd::string getSha256(const char *data, const size_t dataLen)\n{\n    return trantor::utils::toHexString(trantor::utils::sha256(data, dataLen));\n}\n\nstd::string getSha3(const char *data, const size_t dataLen)\n{\n    return trantor::utils::toHexString(trantor::utils::sha3(data, dataLen));\n}\n\nstd::string getBlake2b(const char *data, const size_t dataLen)\n{\n    return trantor::utils::toHexString(trantor::utils::blake2b(data, dataLen));\n}\n\nvoid replaceAll(std::string &s, const std::string &from, const std::string &to)\n{\n    size_t pos = 0;\n    while ((pos = s.find(from, pos)) != std::string::npos)\n    {\n        s.replace(pos, from.size(), to);\n        pos += to.size();\n    }\n}\n\nbool supportsTls() noexcept\n{\n    return trantor::utils::tlsBackend() != \"None\";\n}\n\nbool secureRandomBytes(void *ptr, size_t size)\n{\n    return trantor::utils::secureRandomBytes(ptr, size);\n}\n\nstd::string secureRandomString(size_t size)\n{\n    if (size == 0)\n        return std::string();\n\n    std::string ret(size, 0);\n    const std::string_view chars =\n        \"0123456789\"\n        \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n        \"abcdefghijklmnopqrstuvwxyz\"\n        \"+-\";\n    assert(chars.size() == 64);\n\n    // batch up to 32 bytes of random data for efficiency. Calling\n    // secureRandomBytes can be expensive.\n    auto randByte = []() {\n        thread_local trantor::utils::Hash256 hash;\n        thread_local size_t i = 0;\n        if (i == 0)\n        {\n            bool ok = trantor::utils::secureRandomBytes(&hash, sizeof(hash));\n            if (!ok)\n                throw std::runtime_error(\n                    \"Failed to generate random bytes for secureRandomString\");\n        }\n        unsigned char *hashBytes = reinterpret_cast<unsigned char *>(&hash);\n        auto ret = hashBytes[i];\n        i = (i + 1) % sizeof(hash);\n        return ret;\n    };\n\n    for (size_t i = 0; i < size; ++i)\n        ret[i] = chars[randByte() % 64];\n    return ret;\n}\n\nnamespace internal\n{\nconst size_t fixedRandomNumber = []() {\n    size_t res;\n    utils::secureRandomBytes(&res, sizeof(res));\n    return res;\n}();\n}  // namespace internal\n\n}  // namespace utils\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/WebSocketClientImpl.cc",
    "content": "/**\n *\n *  @file WebSocketClientImpl.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"WebSocketClientImpl.h\"\n#include \"HttpResponseImpl.h\"\n#include \"HttpRequestImpl.h\"\n#include \"HttpResponseParser.h\"\n#include \"HttpUtils.h\"\n#include \"WebSocketConnectionImpl.h\"\n#include \"HttpAppFrameworkImpl.h\"\n#include <drogon/utils/Utilities.h>\n#include <drogon/config.h>\n#include <trantor/net/InetAddress.h>\n#include <trantor/utils/Utilities.h>\n\nusing namespace drogon;\nusing namespace trantor;\n\nWebSocketClientImpl::~WebSocketClientImpl()\n{\n}\n\nWebSocketConnectionPtr WebSocketClientImpl::getConnection()\n{\n    return websockConnPtr_;\n}\n\nvoid WebSocketClientImpl::stop()\n{\n    stop_ = true;\n    if (websockConnPtr_)\n    {\n        websockConnPtr_->shutdown();\n        websockConnPtr_.reset();\n    }\n    tcpClientPtr_.reset();\n}\n\nvoid WebSocketClientImpl::createTcpClient()\n{\n    LOG_TRACE << \"New TcpClient,\" << serverAddr_.toIpPort();\n    tcpClientPtr_ =\n        std::make_shared<trantor::TcpClient>(loop_, serverAddr_, \"httpClient\");\n    if (useSSL_)\n    {\n        auto policy = trantor::TLSPolicy::defaultClientPolicy();\n        policy->setUseOldTLS(useOldTLS_)\n            .setValidate(validateCert_)\n            .setHostname(domain_)\n            .setConfCmds(sslConfCmds_)\n            .setCertPath(clientCertPath_)\n            .setKeyPath(clientKeyPath_);\n        tcpClientPtr_->enableSSL(std::move(policy));\n    }\n    auto thisPtr = shared_from_this();\n    std::weak_ptr<WebSocketClientImpl> weakPtr = thisPtr;\n\n    tcpClientPtr_->setConnectionCallback(\n        [weakPtr](const trantor::TcpConnectionPtr &connPtr) {\n            auto thisPtr = weakPtr.lock();\n            if (!thisPtr)\n                return;\n            if (connPtr->connected())\n            {\n                connPtr->setContext(\n                    std::make_shared<HttpResponseParser>(connPtr));\n                // send request;\n                LOG_TRACE << \"Connection established!\";\n                thisPtr->sendReq(connPtr);\n            }\n            else\n            {\n                LOG_TRACE << \"connection disconnect\";\n                thisPtr->connectionClosedCallback_(thisPtr);\n                thisPtr->websockConnPtr_.reset();\n                if (!thisPtr->stop_)\n                {\n                    thisPtr->loop_->runAfter(1.0, [thisPtr]() {\n                        thisPtr->reconnect();\n                    });\n                }\n            }\n        });\n    tcpClientPtr_->setConnectionErrorCallback([weakPtr]() {\n        auto thisPtr = weakPtr.lock();\n        if (!thisPtr)\n            return;\n        // can't connect to server\n        LOG_TRACE << \"error connecting to server\";\n        thisPtr->requestCallback_(ReqResult::NetworkFailure, nullptr, thisPtr);\n        if (!thisPtr->stop_)\n        {\n            thisPtr->loop_->runAfter(1.0,\n                                     [thisPtr]() { thisPtr->reconnect(); });\n        }\n    });\n    tcpClientPtr_->setMessageCallback(\n        [weakPtr](const trantor::TcpConnectionPtr &connPtr,\n                  trantor::MsgBuffer *msg) {\n            auto thisPtr = weakPtr.lock();\n            if (thisPtr)\n            {\n                thisPtr->onRecvMessage(connPtr, msg);\n            }\n        });\n    tcpClientPtr_->connect();\n}\n\nvoid WebSocketClientImpl::connectToServerInLoop()\n{\n    loop_->assertInLoopThread();\n    upgradeRequest_->addHeader(\"Connection\", \"Upgrade\");\n    upgradeRequest_->addHeader(\"Upgrade\", \"websocket\");\n    bool usePort = ((serverAddr_.toPort() != 80 && !useSSL_) ||\n                    (serverAddr_.toPort() != 443 && useSSL_));\n    upgradeRequest_->addHeader(\n        \"Host\",\n        domain_.empty()\n            ? (usePort ? serverAddr_.toIpPort() : serverAddr_.toIp())\n            : (usePort ? domain_ + \":\" + std::to_string(serverAddr_.toPort())\n                       : domain_));\n    upgradeRequest_->addHeader(\"Sec-WebSocket-Version\", \"13\");\n    auto randStr = utils::genRandomString(16);\n    wsKey_ = utils::base64Encode((const unsigned char *)randStr.data(),\n                                 (unsigned int)randStr.length());\n\n    auto wsKey = wsKey_;\n    wsKey.append(\"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\");\n    unsigned char accKey[20];\n    static_assert(sizeof(accKey) == sizeof(trantor::utils::Hash160));\n    auto sha1 = trantor::utils::sha1(wsKey);\n    memcpy(accKey, &sha1, sizeof(sha1));\n    wsAccept_ = utils::base64Encode(accKey, 20);\n\n    upgradeRequest_->addHeader(\"Sec-WebSocket-Key\", wsKey_);\n    // upgradeRequest_->addHeader(\"Sec-WebSocket-Version\",\"13\");\n\n    assert(!tcpClientPtr_);\n\n    bool hasIpv6Address = false;\n    if (serverAddr_.isIpV6())\n    {\n        auto ipaddr = serverAddr_.ip6NetEndian();\n        for (int i = 0; i < 4; ++i)\n        {\n            if (ipaddr[i] != 0)\n            {\n                hasIpv6Address = true;\n                break;\n            }\n        }\n    }\n\n    if (serverAddr_.ipNetEndian() == 0 && !hasIpv6Address && !domain_.empty() &&\n        serverAddr_.portNetEndian() != 0)\n    {\n        if (!resolver_)\n        {\n            resolver_ = trantor::Resolver::newResolver(loop_);\n        }\n        resolver_->resolve(\n            domain_,\n            [thisPtr = shared_from_this(),\n             hasIpv6Address](const trantor::InetAddress &addr) {\n                thisPtr->loop_->runInLoop([thisPtr, addr, hasIpv6Address]() {\n                    auto port = thisPtr->serverAddr_.portNetEndian();\n                    thisPtr->serverAddr_ = addr;\n                    thisPtr->serverAddr_.setPortNetEndian(port);\n                    LOG_TRACE << \"dns:domain=\" << thisPtr->domain_\n                              << \";ip=\" << thisPtr->serverAddr_.toIp();\n                    if ((thisPtr->serverAddr_.ipNetEndian() != 0 ||\n                         hasIpv6Address) &&\n                        thisPtr->serverAddr_.portNetEndian() != 0)\n                    {\n                        thisPtr->createTcpClient();\n                    }\n                    else\n                    {\n                        thisPtr->requestCallback_(ReqResult::BadServerAddress,\n                                                  nullptr,\n                                                  thisPtr);\n                        return;\n                    }\n                });\n            });\n        return;\n    }\n\n    if ((serverAddr_.ipNetEndian() != 0 || hasIpv6Address) &&\n        serverAddr_.portNetEndian() != 0)\n    {\n        createTcpClient();\n    }\n    else\n    {\n        requestCallback_(ReqResult::BadServerAddress,\n                         nullptr,\n                         shared_from_this());\n        return;\n    }\n}\n\nvoid WebSocketClientImpl::onRecvWsMessage(\n    const trantor::TcpConnectionPtr &connPtr,\n    trantor::MsgBuffer *msgBuffer)\n{\n    if (websockConnPtr_)\n    {\n        websockConnPtr_->onNewMessage(connPtr, msgBuffer);\n    }\n}\n\nvoid WebSocketClientImpl::onRecvMessage(\n    const trantor::TcpConnectionPtr &connPtr,\n    trantor::MsgBuffer *msgBuffer)\n{\n    if (upgraded_)\n    {\n        onRecvWsMessage(connPtr, msgBuffer);\n        return;\n    }\n    auto responseParser = connPtr->getContext<HttpResponseParser>();\n\n    // LOG_TRACE << \"###:\" << msg->readableBytes();\n\n    if (!responseParser->parseResponse(msgBuffer))\n    {\n        requestCallback_(ReqResult::BadResponse, nullptr, shared_from_this());\n        connPtr->shutdown();\n        websockConnPtr_.reset();\n        tcpClientPtr_.reset();\n        return;\n    }\n\n    if (responseParser->gotAll())\n    {\n        auto resp = responseParser->responseImpl();\n        responseParser->reset();\n        auto acceptStr = resp->getHeaderBy(\"sec-websocket-accept\");\n\n        if (resp->statusCode() != k101SwitchingProtocols ||\n            acceptStr != wsAccept_)\n        {\n            requestCallback_(ReqResult::BadResponse,\n                             nullptr,\n                             shared_from_this());\n            connPtr->shutdown();\n            websockConnPtr_.reset();\n            tcpClientPtr_.reset();\n            return;\n        }\n\n        auto &type = resp->getHeaderBy(\"content-type\");\n        if (type.find(\"application/json\") != std::string::npos)\n        {\n            resp->parseJson();\n        }\n        auto &coding = resp->getHeaderBy(\"content-encoding\");\n        if (coding == \"gzip\")\n        {\n            resp->gunzip();\n        }\n#ifdef USE_BROTLI\n        else if (coding == \"br\")\n        {\n            resp->brDecompress();\n        }\n#endif\n        upgraded_ = true;\n        websockConnPtr_ =\n            std::make_shared<WebSocketConnectionImpl>(connPtr, false);\n        websockConnPtr_->setPingMessage(\"\", std::chrono::seconds{30});\n        auto thisPtr = shared_from_this();\n        std::weak_ptr<WebSocketClientImpl> weakPtr = thisPtr;\n        websockConnPtr_->setMessageCallback(\n            [weakPtr](std::string &&message,\n                      const WebSocketConnectionImplPtr &,\n                      const WebSocketMessageType &type) {\n                auto thisPtr = weakPtr.lock();\n                if (!thisPtr)\n                    return;\n                thisPtr->messageCallback_(std::move(message), thisPtr, type);\n            });\n        requestCallback_(ReqResult::Ok, resp, thisPtr);\n        if (msgBuffer->readableBytes() > 0)\n        {\n            onRecvWsMessage(connPtr, msgBuffer);\n        }\n    }\n    else\n    {\n        return;\n    }\n}\n\nvoid WebSocketClientImpl::reconnect()\n{\n    tcpClientPtr_.reset();\n    websockConnPtr_.reset();\n    upgraded_ = false;\n    connectToServerInLoop();\n}\n\nWebSocketClientImpl::WebSocketClientImpl(trantor::EventLoop *loop,\n                                         const trantor::InetAddress &addr,\n                                         bool useSSL,\n                                         bool useOldTLS,\n                                         bool validateCert)\n    : loop_(loop),\n      serverAddr_(addr),\n      useSSL_(useSSL),\n      useOldTLS_(useOldTLS),\n      validateCert_(validateCert)\n{\n    if (addr.isUnspecified())\n        LOG_ERROR << \"Bad IP passed to WebSocket client\";\n}\n\nWebSocketClientImpl::WebSocketClientImpl(trantor::EventLoop *loop,\n                                         const std::string &hostString,\n                                         bool useOldTLS,\n                                         bool validateCert)\n    : loop_(loop), useOldTLS_(useOldTLS), validateCert_(validateCert)\n{\n    auto lowerHost = hostString;\n    std::transform(lowerHost.begin(),\n                   lowerHost.end(),\n                   lowerHost.begin(),\n                   [](unsigned char c) { return tolower(c); });\n    if (lowerHost.find(\"wss://\") != std::string::npos)\n    {\n        useSSL_ = true;\n        lowerHost = lowerHost.substr(6);\n    }\n    else if (lowerHost.find(\"ws://\") != std::string::npos)\n    {\n        useSSL_ = false;\n        lowerHost = lowerHost.substr(5);\n    }\n    else\n    {\n        return;\n    }\n    auto pos = lowerHost.find(']');\n    if (lowerHost[0] == '[' && pos != std::string::npos)\n    {\n        // ipv6\n        domain_ = lowerHost.substr(1, pos - 1);\n        if (lowerHost[pos + 1] == ':')\n        {\n            auto portStr = lowerHost.substr(pos + 2);\n            pos = portStr.find('/');\n            if (pos != std::string::npos)\n            {\n                portStr = portStr.substr(0, pos);\n            }\n            auto port = atoi(portStr.c_str());\n            if (port > 0 && port < 65536)\n            {\n                serverAddr_ = InetAddress(domain_, port, true);\n            }\n        }\n        else\n        {\n            if (useSSL_)\n            {\n                serverAddr_ = InetAddress(domain_, 443, true);\n            }\n            else\n            {\n                serverAddr_ = InetAddress(domain_, 80, true);\n            }\n        }\n    }\n    else\n    {\n        auto pos = lowerHost.find(':');\n        if (pos != std::string::npos)\n        {\n            domain_ = lowerHost.substr(0, pos);\n            auto portStr = lowerHost.substr(pos + 1);\n            pos = portStr.find('/');\n            if (pos != std::string::npos)\n            {\n                portStr = portStr.substr(0, pos);\n            }\n            auto port = atoi(portStr.c_str());\n            if (port > 0 && port < 65536)\n            {\n                serverAddr_ = InetAddress(domain_, port);\n            }\n        }\n        else\n        {\n            domain_ = lowerHost;\n            pos = domain_.find('/');\n            if (pos != std::string::npos)\n            {\n                domain_ = domain_.substr(0, pos);\n            }\n            if (useSSL_)\n            {\n                serverAddr_ = InetAddress(domain_, 443);\n            }\n            else\n            {\n                serverAddr_ = InetAddress(domain_, 80);\n            }\n        }\n    }\n    LOG_TRACE << \"userSSL=\" << useSSL_ << \" domain=\" << domain_;\n}\n\nvoid WebSocketClientImpl::sendReq(const trantor::TcpConnectionPtr &connPtr)\n{\n    trantor::MsgBuffer buffer;\n    assert(upgradeRequest_);\n    auto implPtr = static_cast<HttpRequestImpl *>(upgradeRequest_.get());\n    implPtr->appendToBuffer(&buffer);\n    LOG_TRACE << \"Send request:\"\n              << std::string(buffer.peek(), buffer.readableBytes());\n    connPtr->send(std::move(buffer));\n}\n\nvoid WebSocketClientImpl::connectToServer(\n    const HttpRequestPtr &request,\n    const WebSocketRequestCallback &callback)\n{\n    assert(callback);\n    if (loop_->isInLoopThread())\n    {\n        upgradeRequest_ = request;\n        requestCallback_ = callback;\n        connectToServerInLoop();\n    }\n    else\n    {\n        auto thisPtr = shared_from_this();\n        loop_->queueInLoop([request, callback, thisPtr] {\n            thisPtr->upgradeRequest_ = request;\n            thisPtr->requestCallback_ = callback;\n            thisPtr->connectToServerInLoop();\n        });\n    }\n}\n\nvoid WebSocketClientImpl::setCertPath(const std::string &cert,\n                                      const std::string &key)\n{\n    clientCertPath_ = cert;\n    clientKeyPath_ = key;\n}\n\nvoid WebSocketClientImpl::addSSLConfigs(\n    const std::vector<std::pair<std::string, std::string>> &sslConfCmds)\n{\n    for (const auto &cmd : sslConfCmds)\n    {\n        sslConfCmds_.push_back(cmd);\n    }\n}\n\nWebSocketClientPtr WebSocketClient::newWebSocketClient(const std::string &ip,\n                                                       uint16_t port,\n                                                       bool useSSL,\n                                                       trantor::EventLoop *loop,\n                                                       bool useOldTLS,\n                                                       bool validateCert)\n{\n    bool isIpv6 = ip.find(':') == std::string::npos ? false : true;\n    return std::make_shared<WebSocketClientImpl>(\n        loop == nullptr ? HttpAppFrameworkImpl::instance().getLoop() : loop,\n        trantor::InetAddress(ip, port, isIpv6),\n        useSSL,\n        useOldTLS,\n        validateCert);\n}\n\nWebSocketClientPtr WebSocketClient::newWebSocketClient(\n    const std::string &hostString,\n    trantor::EventLoop *loop,\n    bool useOldTLS,\n    bool validateCert)\n{\n    return std::make_shared<WebSocketClientImpl>(\n        loop == nullptr ? HttpAppFrameworkImpl::instance().getLoop() : loop,\n        hostString,\n        useOldTLS,\n        validateCert);\n}\n"
  },
  {
    "path": "lib/src/WebSocketClientImpl.h",
    "content": "/**\n *\n *  @file WebSocketClientImpl.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"impl_forwards.h\"\n#include <drogon/WebSocketClient.h>\n#include <trantor/net/EventLoop.h>\n#include <trantor/net/TcpClient.h>\n#include <trantor/utils/NonCopyable.h>\n\n#include <memory>\n#include <string>\n\nnamespace drogon\n{\nclass WebSocketClientImpl\n    : public WebSocketClient,\n      public std::enable_shared_from_this<WebSocketClientImpl>\n{\n  public:\n    WebSocketConnectionPtr getConnection() override;\n\n    void setMessageHandler(\n        const std::function<void(std::string &&message,\n                                 const WebSocketClientPtr &,\n                                 const WebSocketMessageType &)> &callback)\n        override\n    {\n        messageCallback_ = callback;\n    }\n\n    void setConnectionClosedHandler(\n        const std::function<void(const WebSocketClientPtr &)> &callback)\n        override\n    {\n        connectionClosedCallback_ = callback;\n    }\n\n    void connectToServer(const HttpRequestPtr &request,\n                         const WebSocketRequestCallback &callback) override;\n\n    void setCertPath(const std::string &cert, const std::string &key) override;\n\n    void addSSLConfigs(const std::vector<std::pair<std::string, std::string>>\n                           &sslConfCmds) override;\n\n    trantor::EventLoop *getLoop() override\n    {\n        return loop_;\n    }\n\n    WebSocketClientImpl(trantor::EventLoop *loop,\n                        const trantor::InetAddress &addr,\n                        bool useSSL = false,\n                        bool useOldTLS = false,\n                        bool validateCert = true);\n\n    WebSocketClientImpl(trantor::EventLoop *loop,\n                        const std::string &hostString,\n                        bool useOldTLS = false,\n                        bool validateCert = true);\n\n    void stop() override;\n\n    ~WebSocketClientImpl() override;\n\n  private:\n    std::shared_ptr<trantor::TcpClient> tcpClientPtr_;\n    trantor::EventLoop *loop_;\n    trantor::InetAddress serverAddr_;\n    std::string domain_;\n    bool useSSL_{false};\n    bool useOldTLS_{false};\n    bool validateCert_{true};\n    bool upgraded_{false};\n    bool stop_{false};\n    std::string wsKey_;\n    std::string wsAccept_;\n    std::string clientCertPath_;\n    std::string clientKeyPath_;\n    std::vector<std::pair<std::string, std::string>> sslConfCmds_;\n\n    HttpRequestPtr upgradeRequest_;\n    std::function<void(std::string &&,\n                       const WebSocketClientPtr &,\n                       const WebSocketMessageType &)>\n        messageCallback_ = [](std::string &&,\n                              const WebSocketClientPtr &,\n                              const WebSocketMessageType &) {};\n    std::function<void(const WebSocketClientPtr &)> connectionClosedCallback_ =\n        [](const WebSocketClientPtr &) {};\n    WebSocketRequestCallback requestCallback_;\n    WebSocketConnectionImplPtr websockConnPtr_;\n\n    void connectToServerInLoop();\n    void sendReq(const trantor::TcpConnectionPtr &connPtr);\n    void onRecvMessage(const trantor::TcpConnectionPtr &, trantor::MsgBuffer *);\n    void onRecvWsMessage(const trantor::TcpConnectionPtr &,\n                         trantor::MsgBuffer *);\n    void reconnect();\n    void createTcpClient();\n    std::shared_ptr<trantor::Resolver> resolver_;\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/WebSocketConnectionImpl.cc",
    "content": "/**\n *\n *  @file WebSocketConnectionImpl.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"WebSocketConnectionImpl.h\"\n#include \"HttpAppFrameworkImpl.h\"\n#include <json/value.h>\n#include <json/writer.h>\n#include <thread>\n#include <limits>\n\nusing namespace drogon;\n\nWebSocketConnectionImpl::WebSocketConnectionImpl(\n    const trantor::TcpConnectionPtr &conn,\n    bool isServer)\n    : tcpConnectionPtr_(conn),\n      localAddr_(conn->localAddr()),\n      peerAddr_(conn->peerAddr()),\n      isServer_(isServer),\n      usingMask_(false)\n{\n}\n\nWebSocketConnectionImpl::~WebSocketConnectionImpl()\n{\n    shutdown();\n}\n\nvoid WebSocketConnectionImpl::send(const char *msg,\n                                   uint64_t len,\n                                   const WebSocketMessageType type)\n{\n    unsigned char opcode;\n    if (type == WebSocketMessageType::Text)\n        opcode = 1;\n    else if (type == WebSocketMessageType::Binary)\n        opcode = 2;\n    else if (type == WebSocketMessageType::Close)\n    {\n        assert(len <= 125);\n        opcode = 8;\n    }\n    else if (type == WebSocketMessageType::Ping)\n    {\n        assert(len <= 125);\n        opcode = 9;\n    }\n    else if (type == WebSocketMessageType::Pong)\n    {\n        assert(len <= 125);\n        opcode = 10;\n    }\n    else\n    {\n        opcode = 0;\n        assert(0);\n    }\n    sendWsData(msg, len, opcode);\n}\n\nvoid WebSocketConnectionImpl::sendWsData(const char *msg,\n                                         uint64_t len,\n                                         unsigned char opcode)\n{\n    LOG_TRACE << \"send \" << len << \" bytes\";\n\n    // Format the frame\n    std::string bytesFormatted;\n    bytesFormatted.resize(len + 10);\n    bytesFormatted[0] = char(0x80 | (opcode & 0x0f));\n\n    int indexStartRawData = -1;\n\n    if (len <= 125)\n    {\n        bytesFormatted[1] = static_cast<char>(len);\n        indexStartRawData = 2;\n    }\n    else if (len <= 65535)\n    {\n        bytesFormatted[1] = 126;\n        bytesFormatted[2] = ((len >> 8) & 255);\n        bytesFormatted[3] = ((len) & 255);\n        LOG_TRACE << \"bytes[2]=\" << (size_t)bytesFormatted[2];\n        LOG_TRACE << \"bytes[3]=\" << (size_t)bytesFormatted[3];\n        indexStartRawData = 4;\n    }\n    else\n    {\n        bytesFormatted[1] = 127;\n        bytesFormatted[2] = ((len >> 56) & 255);\n        bytesFormatted[3] = ((len >> 48) & 255);\n        bytesFormatted[4] = ((len >> 40) & 255);\n        bytesFormatted[5] = ((len >> 32) & 255);\n        bytesFormatted[6] = ((len >> 24) & 255);\n        bytesFormatted[7] = ((len >> 16) & 255);\n        bytesFormatted[8] = ((len >> 8) & 255);\n        bytesFormatted[9] = ((len) & 255);\n\n        indexStartRawData = 10;\n    }\n    if (!isServer_)\n    {\n        int random;\n        // Use the cached randomness if no one else is also using it. Otherwise\n        // generate one from scratch.\n        if (!usingMask_.exchange(true, std::memory_order_acq_rel))\n        {\n            if (masks_.empty())\n            {\n                masks_.resize(16);\n                bool status =\n                    utils::secureRandomBytes(masks_.data(),\n                                             masks_.size() * sizeof(uint32_t));\n                if (status == false)\n                {\n                    LOG_ERROR << \"Failed to generate random numbers for \"\n                                 \"WebSocket mask\";\n                    abort();\n                }\n            }\n            random = masks_.back();\n            masks_.pop_back();\n            usingMask_.store(false, std::memory_order_release);\n        }\n        else\n        {\n            bool status = utils::secureRandomBytes(&random, sizeof(random));\n            if (status == false)\n            {\n                LOG_ERROR\n                    << \"Failed to generate random numbers for WebSocket mask\";\n                abort();\n            }\n        }\n\n        bytesFormatted[1] = (bytesFormatted[1] | 0x80);\n        bytesFormatted.resize(indexStartRawData + 4 + len);\n        memcpy(&bytesFormatted[indexStartRawData], &random, sizeof(random));\n        for (size_t i = 0; i < len; ++i)\n        {\n            bytesFormatted[indexStartRawData + 4 + i] =\n                (msg[i] ^ bytesFormatted[indexStartRawData + (i % 4)]);\n        }\n    }\n    else\n    {\n        bytesFormatted.resize(indexStartRawData);\n        bytesFormatted.append(msg, len);\n    }\n    tcpConnectionPtr_->send(std::move(bytesFormatted));\n}\n\nvoid WebSocketConnectionImpl::send(const std::string_view msg,\n                                   const WebSocketMessageType type)\n{\n    send(msg.data(), msg.length(), type);\n}\n\nvoid WebSocketConnectionImpl::sendJson(const Json::Value &json,\n                                       const WebSocketMessageType type)\n{\n    static std::once_flag once;\n    static Json::StreamWriterBuilder builder;\n    std::call_once(once, []() {\n        builder[\"commentStyle\"] = \"None\";\n        builder[\"indentation\"] = \"\";\n        if (!app().isUnicodeEscapingUsedInJson())\n        {\n            builder[\"emitUTF8\"] = true;\n        }\n        auto &precision = app().getFloatPrecisionInJson();\n        if (precision.first != 0)\n        {\n            builder[\"precision\"] = precision.first;\n            builder[\"precisionType\"] = precision.second;\n        }\n    });\n    auto msg = writeString(builder, json);\n    send(msg.data(), msg.length(), type);\n}\n\nconst trantor::InetAddress &WebSocketConnectionImpl::localAddr() const\n{\n    return localAddr_;\n}\n\nconst trantor::InetAddress &WebSocketConnectionImpl::peerAddr() const\n{\n    return peerAddr_;\n}\n\nbool WebSocketConnectionImpl::connected() const\n{\n    return tcpConnectionPtr_->connected();\n}\n\nbool WebSocketConnectionImpl::disconnected() const\n{\n    return tcpConnectionPtr_->disconnected();\n}\n\nvoid WebSocketConnectionImpl::WebSocketConnectionImpl::shutdown(\n    const CloseCode code,\n    const std::string &reason)\n{\n    tcpConnectionPtr_->getLoop()->invalidateTimer(pingTimerId_);\n    if (!tcpConnectionPtr_->connected())\n        return;\n    std::string message;\n    message.resize(reason.length() + 2);\n    auto c = htons(static_cast<unsigned short>(code));\n    memcpy(&message[0], &c, 2);\n    if (!reason.empty())\n        memcpy(&message[2], reason.data(), reason.length());\n    send(message, WebSocketMessageType::Close);\n    tcpConnectionPtr_->shutdown();\n}\n\nvoid WebSocketConnectionImpl::WebSocketConnectionImpl::forceClose()\n{\n    tcpConnectionPtr_->forceClose();\n}\n\nvoid WebSocketConnectionImpl::setPingMessage(\n    const std::string &message,\n    const std::chrono::duration<double> &interval)\n{\n    auto loop = tcpConnectionPtr_->getLoop();\n    if (loop->isInLoopThread())\n    {\n        setPingMessageInLoop(std::string{message}, interval);\n    }\n    else\n    {\n        loop->queueInLoop(\n            [msg = message, interval, thisPtr = shared_from_this()]() mutable {\n                thisPtr->setPingMessageInLoop(std::move(msg), interval);\n            });\n    }\n}\n\nvoid WebSocketConnectionImpl::disablePing()\n{\n    auto loop = tcpConnectionPtr_->getLoop();\n    if (loop->isInLoopThread())\n    {\n        disablePingInLoop();\n    }\n    else\n    {\n        loop->queueInLoop(\n            [thisPtr = shared_from_this()]() { thisPtr->disablePingInLoop(); });\n    }\n}\n\nbool WebSocketMessageParser::parse(trantor::MsgBuffer *buffer)\n{\n    // According to the rfc6455\n    gotAll_ = false;\n    while (buffer->readableBytes() >= 2)\n    {\n        unsigned char opcode = (*buffer)[0] & 0x0f;\n        bool isControlFrame = false;\n        switch (opcode)\n        {\n            case 0:\n                LOG_TRACE << \"continuation frame\";\n                break;\n            case 1:\n                type_ = WebSocketMessageType::Text;\n                break;\n            case 2:\n                type_ = WebSocketMessageType::Binary;\n                break;\n            case 8:\n                type_ = WebSocketMessageType::Close;\n                isControlFrame = true;\n                break;\n            case 9:\n                type_ = WebSocketMessageType::Ping;\n                isControlFrame = true;\n                break;\n            case 10:\n                type_ = WebSocketMessageType::Pong;\n                isControlFrame = true;\n                break;\n            default:\n                LOG_ERROR << \"Unknown frame type\";\n                return false;\n                break;\n        }\n\n        bool isFin = (((*buffer)[0] & 0x80) == 0x80);\n        if (!isFin && isControlFrame)\n        {\n            // rfc6455-5.5\n            LOG_ERROR << \"Bad frame: all control frames MUST NOT be fragmented\";\n            return false;\n        }\n        auto secondByte = (*buffer)[1];\n        size_t length = secondByte & 127;\n        int isMasked = (secondByte & 0x80);\n        if (isMasked != 0)\n        {\n            LOG_TRACE << \"data encoded!\";\n        }\n        else\n            LOG_TRACE << \"plain data\";\n        size_t indexFirstMask = 2;\n\n        if (length == 126)\n        {\n            indexFirstMask = 4;\n        }\n        else if (length == 127)\n        {\n            indexFirstMask = 10;\n        }\n        if (indexFirstMask > 2)\n        {\n            if (buffer->readableBytes() < indexFirstMask)\n            {\n                // Not enough data yet, wait for more.\n                return true;\n            }\n            if (isControlFrame)\n            {\n                // rfc6455-5.5\n                LOG_ERROR << \"Bad frame: all control frames MUST have a \"\n                             \"payload length \"\n                             \"of 125 bytes or less\";\n                return false;\n            }\n            if (indexFirstMask == 4)\n            {\n                length = (unsigned char)(*buffer)[2];\n                length = (length << 8) + (unsigned char)(*buffer)[3];\n            }\n            else if (indexFirstMask == 10)\n            {\n                length = 0;\n                for (int i = 2; i <= 9; ++i)\n                {\n                    if (length > ((std::numeric_limits<size_t>::max)() >> 8))\n                    {\n                        LOG_ERROR\n                            << \"Payload length too large to handle safely\";\n                        return false;\n                    }\n                    length = (length << 8) + (unsigned char)(*buffer)[i];\n                }\n            }\n            else\n            {\n                LOG_ERROR << \"Websock parsing failed!\";\n                return false;\n            }\n        }\n        if (isMasked != 0)\n        {\n            // The message is sent by the client, check the length\n            if (length > HttpAppFrameworkImpl::instance()\n                             .getClientMaxWebSocketMessageSize())\n            {\n                LOG_ERROR << \"The size of the WebSocket message is too large!\";\n                buffer->retrieveAll();\n                return false;\n            }\n            if (buffer->readableBytes() >= (indexFirstMask + 4 + length))\n            {\n                auto masks = buffer->peek() + indexFirstMask;\n                auto indexFirstDataByte = indexFirstMask + 4;\n                auto rawData = buffer->peek() + indexFirstDataByte;\n                auto oldLen = message_.length();\n                message_.resize(oldLen + length);\n                for (size_t i = 0; i < length; ++i)\n                {\n                    message_[oldLen + i] = (rawData[i] ^ masks[i % 4]);\n                }\n                buffer->retrieve(indexFirstMask + 4 + length);\n                if (isFin)\n                {\n                    gotAll_ = true;\n                    return true;\n                }\n            }\n            else\n            {\n                // Not enough data yet, wait for more.\n                return true;\n            }\n        }\n        else\n        {\n            if (buffer->readableBytes() >= (indexFirstMask + length))\n            {\n                auto rawData = buffer->peek() + indexFirstMask;\n                message_.append(rawData, length);\n                buffer->retrieve(indexFirstMask + length);\n                if (isFin)\n                {\n                    gotAll_ = true;\n                    return true;\n                }\n            }\n            else\n            {\n                // Not enough data yet, wait for more.\n                return true;\n            }\n        }\n    }\n    return true;\n}\n\nvoid WebSocketConnectionImpl::onNewMessage(\n    const trantor::TcpConnectionPtr &connPtr,\n    trantor::MsgBuffer *buffer)\n{\n    auto self = shared_from_this();\n    while (buffer->readableBytes() > 0)\n    {\n        auto success = parser_.parse(buffer);\n        if (success)\n        {\n            std::string message;\n            WebSocketMessageType type;\n            if (parser_.gotAll(message, type))\n            {\n                if (type == WebSocketMessageType::Ping)\n                {\n                    // ping\n                    send(message, WebSocketMessageType::Pong);\n                }\n                else if (type == WebSocketMessageType::Close)\n                {\n                    // close\n                    connPtr->shutdown();\n                }\n                else if (type == WebSocketMessageType::Unknown)\n                {\n                    return;\n                }\n                // LOG_TRACE << \"new message received: \" << message\n                //           << \"\\n(type=\" << (int)type << \")\";\n                messageCallback_(std::move(message), self, type);\n            }\n            else\n            {\n                return;\n            }\n        }\n        else\n        {\n            // Websock error!\n            connPtr->shutdown();\n            return;\n        }\n    }\n    return;\n}\n\nvoid WebSocketConnectionImpl::disablePingInLoop()\n{\n    if (pingTimerId_ != trantor::InvalidTimerId)\n    {\n        tcpConnectionPtr_->getLoop()->invalidateTimer(pingTimerId_);\n    }\n}\n\nvoid WebSocketConnectionImpl::setPingMessageInLoop(\n    std::string &&message,\n    const std::chrono::duration<double> &interval)\n{\n    std::weak_ptr<WebSocketConnectionImpl> weakPtr = shared_from_this();\n    disablePingInLoop();\n    pingTimerId_ = tcpConnectionPtr_->getLoop()->runEvery(\n        interval.count(), [weakPtr, message = std::move(message)]() {\n            auto thisPtr = weakPtr.lock();\n            if (thisPtr)\n            {\n                thisPtr->send(message, WebSocketMessageType::Ping);\n            }\n        });\n}\n"
  },
  {
    "path": "lib/src/WebSocketConnectionImpl.h",
    "content": "/**\n *\n *  @file WebSocketConnectionImpl.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"impl_forwards.h\"\n#include <drogon/WebSocketConnection.h>\n#include <json/value.h>\n#include <string_view>\n#include <trantor/utils/NonCopyable.h>\n#include <trantor/net/TcpConnection.h>\n\nnamespace drogon\n{\nclass WebSocketConnectionImpl;\nusing WebSocketConnectionImplPtr = std::shared_ptr<WebSocketConnectionImpl>;\n\nclass WebSocketMessageParser\n{\n  public:\n    bool parse(trantor::MsgBuffer *buffer);\n\n    bool gotAll(std::string &message, WebSocketMessageType &type)\n    {\n        assert(message.empty());\n        if (!gotAll_)\n            return false;\n        message.swap(message_);\n        type = type_;\n        return true;\n    }\n\n  private:\n    std::string message_;\n    WebSocketMessageType type_;\n    bool gotAll_{false};\n};\n\nclass WebSocketConnectionImpl final\n    : public WebSocketConnection,\n      public std::enable_shared_from_this<WebSocketConnectionImpl>,\n      public trantor::NonCopyable\n{\n  public:\n    explicit WebSocketConnectionImpl(const trantor::TcpConnectionPtr &conn,\n                                     bool isServer = true);\n\n    ~WebSocketConnectionImpl() override;\n    void send(\n        const char *msg,\n        uint64_t len,\n        const WebSocketMessageType type = WebSocketMessageType::Text) override;\n    void send(\n        std::string_view msg,\n        const WebSocketMessageType type = WebSocketMessageType::Text) override;\n    void sendJson(\n        const Json::Value &json,\n        const WebSocketMessageType type = WebSocketMessageType::Text) override;\n\n    const trantor::InetAddress &localAddr() const override;\n    const trantor::InetAddress &peerAddr() const override;\n\n    bool connected() const override;\n    bool disconnected() const override;\n\n    void shutdown(const CloseCode code = CloseCode::kNormalClosure,\n                  const std::string &reason = \"\") override;  // close write\n    void forceClose() override;                              // close\n\n    void setPingMessage(const std::string &message,\n                        const std::chrono::duration<double> &interval) override;\n\n    void disablePing() override;\n\n    void setMessageCallback(\n        const std::function<void(std::string &&,\n                                 const WebSocketConnectionImplPtr &,\n                                 const WebSocketMessageType &)> &callback)\n    {\n        messageCallback_ = callback;\n    }\n\n    void setCloseCallback(\n        const std::function<void(const WebSocketConnectionImplPtr &)> &callback)\n    {\n        closeCallback_ = callback;\n    }\n\n    void onNewMessage(const trantor::TcpConnectionPtr &connPtr,\n                      trantor::MsgBuffer *buffer);\n\n    void onClose()\n    {\n        if (pingTimerId_ != trantor::InvalidTimerId)\n            tcpConnectionPtr_->getLoop()->invalidateTimer(pingTimerId_);\n        closeCallback_(shared_from_this());\n    }\n\n  private:\n    trantor::TcpConnectionPtr tcpConnectionPtr_;\n    trantor::InetAddress localAddr_;\n    trantor::InetAddress peerAddr_;\n    bool isServer_{true};\n    WebSocketMessageParser parser_;\n    trantor::TimerId pingTimerId_{trantor::InvalidTimerId};\n    std::vector<uint32_t> masks_;\n    std::atomic<bool> usingMask_;\n\n    std::function<void(std::string &&,\n                       const WebSocketConnectionImplPtr &,\n                       const WebSocketMessageType &)>\n        messageCallback_ = [](std::string &&,\n                              const WebSocketConnectionImplPtr &,\n                              const WebSocketMessageType &) {};\n    std::function<void(const WebSocketConnectionImplPtr &)> closeCallback_ =\n        [](const WebSocketConnectionImplPtr &) {};\n    void sendWsData(const char *msg, uint64_t len, unsigned char opcode);\n    void disablePingInLoop();\n    void setPingMessageInLoop(std::string &&message,\n                              const std::chrono::duration<double> &interval);\n};\n\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/YamlConfigAdapter.cc",
    "content": "#include \"YamlConfigAdapter.h\"\n#ifdef HAS_YAML_CPP\n#include <yaml-cpp/yaml.h>\n#endif\nusing namespace drogon;\n#ifdef HAS_YAML_CPP\nnamespace YAML\n{\nstatic bool yaml2json(const Node &node, Json::Value &jsonValue)\n{\n    if (node.IsNull())\n    {\n        return false;\n    }\n    else if (node.IsScalar())\n    {\n        if (node.Tag() != \"!\")\n        {\n            try\n            {\n                jsonValue = node.as<Json::Value::Int64>();\n                return true;\n            }\n            catch (const YAML::BadConversion &e)\n            {\n            }\n            try\n            {\n                jsonValue = node.as<double>();\n                return true;\n            }\n            catch (const YAML::BadConversion &e)\n            {\n            }\n            try\n            {\n                jsonValue = node.as<bool>();\n                return true;\n            }\n            catch (const YAML::BadConversion &e)\n            {\n            }\n        }\n\n        Json::Value v(node.Scalar());\n        jsonValue.swapPayload(v);\n        return true;\n    }\n    else if (node.IsSequence())\n    {\n        for (std::size_t i = 0; i < node.size(); i++)\n        {\n            Json::Value v;\n            if (yaml2json(node[i], v))\n            {\n                jsonValue.append(v);\n            }\n            else\n            {\n                return false;\n            }\n        }\n\n        return true;\n    }\n    else if (node.IsMap())\n    {\n        for (YAML::const_iterator it = node.begin(); it != node.end(); ++it)\n        {\n            Json::Value v;\n            if (yaml2json(it->second, v))\n            {\n                jsonValue[it->first.Scalar()] = v;\n            }\n            else\n            {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    return false;\n}\n\ntemplate <>\nstruct convert<Json::Value>\n{\n    static bool decode(const Node &node, Json::Value &rhs)\n    {\n        return yaml2json(node, rhs);\n    };\n};\n}  // namespace YAML\n\n#endif\nJson::Value YamlConfigAdapter::getJson(const std::string &content) const\n    noexcept(false)\n{\n#if HAS_YAML_CPP\n    // parse yaml file\n    YAML::Node config = YAML::Load(content);\n    if (!config.IsNull())\n    {\n        return config.as<Json::Value>();\n    }\n    else\n        return Json::Value();\n#else\n    throw std::runtime_error(\"please install yaml-cpp library\");\n#endif\n}\n\nstd::vector<std::string> YamlConfigAdapter::getExtensions() const\n{\n    return {\"yaml\", \"yml\"};\n}\n"
  },
  {
    "path": "lib/src/YamlConfigAdapter.h",
    "content": "#pragma once\n#include \"ConfigAdapter.h\"\n\nnamespace drogon\n{\nclass YamlConfigAdapter : public ConfigAdapter\n{\n  public:\n    YamlConfigAdapter() = default;\n    ~YamlConfigAdapter() override = default;\n    Json::Value getJson(const std::string &content) const\n        noexcept(false) override;\n    std::vector<std::string> getExtensions() const override;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/drogon_test.cc",
    "content": "#include <drogon/drogon_test.h>\n\n#include <set>\n#include <future>\n#include <condition_variable>\n\nnamespace drogon\n{\nnamespace test\n{\nstd::mutex ThreadSafeStream::mtx_;\n\nnamespace internal\n{\nstd::mutex mtxRegister;\nstd::mutex mtxTestStats;\nbool testHasPrinted = false;\nstd::set<Case *> registeredTests;\nstd::promise<void> allTestRan;\nstd::atomic<size_t> numAssertions;\nstd::atomic<size_t> numCorrectAssertions;\nsize_t numTestCases;\nstd::atomic<size_t> numFailedTestCases;\nbool printSuccessfulTests;\n\nvoid registerCase(Case *test)\n{\n    std::unique_lock<std::mutex> l(mtxRegister);\n    registeredTests.insert(test);\n}\n\nvoid unregisterCase(Case *test)\n{\n    std::unique_lock<std::mutex> l(mtxRegister);\n    registeredTests.erase(test);\n\n    if (registeredTests.empty())\n        allTestRan.set_value();\n}\n\nstatic std::string leftpad(const std::string &str, size_t len)\n{\n    if (len <= str.size())\n        return str;\n    return std::string(len - str.size(), ' ') + str;\n}\n\nstd::string prettifyString(const std::string_view sv, size_t maxLength)\n{\n    if (sv.size() <= maxLength)\n        return \"\\\"\" + escapeString(sv) + \"\\\"\";\n\n    const std::string msg = \"...\\\" (truncated)\";\n    return \"\\\"\" + escapeString(sv.substr(0, maxLength)) + msg;\n}\n\n}  // namespace internal\n\nstatic void printHelp(std::string_view argv0)\n{\n    print() << \"A Drogon Test application:\\n\\n\"\n            << \"Usage: \" << argv0 << \" [options]\\n\"\n            << \"options:\\n\"\n            << \"    -r            Run a specific test\\n\"\n            << \"    -s            Print successful tests\\n\"\n            << \"    -l            List available tests\\n\"\n            << \"    -h | --help   Print this help message\\n\";\n}\n\nvoid printTestStats()\n{\n    std::unique_lock<std::mutex> lk(internal::mtxTestStats);\n    if (internal::testHasPrinted)\n        return;\n    const size_t successAssertions = internal::numCorrectAssertions;\n    const size_t totalAssertions = internal::numAssertions;\n    const size_t successTests =\n        internal::numTestCases - internal::numFailedTestCases;\n    const size_t totalTests = internal::numTestCases;\n\n    float ratio;\n    if (totalAssertions != 0)\n        ratio = (float)successTests / totalTests;\n    else\n        ratio = 1;\n    const size_t barSize = 80;\n    auto greenBar = size_t(barSize * ratio);\n    auto redBar = size_t(barSize * (1 - ratio));\n    if (greenBar + redBar != barSize)\n    {\n        float fraction = (ratio * barSize) - (size_t)(ratio * barSize);\n        if (fraction >= 0.5f)\n            greenBar++;\n        else\n            redBar++;\n    }\n    if (successAssertions != totalAssertions && redBar == 0)\n    {\n        redBar = 1;\n        greenBar--;\n    }\n\n    print() << \"\\n\\x1B[0;31m\" << std::string(redBar, '=') << \"\\x1B[0;32m\"\n            << std::string(greenBar, '=') << \"\\x1B[0m\\n\";\n\n    if (successAssertions == totalAssertions)\n    {\n        print() << \"\\x1B[1;32m  All tests passed\\x1B[0m (\" << totalAssertions\n                << \" assertions in \" << totalTests << \" tests cases).\\n\";\n    }\n    else\n    {\n        std::string totalAssertsionStr = std::to_string(totalAssertions);\n        std::string successAssertionsStr = std::to_string(successAssertions);\n        std::string failedAssertsionStr =\n            std::to_string(totalAssertions - successAssertions);\n        std::string totalTestsStr = std::to_string(totalTests);\n        std::string successTestsStr = std::to_string(successTests);\n        std::string failedTestsStr = std::to_string(totalTests - successTests);\n        const size_t totalLen =\n            (std::max)(totalAssertsionStr.size(), totalTestsStr.size());\n        const size_t successLen =\n            (std::max)(successAssertionsStr.size(), successTestsStr.size());\n        const size_t failedLen =\n            (std::max)(failedAssertsionStr.size(), failedTestsStr.size());\n        using internal::leftpad;\n        print() << \"assertions: \" << leftpad(totalAssertsionStr, totalLen)\n                << \" | \\x1B[0;32m\" << leftpad(successAssertionsStr, successLen)\n                << \" passed\\x1B[0m | \\x1B[0;31m\"\n                << leftpad(failedAssertsionStr, failedLen) << \" failed\\x1B[0m\\n\"\n                << \"test cases: \" << leftpad(totalTestsStr, totalLen)\n                << \" | \\x1B[0;32m\" << leftpad(successTestsStr, successLen)\n                << \" passed\\x1B[0m | \\x1B[0;31m\"\n                << leftpad(failedTestsStr, failedLen) << \" failed\\x1B[0m\\n\";\n    }\n    internal::testHasPrinted = true;\n}\n\nint run(int argc, char **argv)\n{\n    internal::numCorrectAssertions = 0;\n    internal::numAssertions = 0;\n    internal::numFailedTestCases = 0;\n    internal::numTestCases = 0;\n    internal::printSuccessfulTests = false;\n\n    std::string targetTest;\n    bool listTests = false;\n    for (int i = 1; i < argc; i++)\n    {\n        const std::string param = argv[i];\n        if (param == \"-r\")\n        {\n            if (!targetTest.empty())\n            {\n                printErr() << \"Only one test can be specified to run\\n\";\n                exit(1);\n            }\n            else if (i + 1 >= argc)\n            {\n                printErr() << \"Missing test name after -r.\\n\";\n                exit(1);\n            }\n\n            targetTest = argv[i + 1];\n            i++;\n        }\n        else if (param == \"-h\" || param == \"--help\")\n        {\n            printHelp(argv[0]);\n            exit(0);\n        }\n        else if (param == \"-s\")\n        {\n            internal::printSuccessfulTests = true;\n        }\n        else if (param == \"-l\")\n        {\n            listTests = true;\n        }\n        else\n        {\n            printErr() << \"Unknown parameter: \" << param << \"\\n\";\n            printHelp(argv[0]);\n            exit(1);\n        }\n    }\n    auto classNames = DrClassMap::getAllClassName();\n\n    if (listTests)\n    {\n        print() << \"Available Tests:\\n\";\n        for (const auto &name : classNames)\n        {\n            if (name.find(DROGON_TESTCASE_PREIX_STR_) == 0)\n            {\n                auto test =\n                    std::unique_ptr<DrObjectBase>(DrClassMap::newObject(name));\n                auto ptr = dynamic_cast<TestCase *>(test.get());\n                if (ptr == nullptr)\n                    continue;\n                print() << \"  \" << ptr->name() << \"\\n\";\n            }\n        }\n        exit(0);\n    }\n\n    std::vector<std::shared_ptr<TestCase>> testCases;\n    // NOTE: Registering a dummy case prevents the test-end signal to be\n    // emitted too early as there's always an case that hasn't finish\n    std::shared_ptr<Case> dummyCase = std::make_shared<Case>(\"__dummy_dummy_\");\n    for (const auto &name : classNames)\n    {\n        if (name.find(DROGON_TESTCASE_PREIX_STR_) == 0)\n        {\n            auto obj =\n                std::shared_ptr<DrObjectBase>(DrClassMap::newObject(name));\n            auto test = std::dynamic_pointer_cast<TestCase>(obj);\n            if (test == nullptr)\n            {\n                LOG_WARN << \"Class \" << name\n                         << \" seems to be a test case. But type information \"\n                            \"disagrees.\";\n                continue;\n            }\n            if (targetTest.empty() || test->name() == targetTest)\n            {\n                internal::numTestCases++;\n                test->doTest_(std::make_shared<Case>(test->name()));\n                testCases.emplace_back(std::move(test));\n            }\n        }\n    }\n    dummyCase = {};\n\n    if (targetTest != \"\" && internal::numTestCases == 0)\n    {\n        printErr() << \"Cannot find test named \" << targetTest << \"\\n\";\n        exit(1);\n    }\n\n    std::unique_lock<std::mutex> l(internal::mtxRegister);\n    if (internal::registeredTests.empty() == false)\n    {\n        auto fut = internal::allTestRan.get_future();\n        l.unlock();\n        fut.get();\n        assert(internal::registeredTests.empty());\n    }\n    testCases.clear();\n\n    printTestStats();\n\n    return internal::numCorrectAssertions != internal::numAssertions;\n}\n\nThreadSafeStream print()\n{\n    return ThreadSafeStream(std::cout);\n}\n\nThreadSafeStream printErr()\n{\n    return ThreadSafeStream(std::cerr);\n}\n\n}  // namespace test\n}  // namespace drogon\n"
  },
  {
    "path": "lib/src/impl_forwards.h",
    "content": "#pragma once\n\n#include <memory>\n#include <functional>\n\nnamespace drogon\n{\nclass HttpRequest;\nusing HttpRequestPtr = std::shared_ptr<HttpRequest>;\nclass HttpResponse;\nusing HttpResponsePtr = std::shared_ptr<HttpResponse>;\nclass Cookie;\nclass Session;\nusing SessionPtr = std::shared_ptr<Session>;\nclass UploadFile;\nclass WebSocketControllerBase;\nusing WebSocketControllerBasePtr = std::shared_ptr<WebSocketControllerBase>;\nclass HttpFilterBase;\nusing HttpFilterBasePtr = std::shared_ptr<HttpFilterBase>;\nclass HttpMiddlewareBase;\nusing HttpMiddlewareBasePtr = std::shared_ptr<HttpMiddlewareBase>;\nclass HttpSimpleControllerBase;\nusing HttpSimpleControllerBasePtr = std::shared_ptr<HttpSimpleControllerBase>;\nclass HttpRequestImpl;\nusing HttpRequestImplPtr = std::shared_ptr<HttpRequestImpl>;\nclass HttpResponseImpl;\nusing HttpResponseImplPtr = std::shared_ptr<HttpResponseImpl>;\nclass WebSocketConnectionImpl;\nusing WebSocketConnectionImplPtr = std::shared_ptr<WebSocketConnectionImpl>;\nclass HttpRequestParser;\nclass PluginsManager;\nclass ListenerManager;\nclass SharedLibManager;\nclass SessionManager;\nclass HttpServer;\n\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\nclass DbClientManager;\n}  // namespace orm\n\nnamespace nosql\n{\nclass RedisClient;\nusing RedisClientPtr = std::shared_ptr<RedisClient>;\nclass RedisClientManager;\n}  // namespace nosql\n}  // namespace drogon\n\nnamespace trantor\n{\nclass EventLoop;\nclass TcpConnection;\nusing TcpConnectionPtr = std::shared_ptr<TcpConnection>;\nclass Resolver;\nclass AsyncFileLogger;\n}  // namespace trantor\n\nnamespace drogon\n{\nusing HttpAsyncCallback =\n    std::function<void(const HttpRequestImplPtr &,\n                       std::function<void(const HttpResponsePtr &)> &&)>;\nusing WebSocketNewAsyncCallback =\n    std::function<void(const HttpRequestImplPtr &,\n                       std::function<void(const HttpResponsePtr &)> &&,\n                       const WebSocketConnectionImplPtr &)>;\n}  // namespace drogon\n"
  },
  {
    "path": "lib/tests/CMakeLists.txt",
    "content": "link_libraries(${PROJECT_NAME})\nif(WIN32)\n  link_libraries(iphlpapi)\nendif(WIN32)\nset(UNITTEST_SOURCES\n    unittests/main.cc\n    unittests/Base64Test.cc\n    unittests/UrlCodecTest.cc\n    unittests/GzipTest.cc\n    unittests/HttpViewDataTest.cc\n    unittests/CookieTest.cc\n    unittests/ClassNameTest.cc\n    unittests/HttpDateTest.cc\n    unittests/HttpHeaderTest.cc\n    unittests/MD5Test.cc\n    unittests/MsgBufferTest.cc\n    unittests/OStringStreamTest.cc\n    unittests/PubSubServiceUnittest.cc\n    unittests/Sha1Test.cc\n    unittests/FileTypeTest.cc\n    unittests/DrObjectTest.cc\n    unittests/HttpFullDateTest.cc\n    unittests/MainLoopTest.cc\n    unittests/CacheMapTest.cc\n    unittests/StringOpsTest.cc\n    unittests/ControllerCreationTest.cc\n    unittests/MultiPartParserTest.cc\n    unittests/SlashRemoverTest.cc\n    unittests/UtilitiesTest.cc\n    unittests/UuidUnittest.cc\n)\n\nif(DROGON_CXX_STANDARD GREATER_EQUAL 20 AND HAS_COROUTINE)\n  set(UNITTEST_SOURCES ${UNITTEST_SOURCES} unittests/CoroutineTest.cc)\nendif()\n\nif(Brotli_FOUND)\n  set(UNITTEST_SOURCES ${UNITTEST_SOURCES} unittests/BrotliTest.cc)\nendif()\n\nif(CMAKE_CXX_COMPILER_ID MATCHES \"MSVC\" AND BUILD_SHARED_LIBS)\n  set(UNITTEST_SOURCES ${UNITTEST_SOURCES} ../src/HttpUtils.cc)\nelse()\n  set(UNITTEST_SOURCES ${UNITTEST_SOURCES} ../src/HttpFileImpl.cc\n                       unittests/HttpFileTest.cc\n                       unittests/WebsocketResponseTest.cc)\nendif()\n\nadd_executable(unittest ${UNITTEST_SOURCES})\n\nif (BUILD_CTL)\n  set(INTEGRATION_TEST_CLIENT_SOURCES\n      integration_test/client/main.cc\n      integration_test/client/WebSocketTest.cc\n      integration_test/client/MultipleWsTest.cc\n      integration_test/client/HttpPipeliningTest.cc\n      integration_test/client/RequestStreamTest.cc)\n  add_executable(integration_test_client ${INTEGRATION_TEST_CLIENT_SOURCES})\n\n  set(INTEGRATION_TEST_SERVER_SOURCES\n      integration_test/server/CustomCtrl.cc\n      integration_test/server/CustomHeaderFilter.cc\n      integration_test/server/DoNothingPlugin.cc\n      integration_test/server/ForwardCtrl.cc\n      integration_test/server/JsonTestController.cc\n      integration_test/server/ListParaCtl.cc\n      integration_test/server/PipeliningTest.cc\n      integration_test/server/TestController.cc\n      integration_test/server/TestPlugin.cc\n      integration_test/server/TestViewCtl.cc\n      integration_test/server/WebSocketTest.cc\n      integration_test/server/api_Attachment.cc\n      integration_test/server/api_v1_ApiTest.cc\n      integration_test/server/TimeFilter.cc\n      integration_test/server/DigestAuthFilter.cc\n      integration_test/server/MethodTest.cc\n      integration_test/server/RangeTestController.cc\n      integration_test/server/BeginAdviceTest.cc\n      integration_test/server/MiddlewareTest.cc\n      integration_test/server/RequestStreamTestCtrl.cc\n      integration_test/server/main.cc)\n\n  if(DROGON_CXX_STANDARD GREATER_EQUAL 20 AND HAS_COROUTINE)\n    set(INTEGRATION_TEST_SERVER_SOURCES\n        ${INTEGRATION_TEST_SERVER_SOURCES}\n        integration_test/server/CoroFilter.cpp\n        integration_test/server/api_v1_CoroTest.cc)\n    set(CMAKE_CXX_STANDARD 20)\n    set(CMAKE_CXX_STANDARD_REQUIRED TRUE)\n  endif(DROGON_CXX_STANDARD GREATER_EQUAL 20 AND HAS_COROUTINE)\n\n  add_executable(integration_test_server ${INTEGRATION_TEST_SERVER_SOURCES})\n  drogon_create_views(integration_test_server\n                      ${CMAKE_CURRENT_SOURCE_DIR}/integration_test/server\n                      ${CMAKE_CURRENT_BINARY_DIR})\n  add_dependencies(integration_test_server drogon_ctl)\n  add_custom_command(\n    TARGET integration_test_server POST_BUILD\n    COMMAND ${CMAKE_COMMAND}\n            -E\n            copy_if_different\n            ${PROJECT_SOURCE_DIR}/config.example.json\n            ${PROJECT_SOURCE_DIR}/drogon.jpg\n            ${CMAKE_CURRENT_SOURCE_DIR}/integration_test/server/index.html\n            ${CMAKE_CURRENT_SOURCE_DIR}/integration_test/server/main.cc\n            ${CMAKE_CURRENT_SOURCE_DIR}/integration_test/server/test.md\n            ${CMAKE_CURRENT_SOURCE_DIR}/integration_test/server/index.html.gz\n            ${CMAKE_CURRENT_SOURCE_DIR}/integration_test/server/中文.txt\n            ${PROJECT_SOURCE_DIR}/trantor/trantor/tests/server.crt\n            ${PROJECT_SOURCE_DIR}/trantor/trantor/tests/server.key\n            $<TARGET_FILE_DIR:integration_test_server>)\n  add_custom_command(\n    TARGET integration_test_server POST_BUILD\n    COMMAND ${CMAKE_COMMAND}\n            -E\n            copy_directory\n            ${CMAKE_CURRENT_SOURCE_DIR}/integration_test/server/a-directory\n            $<TARGET_FILE_DIR:integration_test_server>/a-directory)\nendif(BUILD_CTL)\n\nset(COOKIE_SAME_SITE\n  main_CookieSameSite.cc\n  CookieSameSite.cc\n)\nadd_executable(cookie_same_site ${COOKIE_SAME_SITE})\n\nadd_executable(real_ip_resolver RealIpResolverTest.cc)\n\nset(tests unittest cookie_same_site real_ip_resolver)\nif (BUILD_CTL)\n  list(APPEND tests integration_test_server integration_test_client)\nendif(BUILD_CTL)\nset_property(TARGET ${tests} PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})\nset_property(TARGET ${tests} PROPERTY CXX_STANDARD_REQUIRED ON)\nset_property(TARGET ${tests} PROPERTY CXX_EXTENSIONS OFF)\n\nParseAndAddDrogonTests(unittest)\nParseAndAddDrogonTests(cookie_same_site)\nParseAndAddDrogonTests(real_ip_resolver)\n"
  },
  {
    "path": "lib/tests/CookieSameSite.cc",
    "content": "#include <drogon/Cookie.h>\n#include <drogon/drogon_test.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/HttpClient.h>\n#include <drogon/HttpRequest.h>\n#include <drogon/HttpResponse.h>\n#include <drogon/HttpTypes.h>\n\nusing namespace drogon;\n\nstruct CookieSameSiteSequence\n{\n    CookieSameSiteSequence()\n    {\n        i = valid_sameSite_values.begin();\n        sessionCookie.setKey(\"JSESSIONID\");\n    }\n\n    std::vector<std::string> valid_sameSite_values{\"Null\",\n                                                   \"Lax\",\n                                                   \"None\",\n                                                   \"Strict\"};\n\n    std::vector<std::string>::const_iterator i;\n    Cookie sessionCookie;\n};\n\nDROGON_TEST(CookieSameSite)\n{\n    auto client =\n        HttpClient::newHttpClient(\"https://127.0.0.1:8855\",\n                                  HttpAppFramework::instance().getLoop(),\n                                  false,\n                                  false);\n    auto req = HttpRequest::newHttpRequest();\n\n    CookieSameSiteSequence seq;\n\n    while (seq.i != seq.valid_sameSite_values.end())\n    {\n        std::promise<void> p1;\n        std::future<void> f1 = p1.get_future();\n        req->setPath(std::string(\"/CookieSameSiteController/set/\") + *seq.i);\n        if (seq.sessionCookie.getValue() != \"\")\n        {\n            // add session cookie\n            req->addCookie(seq.sessionCookie.key(), seq.sessionCookie.value());\n        }  // endif\n\n        client->sendRequest(\n            req,\n            [TEST_CTX, &seq, &p1](ReqResult res, const HttpResponsePtr &resp) {\n                REQUIRE(res == ReqResult::Ok);\n                REQUIRE(resp != nullptr);\n\n                CHECK(resp->getStatusCode() == HttpStatusCode::k200OK);\n                CHECK(resp->contentType() == CT_APPLICATION_JSON);\n\n                auto json = resp->getJsonObject();\n                auto cookie = resp->getCookie(\"JSESSIONID\");\n\n                LOG_INFO << \"Client: cookie-value == \" << cookie.value()\n                         << \", requested value == \" << (*seq.i)\n                         << \", new  value == \"\n                         << (*json)[\"new value\"].asString()\n                         << \", received value == \"\n                         << Cookie::convertSameSite2String(\n                                cookie.getSameSite());\n                seq.sessionCookie = resp->getCookie(\"JSESSIONID\");\n                CHECK(*seq.i ==\n                      Cookie::convertSameSite2String(cookie.getSameSite()));\n\n                p1.set_value();\n            });\n        f1.get();\n        seq.i++;\n    }\n}\n"
  },
  {
    "path": "lib/tests/RealIpResolverTest.cc",
    "content": "#define DROGON_TEST_MAIN\n#include <drogon/drogon_test.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/HttpClient.h>\n#include <drogon/HttpRequest.h>\n#include <drogon/HttpResponse.h>\n#include <drogon/plugins/RealIpResolver.h>\n#include <drogon/drogon.h>\n#include <drogon/HttpTypes.h>\n\nusing namespace drogon;\n\nDROGON_TEST(RealIpResolver)\n{\n    auto client =\n        HttpClient::newHttpClient(\"http://127.0.0.1:8017\",\n                                  HttpAppFramework::instance().getLoop());\n\n    auto newRequest = []() {\n        auto req = HttpRequest::newHttpRequest();\n        req->setPath(\"/RealIpController/my-ip\");\n        return req;\n    };\n\n    // 1. No headers\n    {\n        auto req = newRequest();\n        client->sendRequest(\n            req, [TEST_CTX](ReqResult res, const HttpResponsePtr &resp) {\n                REQUIRE(res == ReqResult::Ok);\n                CHECK(resp->getStatusCode() == HttpStatusCode::k200OK);\n                CHECK(resp->contentType() == drogon::CT_TEXT_PLAIN);\n                CHECK(resp->body() == \"127.0.0.1\");\n            });\n    }\n    // 2. header only contains real ip\n    {\n        auto req = newRequest();\n        req->addHeader(\"x-forwarded-for\", \"1.1.1.1\");\n        client->sendRequest(\n            req, [TEST_CTX](ReqResult res, const HttpResponsePtr &resp) {\n                REQUIRE(res == ReqResult::Ok);\n                CHECK(resp->getStatusCode() == HttpStatusCode::k200OK);\n                CHECK(resp->contentType() == drogon::CT_TEXT_PLAIN);\n                CHECK(resp->body() == \"1.1.1.1\");\n            });\n    }\n    // 3. Ip with port\n    {\n        auto req = newRequest();\n        req->addHeader(\"x-forwarded-for\", \"1.1.1.1:7777\");\n        client->sendRequest(\n            req, [TEST_CTX](ReqResult res, const HttpResponsePtr &resp) {\n                REQUIRE(res == ReqResult::Ok);\n                CHECK(resp->getStatusCode() == HttpStatusCode::k200OK);\n                CHECK(resp->contentType() == drogon::CT_TEXT_PLAIN);\n                CHECK(resp->body() == \"1.1.1.1\");\n            });\n    }\n    // 3. multiple ips\n    {\n        auto req = newRequest();\n        req->addHeader(\"x-forwarded-for\",\n                       \"2.2.2.2,1.1.1.1:7001,  172.16.0.100:7002,127.0.0.1\");\n        client->sendRequest(\n            req, [TEST_CTX](ReqResult res, const HttpResponsePtr &resp) {\n                REQUIRE(res == ReqResult::Ok);\n                CHECK(resp->getStatusCode() == HttpStatusCode::k200OK);\n                CHECK(resp->contentType() == drogon::CT_TEXT_PLAIN);\n                CHECK(resp->body() == \"1.1.1.1\");\n            });\n    }\n    // 4. Ignore error in header\n    {\n        auto req = newRequest();\n        req->addHeader(\"x-forwarded-for\",\n                       \"2.2.2.2,1.1.1.1:7001, wrong,9.9.9.9\");\n        client->sendRequest(\n            req, [TEST_CTX](ReqResult res, const HttpResponsePtr &resp) {\n                REQUIRE(res == ReqResult::Ok);\n                CHECK(resp->getStatusCode() == HttpStatusCode::k200OK);\n                CHECK(resp->contentType() == drogon::CT_TEXT_PLAIN);\n                CHECK(resp->body() == \"1.1.1.1\");\n            });\n    }\n};\n\nclass RealIpController : public drogon::HttpController<RealIpController>\n{\n  public:\n    METHOD_LIST_BEGIN\n    METHOD_ADD(RealIpController::getRealIp, \"/my-ip\", Get);\n    METHOD_LIST_END\n\n    void getRealIp(const HttpRequestPtr &req,\n                   std::function<void(const HttpResponsePtr &)> &&callback)\n    {\n        auto addr = req->attributes()->get<trantor::InetAddress>(\"real-ip\");\n        auto resp = HttpResponse::newHttpResponse();\n        resp->setContentTypeCode(drogon::CT_TEXT_PLAIN);\n        resp->setBody(addr.toIp());\n        callback(resp);\n    }\n};\n\n// -- main\nint main(int argc, char **argv)\n{\n    trantor::Logger::setLogLevel(trantor::Logger::kInfo);\n    std::promise<void> p1;\n    std::future<void> f1 = p1.get_future();\n\n    std::stringstream ss;\n    ss << R\"({\n    \"listeners\": [\n        {\n            \"address\": \"0.0.0.0\",\n            \"port\": 8017\n        }\n    ],\n    \"plugins\": [\n        {\n            \"name\": \"drogon::plugin::RealIpResolver\",\n            \"config\": {\n                \"trust_ips\": [\"127.0.0.1\", \"172.16.0.0/12\", \"9.9.9.9/32\"],\n                \"from_header\": \"x-forwarded-for\",\n                \"attribute_key\": \"real-ip\"\n            }\n        }\n    ]\n})\";\n    Json::Value config;\n    ss >> config;\n\n    std::thread thr([&]() {\n        app().loadConfigJson(config);\n        app().getLoop()->queueInLoop([&p1]() { p1.set_value(); });\n        app().run();\n    });\n\n    f1.get();\n    std::this_thread::sleep_for(std::chrono::milliseconds(200));\n    int testStatus = test::run(argc, argv);\n    app().getLoop()->queueInLoop([]() { app().quit(); });\n    thr.join();\n    return testStatus;\n}\n"
  },
  {
    "path": "lib/tests/integration_test/client/HttpPipeliningTest.cc",
    "content": "#include <drogon/HttpClient.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/drogon_test.h>\n#include <string>\n#include <iostream>\n#include <atomic>\nusing namespace drogon;\n\nstatic int counter = -1;\n\nDROGON_TEST(HttpPipeliningTest)\n{\n    auto client = HttpClient::newHttpClient(\"127.0.0.1\", 8848);\n    client->setPipeliningDepth(64);\n\n    auto request1 = HttpRequest::newHttpRequest();\n    request1->setPath(\"/pipe\");\n    request1->setMethod(Head);\n\n    client->sendRequest(\n        request1, [TEST_CTX](ReqResult r, const HttpResponsePtr &resp) {\n            REQUIRE(r == ReqResult::Ok);\n            auto counterHeader = resp->getHeader(\"counter\");\n            int c = atoi(counterHeader.data());\n            if (c <= counter)\n                FAIL(\"The response was received in the wrong order!\");\n            else\n                SUCCESS();\n            counter = c;\n            REQUIRE(resp->body().empty());\n        });\n\n    auto request2 = HttpRequest::newHttpRequest();\n    request2->setPath(\"/drogon.jpg\");\n    client->sendRequest(request2,\n                        [TEST_CTX](ReqResult r, const HttpResponsePtr &resp) {\n                            REQUIRE(r == ReqResult::Ok);\n                            REQUIRE(resp->getBody().length() == 44618UL);\n                        });\n\n    for (int i = 0; i < 19; ++i)\n    {\n        client->sendRequest(\n            request1, [TEST_CTX](ReqResult r, const HttpResponsePtr &resp) {\n                REQUIRE(r == ReqResult::Ok);\n                auto counterHeader = resp->getHeader(\"counter\");\n                int c = atoi(counterHeader.data());\n                if (c <= counter)\n                    FAIL(\"The response was received in the wrong order!\");\n                else\n                    SUCCESS();\n                counter = c;\n                REQUIRE(resp->body().empty());\n            });\n    }\n}\n\nDROGON_TEST(HttpPipeliningStrangeTest1)\n{\n    auto client = HttpClient::newHttpClient(\"127.0.0.1\", 8848);\n    client->setPipeliningDepth(64);\n    for (int i = 0; i < 4; ++i)\n    {\n        auto request = HttpRequest::newHttpRequest();\n        request->setPath(\"/pipe/strange-1\");\n        request->setBody(std::to_string(i));\n        client->sendRequest(request,\n                            [TEST_CTX, i](ReqResult r,\n                                          const HttpResponsePtr &resp) {\n                                REQUIRE(r == ReqResult::Ok);\n                                REQUIRE(resp->body() == std::to_string(i));\n                            });\n    }\n}\n\nDROGON_TEST(HttpPipeliningStrangeTest2)\n{\n    auto client = HttpClient::newHttpClient(\"127.0.0.1\", 8848);\n    client->setPipeliningDepth(64);\n    for (int i = 0; i < 6; ++i)\n    {\n        auto request = HttpRequest::newHttpRequest();\n        request->setPath(\"/pipe/strange-2\");\n        request->setBody(std::to_string(i));\n        client->sendRequest(request,\n                            [TEST_CTX, i](ReqResult r,\n                                          const HttpResponsePtr &resp) {\n                                REQUIRE(r == ReqResult::Ok);\n                                REQUIRE(resp->body() == std::to_string(i));\n                            });\n    }\n}\n"
  },
  {
    "path": "lib/tests/integration_test/client/MultipleWsTest.cc",
    "content": "#include <drogon/WebSocketClient.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/drogon_test.h>\n\n#include <iostream>\n\nusing namespace drogon;\nusing namespace std::chrono_literals;\n\nstruct DataPack\n{\n    WebSocketClientPtr wsPtr;\n    std::shared_ptr<drogon::test::CaseBase> TEST_CTX;\n};\n\nstatic const int kClientCount = 100;\n\nDROGON_TEST(MultipleWsTest)\n{\n    for (size_t i = 0; i < kClientCount; i++)\n    {\n        auto wsPtr = WebSocketClient::newWebSocketClient(\"127.0.0.1\", 8848);\n        auto pack = std::make_shared<DataPack *>(new DataPack{wsPtr, TEST_CTX});\n\n        wsPtr->setMessageHandler(\n            [pack, i](const std::string &message,\n                      const WebSocketClientPtr &wsPtr,\n                      const WebSocketMessageType &type) mutable {\n                if (pack == nullptr)\n                    return;\n                auto TEST_CTX = (*pack)->TEST_CTX;\n                CHECK((type == WebSocketMessageType::Text ||\n                       type == WebSocketMessageType::Pong));\n                if (type == WebSocketMessageType::Pong && TEST_CTX != nullptr)\n                {\n                    auto wsPtr = (*pack)->wsPtr;\n\n                    // Check if the correct connection got the result\n                    wsPtr->stop();\n                    CHECK(message == std::to_string(i));\n                    delete *pack;\n                    pack = nullptr;\n                }\n            });\n        auto req = HttpRequest::newHttpRequest();\n        req->setPath(\"/chat\");\n        wsPtr->connectToServer(\n            req,\n            [pack, i](ReqResult r,\n                      const HttpResponsePtr &resp,\n                      const WebSocketClientPtr &wsPtr) mutable {\n                auto TEST_CTX = (*pack)->TEST_CTX;\n                CHECK((*pack)->wsPtr == wsPtr);\n                CHECK(r == ReqResult::Ok);\n                if (r != ReqResult::Ok)\n                {\n                    wsPtr->stop();\n                    delete *pack;\n                    pack = nullptr;\n                }\n                REQUIRE(wsPtr != nullptr);\n                REQUIRE(resp != nullptr);\n\n                wsPtr->getConnection()->setPingMessage(std::to_string(i), 1.5s);\n                wsPtr->getConnection()->send(\"hello!\");\n                CHECK(wsPtr->getConnection()->connected());\n\n                TEST_CTX = {};\n            });\n    }\n}\n"
  },
  {
    "path": "lib/tests/integration_test/client/RequestStreamTest.cc",
    "content": "#include <drogon/HttpClient.h>\n#include <drogon/drogon_test.h>\n#include <trantor/net/TcpClient.h>\n#include <chrono>\n#include <string>\n#include <iostream>\n#include <fstream>\n\nusing namespace drogon;\n\ntemplate <typename T>\nvoid checkStreamRequest(T &&TEST_CTX,\n                        trantor::EventLoop *loop,\n                        const trantor::InetAddress &addr,\n                        const std::vector<std::string_view> &dataToSend,\n                        std::string_view expectedResp)\n{\n    auto tcpClient = std::make_shared<trantor::TcpClient>(loop, addr, \"test\");\n    std::promise<void> promise;\n\n    auto respString = std::make_shared<std::string>();\n    tcpClient->setMessageCallback(\n        [respString](const trantor::TcpConnectionPtr &conn,\n                     trantor::MsgBuffer *buf) {\n            respString->append(buf->read(buf->readableBytes()));\n        });\n    tcpClient->setConnectionCallback(\n        [TEST_CTX, &promise, respString, dataToSend, expectedResp](\n            const trantor::TcpConnectionPtr &conn) {\n            if (conn->disconnected())\n            {\n                LOG_INFO << \"Disconnected from server\";\n                CHECK(respString->substr(0, expectedResp.size()) ==\n                      expectedResp);\n                promise.set_value();\n                return;\n            }\n            LOG_INFO << \"Connected to server\";\n            CHECK(conn->connected());\n            for (auto &data : dataToSend)\n            {\n                conn->send(data.data(), data.size());\n                std::this_thread::sleep_for(std::chrono::milliseconds(100));\n            }\n            conn->shutdown();\n        });\n    tcpClient->connect();\n    promise.get_future().wait();\n}\n\nDROGON_TEST(RequestStreamTest)\n{\n    const std::string ip = \"127.0.0.1\";\n    const uint16_t port = 8848;\n    auto client = HttpClient::newHttpClient(ip, port);\n    HttpRequestPtr req;\n\n    bool enabled = false;\n\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/stream_status\");\n    {\n        auto [res, resp] = client->sendRequest(req);\n        REQUIRE(res == ReqResult::Ok);\n        REQUIRE(resp->statusCode() == k200OK);\n        if (resp->body() == \"enabled\")\n        {\n            enabled = true;\n        }\n        else\n        {\n            LOG_INFO << \"Server does not enable request stream.\";\n        }\n    }\n\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/stream_chunk\");\n    req->setMethod(Post);\n    req->setBody(\"1234567890\");\n    client->sendRequest(req,\n                        [TEST_CTX, enabled](ReqResult r,\n                                            const HttpResponsePtr &resp) {\n                            REQUIRE(r == ReqResult::Ok);\n                            if (enabled)\n                            {\n                                CHECK(resp->statusCode() == k200OK);\n                                CHECK(resp->body() == \"1234567890\");\n                            }\n                            else\n                            {\n                                CHECK(resp->statusCode() == k400BadRequest);\n                                CHECK(resp->body() == \"no stream\");\n                            }\n                        });\n\n    if (!enabled)\n    {\n        return;\n    }\n\n    LOG_INFO << \"Test request stream\";\n\n    std::string filePath = \"./中文.txt\";\n    std::ifstream file(filePath);\n    std::stringstream content;\n    REQUIRE(file.is_open());\n    content << file.rdbuf();\n\n    req = HttpRequest::newFileUploadRequest({UploadFile{filePath}});\n    req->setPath(\"/stream_upload_echo\");\n    req->setMethod(Post);\n    client->sendRequest(req,\n                        [TEST_CTX,\n                         content = content.str()](ReqResult r,\n                                                  const HttpResponsePtr &resp) {\n                            CHECK(r == ReqResult::Ok);\n                            CHECK(resp->statusCode() == k200OK);\n                            CHECK(resp->body() == content);\n                        });\n\n    checkStreamRequest(TEST_CTX,\n                       client->getLoop(),\n                       trantor::InetAddress{ip, port},\n                       // Good request\n                       {\"POST /stream_chunk HTTP/1.1\\r\\n\"\n                        \"Transfer-Encoding: chunked\\r\\n\\r\\n\",\n                        \"1\\r\\nz\\r\\n\",\n                        \"2\\r\\nzz\\r\\n0\\r\\n\\r\\n\"},\n                       // Good response\n                       \"HTTP/1.1 200 OK\\r\\n\");\n\n    checkStreamRequest(TEST_CTX,\n                       client->getLoop(),\n                       trantor::InetAddress{ip, port},\n                       // Bad request\n                       {\"POST /stream_chunk HTTP/1.1\\r\\n\"\n                        \"Transfer-Encoding: chunked\\r\\n\\r\\n\",\n                        \"1\\r\\nz\\r\\n\",\n                        \"1\\r\\nzz\\r\\n\",\n                        \"0\\r\\n\\r\\n\"},\n                       // Bad response\n                       \"HTTP/1.1 400 Bad Request\\r\\n\");\n}\n"
  },
  {
    "path": "lib/tests/integration_test/client/WebSocketTest.cc",
    "content": "#include <drogon/WebSocketClient.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/drogon_test.h>\n\n#include <iostream>\n\nusing namespace drogon;\nusing namespace std::chrono_literals;\n\nstruct DataPack\n{\n    WebSocketClientPtr wsPtr;\n    std::shared_ptr<drogon::test::CaseBase> TEST_CTX;\n};\n\nstatic WebSocketClientPtr wsPtr_;\n\nDROGON_TEST(WebSocketTest)\n{\n    wsPtr_ = WebSocketClient::newWebSocketClient(\"127.0.0.1\", 8848);\n    auto pack = std::make_shared<DataPack *>(new DataPack{wsPtr_, TEST_CTX});\n    auto req = HttpRequest::newHttpRequest();\n    req->setPath(\"/chat\");\n    wsPtr_->setMessageHandler([pack](const std::string &message,\n                                     const WebSocketClientPtr &wsPtr,\n                                     const WebSocketMessageType &type) mutable {\n        if (pack == nullptr)\n            return;\n        auto TEST_CTX = (*pack)->TEST_CTX;\n        if (type == WebSocketMessageType::Pong)\n        {\n            auto wsPtr = (*pack)->wsPtr;\n\n            wsPtr_->stop();\n            CHECK(message.empty());\n            delete *pack;\n            pack = nullptr;\n        }\n        else\n        {\n            CHECK(type == WebSocketMessageType::Text);\n        }\n    });\n\n    wsPtr_->connectToServer(req,\n                            [pack](ReqResult r,\n                                   const HttpResponsePtr &resp,\n                                   const WebSocketClientPtr &wsPtr) mutable {\n                                auto TEST_CTX = (*pack)->TEST_CTX;\n                                CHECK((*pack)->wsPtr == wsPtr);\n                                if (r != ReqResult::Ok)\n                                {\n                                    wsPtr_->stop();\n                                    wsPtr_.reset();\n                                    delete *pack;\n                                    pack = nullptr;\n                                }\n                                REQUIRE(r == ReqResult::Ok);\n                                REQUIRE(wsPtr != nullptr);\n                                REQUIRE(resp != nullptr);\n                                wsPtr->getConnection()->setPingMessage(\"\", 1s);\n                                wsPtr->getConnection()->send(\"hello!\");\n                                CHECK(wsPtr->getConnection()->connected());\n                                // Drop the testing context as WS controllers\n                                // stores the lambda and never release it.\n                                // Causing a dead lock later.\n                                TEST_CTX = {};\n                                pack.reset();\n                            });\n}\n"
  },
  {
    "path": "lib/tests/integration_test/client/main.cc",
    "content": "/**\n *\n *  @file test.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n// Make a http client to test the example server app;\n\n#define DROGON_TEST_MAIN\n#include <drogon/drogon.h>\n#include <trantor/net/EventLoopThread.h>\n#include <trantor/net/TcpClient.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/drogon_test.h>\n\n#include <mutex>\n#include <algorithm>\n#include <atomic>\n#include <chrono>\n\n#ifndef _WIN32\n#include <unistd.h>\n#endif\n\n#include <sys/stat.h>\n\n#define JPG_LEN 44618UL\nsize_t indexLen;\nsize_t indexImplicitLen;\n\nusing namespace drogon;\n\nCookie sessionID;\n\nvoid doTest(const HttpClientPtr &client, std::shared_ptr<test::Case> TEST_CTX)\n{\n    /// Get cookie\n    if (!sessionID)\n    {\n        auto req = HttpRequest::newHttpRequest();\n        req->setMethod(drogon::Get);\n        req->setPath(\"/\");\n        std::promise<int> waitCookie;\n        bool haveCert = false;\n        auto f = waitCookie.get_future();\n        client->sendRequest(req,\n                            [client, &waitCookie, &haveCert, TEST_CTX](\n                                ReqResult result, const HttpResponsePtr &resp) {\n                                REQUIRE(result == ReqResult::Ok);\n\n                                auto &id = resp->getCookie(\"JSESSIONID\");\n                                REQUIRE(id);\n\n                                haveCert = resp->peerCertificate() != nullptr;\n\n                                sessionID = id;\n                                client->addCookie(id);\n                                waitCookie.set_value(1);\n                            });\n        f.get();\n        CHECK(haveCert == client->secure());\n    }\n    else\n        client->addCookie(sessionID);\n\n    /// Test begin advice\n    auto req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/test_begin_advice\");\n    client->sendRequest(req,\n                        [req, client, TEST_CTX](ReqResult result,\n                                                const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody() == \"DrogonReady\");\n                        });\n\n    /// Test session\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/slow\");\n    client->sendRequest(\n        req,\n        [req, client, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n\n            auto req1 = HttpRequest::newHttpRequest();\n            req1->setMethod(drogon::Get);\n            req1->setPath(\"/slow\");\n            client->sendRequest(\n                req1,\n                [req1, TEST_CTX](ReqResult result,\n                                 const HttpResponsePtr &resp1) {\n                    REQUIRE(result == ReqResult::Ok);\n\n                    auto &json = resp1->jsonObject();\n                    REQUIRE(json != nullptr);\n                    REQUIRE(json->get(\"message\", std::string(\"\")).asString() ==\n                            \"Access interval should be \"\n                            \"at least 10 \"\n                            \"seconds\");\n                });\n        });\n    /// Test gzip\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->addHeader(\"accept-encoding\", \"gzip\");\n    req->setPath(\"/api/v1/apitest/get/111\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody().length() == 4994UL);\n                        });\n/// Test brotli\n#ifdef USE_BROTLI\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->addHeader(\"accept-encoding\", \"br\");\n    req->setPath(\"/api/v1/apitest/get/111\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody().length() == 4994UL);\n                        });\n#endif\n    /// Post json\n    Json::Value json;\n    json[\"request\"] = \"json\";\n    req = HttpRequest::newCustomHttpRequest(json);\n    req->setMethod(drogon::Post);\n    req->setPath(\"/api/v1/apitest/json\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n\n                            std::shared_ptr<Json::Value> ret = *resp;\n                            REQUIRE(ret != nullptr);\n                            CHECK((*ret)[\"result\"].asString() == \"ok\");\n                        });\n    // Post json again\n    req = HttpRequest::newHttpJsonRequest(json);\n    assert(req->jsonObject());\n    req->setMethod(drogon::Post);\n    req->setPath(\"/api/v1/apitest/json\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n\n                            std::shared_ptr<Json::Value> ret = *resp;\n                            REQUIRE(ret != nullptr);\n                            CHECK((*ret)[\"result\"].asString() == \"ok\");\n                        });\n\n    // Post json again\n    req = HttpRequest::newHttpJsonRequest(json);\n    req->setMethod(drogon::Post);\n    req->setPath(\"/api/v1/apitest/json\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n\n                            std::shared_ptr<Json::Value> ret = *resp;\n                            REQUIRE(ret != nullptr);\n                            CHECK((*ret)[\"result\"].asString() == \"ok\");\n                        });\n\n    // Test 404\n    req = HttpRequest::newHttpJsonRequest(json);\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/apitest/notFoundRouting\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() == k404NotFound);\n                        });\n    /// 1 Get /\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody() == \"<p>Hello, world!</p>\");\n                            // LOG_DEBUG << resp->getBody();\n                        });\n    /// 3. Post to /tpost to test Http Method constraint\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/tpost\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            CHECK(resp->getStatusCode() == k405MethodNotAllowed);\n        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Post);\n    req->setPath(\"/tpost\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody() == \"<p>Hello, world!</p>\");\n                        });\n\n    /// 4. Http OPTIONS Method\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Options);\n    req->setPath(\"/tpost\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->statusCode() == k200OK);\n                            auto allow = resp->getHeader(\"allow\");\n                            CHECK(allow.find(\"POST\") != std::string::npos);\n                        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Options);\n    req->setPath(\"/api/v1/apitest\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->statusCode() == k200OK);\n                            auto allow = resp->getHeader(\"allow\");\n                            CHECK(allow == \"OPTIONS,GET,HEAD,POST\");\n                        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Options);\n    req->setPath(\"/slow\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->statusCode() == k403Forbidden);\n                        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Options);\n    req->setPath(\"/*\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            CHECK(resp->statusCode() == k200OK);\n            auto allow = resp->getHeader(\"allow\");\n            CHECK(allow == \"GET,HEAD,POST,PUT,DELETE,OPTIONS,PATCH\");\n        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Options);\n    req->setPath(\"/api/v1/apitest/static\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->statusCode() == k200OK);\n                            auto allow = resp->getHeader(\"allow\");\n                            CHECK(allow == \"OPTIONS,GET,HEAD\");\n                        });\n\n    /// 4. Test HttpController\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Post);\n    req->setPath(\"/api/v1/apitest\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody() == \"ROOT Post!!!\");\n                        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/apitest\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody() == \"ROOT Get!!!\");\n                        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/apitest/get/abc/123\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            auto body = resp->getBody();\n            CHECK(body.find(\"<td>p1</td>\\n        <td>123</td>\") !=\n                  std::string::npos);\n            CHECK(body.find(\"<td>p2</td>\\n        <td>abc</td>\") !=\n                  std::string::npos);\n        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/apitest/3.14/List\");\n    req->setParameter(\"P2\", \"1234\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            auto body = resp->getBody();\n            CHECK(body.find(\"<td>p1</td>\\n        <td>3.140000</td>\") !=\n                  std::string::npos);\n            CHECK(body.find(\"<td>p2</td>\\n        <td>1234</td>\") !=\n                  std::string::npos);\n        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/reg/123/rest/of/the/path\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            REQUIRE(resp->getJsonObject() != nullptr);\n\n            auto &json = resp->getJsonObject();\n            CHECK(json->isMember(\"p1\"));\n            CHECK(json->get(\"p1\", 0).asInt() == 123);\n            CHECK(json->isMember(\"p2\"));\n            CHECK(json->get(\"p2\", \"\").asString() == \"rest/of/the/path\");\n        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/apitest/static\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody() == \"staticApi,hello!!\");\n                        });\n\n    // auto loop = app().loop();\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Post);\n    req->setPath(\"/api/v1/apitest/static\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody() == \"staticApi,hello!!\");\n                        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/apitest/get/111\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody().length() == 4994UL);\n                        });\n\n    /// Test method routing, see MethodTest.h\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Post);\n    req->setPath(\"/api/method/regex/drogon/test?test=drogon\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            auto jsonPtr = resp->getJsonObject();\n                            CHECK(jsonPtr);\n                            CHECK(jsonPtr->asString() == \"POST\");\n                        });\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/method/regex/drogon/test\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            auto jsonPtr = resp->getJsonObject();\n                            CHECK(jsonPtr);\n                            CHECK(jsonPtr->asString() == \"GET\");\n                        });\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Post);\n    req->setPath(\"/api/method/drogon/test?test=drogon\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            auto jsonPtr = resp->getJsonObject();\n                            CHECK(jsonPtr);\n                            CHECK(jsonPtr->asString() == \"POST\");\n                        });\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/method/drogon/test\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            auto jsonPtr = resp->getJsonObject();\n                            CHECK(jsonPtr);\n                            CHECK(jsonPtr->asString() == \"GET\");\n                        });\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Post);\n    req->setPath(\"/api/method/test?test=drogon\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            auto jsonPtr = resp->getJsonObject();\n                            CHECK(jsonPtr);\n                            CHECK(jsonPtr->asString() == \"POST\");\n                        });\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/method/test\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            auto jsonPtr = resp->getJsonObject();\n                            CHECK(jsonPtr);\n                            CHECK(jsonPtr->asString() == \"GET\");\n                        });\n    /// Test static function\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/handle11/11/2 2/?p3=3 x\");\n    req->setParameter(\"p4\", \"44\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            auto body = resp->getBody();\n            CHECK(body.find(\"<td>int p1</td>\\n        <td>11</td>\") !=\n                  std::string::npos);\n            CHECK(body.find(\"<td>int p4</td>\\n        <td>44</td>\") !=\n                  std::string::npos);\n            CHECK(body.find(\"<td>string p2</td>\\n        <td>2 2</td>\") !=\n                  std::string::npos);\n            CHECK(body.find(\"<td>string p3</td>\\n        <td>3 x</td>\") !=\n                  std::string::npos);\n        });\n    /// Test Incomplete URL\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/handle11/11/2 2/\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            auto body = resp->getBody();\n            CHECK(body.find(\"<td>int p1</td>\\n        <td>11</td>\") !=\n                  std::string::npos);\n            CHECK(body.find(\"<td>int p4</td>\\n        <td>0</td>\") !=\n                  std::string::npos);\n            CHECK(body.find(\"<td>string p2</td>\\n        <td>2 2</td>\") !=\n                  std::string::npos);\n            CHECK(body.find(\"<td>string p3</td>\\n        <td></td>\") !=\n                  std::string::npos);\n        });\n    /// Test lambda\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/handle2/111/222\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            auto body = resp->getBody();\n            CHECK(body.find(\"<td>a</td>\\n        <td>111</td>\") !=\n                  std::string::npos);\n            CHECK(body.find(\"<td>b</td>\\n        <td>222.000000</td>\") !=\n                  std::string::npos);\n        });\n\n    /// Test std::bind and std::function\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/handle4/444/333/111\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            auto body = resp->getBody();\n            CHECK(body.find(\"<td>int p1</td>\\n        <td>111</td>\") !=\n                  std::string::npos);\n            CHECK(body.find(\"<td>int p4</td>\\n        <td>444</td>\") !=\n                  std::string::npos);\n            CHECK(body.find(\"<td>string p2</td>\\n        <td></td>\") !=\n                  std::string::npos);\n            CHECK(body.find(\"<td>string p3</td>\\n        <td>333</td>\") !=\n                  std::string::npos);\n        });\n    /// Test gzip_static\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/index.html\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            CHECK(resp->getBody().length() == indexLen);\n            CHECK(resp->contentType() == CT_TEXT_HTML);\n            CHECK(resp->contentTypeString().find(\"text/html\") == 0);\n        });\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/index.html\");\n    req->addHeader(\"accept-encoding\", \"gzip\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->contentType() == CT_TEXT_HTML);\n                            CHECK(resp->getBody().length() == indexLen);\n                        });\n    /// Test serving file with non-ASCII files\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/中文.txt\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            // Only tests for serving a file, not the content\n                            // since no good way to read it on Windows without\n                            // using the wild-char API\n                        });\n    /// Test custom content types\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/test.md\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->contentType() == CT_CUSTOM);\n                            CHECK(resp->contentTypeString() == \"text/markdown\");\n                        });\n    /// Unknown files should be application/octet-stream\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/main.cc\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            REQUIRE(resp->contentType() == CT_APPLICATION_OCTET_STREAM);\n        });\n    // Test 405\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Post);\n    req->setPath(\"/drogon.jpg\");\n    client->sendRequest(req,\n                        [req, client, TEST_CTX](ReqResult result,\n                                                const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() ==\n                                  k405MethodNotAllowed);\n                        });\n    /// Test file download\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/drogon.jpg\");\n    client->sendRequest(\n        req,\n        [req, client, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            REQUIRE(resp->getBody().length() == JPG_LEN);\n            auto &lastModified = resp->getHeader(\"last-modified\");\n            // LOG_DEBUG << lastModified;\n            // Test 'Not Modified'\n            auto req = HttpRequest::newHttpRequest();\n            req->setMethod(drogon::Get);\n            req->setPath(\"/drogon.jpg\");\n            req->addHeader(\"If-Modified-Since\", lastModified);\n            client->sendRequest(req,\n                                [req, TEST_CTX](ReqResult result,\n                                                const HttpResponsePtr &resp) {\n                                    REQUIRE(result == ReqResult::Ok);\n                                    REQUIRE(resp->statusCode() ==\n                                            k304NotModified);\n                                    // pro.set_value(1);\n                                });\n        });\n\n    /// Test file download, It is forbidden to download files from the\n    /// parent folder\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/../../drogon.jpg\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->statusCode() == k403Forbidden);\n                        });\n    /// Test controllers created and initialized by users\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/customctrl/antao\");\n    req->addHeader(\"custom_header\", \"yes\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody() == \"<P>Hi, antao</P>\");\n                        });\n    /// Test controllers created and initialized by users\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/absolute/123\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->statusCode() == k200OK);\n                        });\n    /// Test synchronous advice\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/plaintext\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody() == \"Hello, World!\");\n                        });\n    /// Test form post\n    req = HttpRequest::newHttpFormPostRequest();\n    req->setPath(\"/api/v1/apitest/form\");\n    req->setParameter(\"k1\", \"1\");\n    req->setParameter(\"k2\", \"安\");\n    req->setParameter(\"k3\", \"test@example.com\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            auto ret = resp->getJsonObject();\n                            REQUIRE(ret != nullptr);\n                            CHECK((*ret)[\"result\"].asString() == \"ok\");\n                        });\n\n    /// Test attributes\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/apitest/attrs\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            auto ret = resp->getJsonObject();\n                            REQUIRE(ret != nullptr);\n                            CHECK((*ret)[\"result\"].asString() == \"ok\");\n                        });\n\n    /// Test attachment download\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/attachment/download\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody().length() == JPG_LEN);\n                        });\n    // Test implicit pages\n    auto body = std::make_shared<std::string>();\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/a-directory\");\n    client->sendRequest(req,\n                        [req, TEST_CTX, body](ReqResult result,\n                                              const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody().length() == indexImplicitLen);\n                            *body = std::string(resp->getBody().data(),\n                                                resp->getBody().length());\n                        });\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/a-directory/page.html\");\n    client->sendRequest(req,\n                        [req, TEST_CTX, body](ReqResult result,\n                                              const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody().length() == indexImplicitLen);\n                            CHECK(std::equal(body->begin(),\n                                             body->end(),\n                                             resp->getBody().begin()));\n                        });\n    // Test file upload\n    UploadFile file1(\"./中文.txt\");\n    UploadFile file2(\"./drogon.jpg\", \"drogon1.jpg\");\n    UploadFile file3(\"./config.example.json\", \"config.json\", \"file3\");\n    req = HttpRequest::newFileUploadRequest({file1, file2, file3});\n    req->setPath(\"/api/attachment/upload\");\n    req->setParameter(\"P1\", \"upload\");\n    req->setParameter(\"P2\", \"test\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            auto json = resp->getJsonObject();\n                            REQUIRE(json != nullptr);\n                            CHECK((*json)[\"result\"].asString() == \"ok\");\n                            CHECK((*json)[\"P1\"] == \"upload\");\n                            CHECK((*json)[\"P2\"] == \"test\");\n                        });\n\n    // Test file upload, file type and extension interface.\n    UploadFile image(\"./drogon.jpg\");\n    req = HttpRequest::newFileUploadRequest({image});\n    req->setPath(\"/api/attachment/uploadImage\");\n    req->setParameter(\"P1\", \"upload\");\n    req->setParameter(\"P2\", \"test\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            auto json = resp->getJsonObject();\n                            REQUIRE(json != nullptr);\n                            CHECK((*json)[\"P1\"] == \"upload\");\n                            CHECK((*json)[\"P2\"] == \"test\");\n                        });\n\n    // Test newFileResponse\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/RangeTestController/\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            CHECK(resp->getStatusCode() == k200OK);\n            CHECK(resp->getBody().size() == 1'000'000);\n            CHECK(resp->getHeader(\"Content-Length\") == \"1000000\");\n        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/RangeTestController/999980/0\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() == k206PartialContent);\n                            CHECK(resp->getBody() == \"01234567890123456789\");\n                        });\n\n    // Test > 200k\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/RangeTestController/1/500000\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            CHECK(resp->getBody().size() == 500'000);\n            CHECK(resp->getHeader(\"Content-Length\") == \"500000\");\n            CHECK(resp->getHeader(\"Content-Range\") == \"bytes 1-500000/1000000\");\n        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/RangeTestController/10/20\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            LOG_DEBUG << \"result=\" << (int)result;\n            REQUIRE(result == ReqResult::Ok);\n            CHECK(resp->getStatusCode() == k206PartialContent);\n            CHECK(resp->getBody() == \"01234567890123456789\");\n            CHECK(resp->getHeader(\"Content-Length\") == \"20\");\n            CHECK(resp->getHeader(\"Content-Range\") == \"bytes 10-29/1000000\");\n        });\n\n    // Test invalid range\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/RangeTestController/0/2000000\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            CHECK(resp->getHeader(\"Content-Range\") == \"bytes */1000000\");\n            CHECK(resp->getStatusCode() == k416RequestedRangeNotSatisfiable);\n        });\n\n    //\n    // Test StaticFileRouter with range header\n    //\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/range-test.txt\");\n    req->setMethod(drogon::Head);\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            CHECK(resp->getStatusCode() == k200OK);\n            CHECK(resp->getHeader(\"content-length\") == \"1000000\");\n            CHECK(resp->getHeader(\"accept-range\") == \"bytes\");\n        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/range-test.txt\");\n    req->addHeader(\"range\", \"bytes=0-19\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() == k206PartialContent);\n                            CHECK(resp->getBody() == \"01234567890123456789\");\n                        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/range-test.txt\");\n    req->addHeader(\"range\", \"bytes=-20\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() == k206PartialContent);\n                            CHECK(resp->getBody() == \"01234567890123456789\");\n                        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/range-test.txt\");\n    req->addHeader(\"range\", \"bytes=999980-\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() == k206PartialContent);\n                            CHECK(resp->getBody() == \"01234567890123456789\");\n                        });\n\n    // Using .. to access a upper directory should be permitted as long as\n    // it never leaves the document root\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/a-directory/../index.html\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody().length() == indexLen);\n                        });\n\n    // . (current directory) shall also be allowed\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/a-directory/./page.html\");\n    client->sendRequest(req,\n                        [req, TEST_CTX, body](ReqResult result,\n                                              const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getBody().length() == indexImplicitLen);\n                            CHECK(std::equal(body->begin(),\n                                             body->end(),\n                                             resp->getBody().begin()));\n                        });\n\n    // Test exception handling\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/this_will_fail\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            CHECK(resp->getStatusCode() == k500InternalServerError);\n        });\n\n    // The result of this API is cached for (almost) forever. And the endpoint\n    // increments a internal counter on each invoke. This tests if the respond\n    // is taken from the cache after the first invoke.\n    // Try poking the cache test endpoint 3 times. They should all respond 0\n    // since the first respond is cached by the server.\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/ApiTest/cacheTest\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() == k200OK);\n                            CHECK(resp->body() == \"0\");\n                        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/ApiTest/cacheTest\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() == k200OK);\n                            CHECK(resp->body() == \"0\");\n                        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/ApiTest/cacheTest\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() == k200OK);\n                            CHECK(resp->body() == \"0\");\n                        });\n\n    // This API caches it's result on the third (counting from 1) calls. Thus\n    // we expect to always see 2 upon the third call. And all previous calls\n    // should be less than or equal to 2, as another test is also poking the API\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/ApiTest/cacheTest2\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            CHECK(resp->getStatusCode() == k200OK);\n            int n = 100;\n            CHECK_NOTHROW(n = std::stoi(std::string(resp->body())));\n            CHECK(n <= 2);\n        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/ApiTest/cacheTest2\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            CHECK(resp->getStatusCode() == k200OK);\n            int n = 100;\n            CHECK_NOTHROW(n = std::stoi(std::string(resp->body())));\n            CHECK(n <= 2);\n        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/api/v1/ApiTest/cacheTest2\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() == k200OK);\n                            CHECK(resp->body() == \"2\");\n                        });\n\n    // Same as cacheTest2. But the server has to handle this API through regex.\n    // it is intentionally made that the final part of the path can't contain\n    // a \"z\" character\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/cacheTestRegex/foobar\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            CHECK(resp->getStatusCode() == k200OK);\n            int n = 100;\n            CHECK_NOTHROW(n = std::stoi(std::string(resp->body())));\n            CHECK(n <= 2);\n        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/cacheTestRegex/deadbeef\");\n    client->sendRequest(\n        req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            CHECK(resp->getStatusCode() == k200OK);\n            int n = 100;\n            CHECK_NOTHROW(n = std::stoi(std::string(resp->body())));\n            CHECK(n <= 2);\n        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/cacheTestRegex/leet\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() == k200OK);\n                            CHECK(resp->body() == \"2\");\n                        });\n    req = HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Get);\n    req->setPath(\"/cacheTestRegex/zebra\");\n    client->sendRequest(req,\n                        [req, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() == k404NotFound);\n                        });\n\n    // Post compressed data\n    req = HttpRequest::newHttpRequest();\n    std::string deadbeef = \"deadbeef\";\n    req->setPath(\"/api/v1/ApiTest/echoBody\");\n    req->addHeader(\"Content-Encoding\", \"gzip\");\n    req->setMethod(drogon::Post);\n    req->setBody(utils::gzipCompress(deadbeef.c_str(), deadbeef.size()));\n    client->sendRequest(req,\n                        [deadbeef, TEST_CTX](ReqResult result,\n                                             const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() == k200OK);\n                            CHECK(resp->body() == deadbeef);\n                        });\n\n    std::string largeString(128 * 1024, 'a');  // 128KB of 'a'\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/api/v1/ApiTest/echoBody\");\n    req->addHeader(\"Content-Encoding\", \"gzip\");\n    req->setMethod(drogon::Post);\n    req->setBody(utils::gzipCompress(largeString.c_str(), largeString.size()));\n    client->sendRequest(req,\n                        [largeString, TEST_CTX](ReqResult result,\n                                                const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() == k200OK);\n                            CHECK(resp->body() == largeString);\n                        });\n\n#ifdef USE_BROTLI\n    // Post compressed data\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/api/v1/ApiTest/echoBody\");\n    req->addHeader(\"Content-Encoding\", \"br\");\n    req->setMethod(drogon::Post);\n    req->setBody(utils::brotliCompress(deadbeef.c_str(), deadbeef.size()));\n    client->sendRequest(req,\n                        [deadbeef, TEST_CTX](ReqResult result,\n                                             const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() == k200OK);\n                            CHECK(resp->body() == deadbeef);\n                        });\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/api/v1/ApiTest/echoBody\");\n    req->addHeader(\"Content-Encoding\", \"br\");\n    req->setMethod(drogon::Post);\n    req->setBody(\n        utils::brotliCompress(largeString.c_str(), largeString.size()));\n    client->sendRequest(req,\n                        [largeString, TEST_CTX](ReqResult result,\n                                                const HttpResponsePtr &resp) {\n                            REQUIRE(result == ReqResult::Ok);\n                            CHECK(resp->getStatusCode() == k200OK);\n                            CHECK(resp->body() == largeString);\n                        });\n#endif\n\n    // Test middleware\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/test-middleware\");\n    client->sendRequest(req,\n                        [TEST_CTX, req](ReqResult r,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(r == ReqResult::Ok);\n                            CHECK(resp->body() == \"1234test4321\");\n                        });\n\n    req = HttpRequest::newHttpRequest();\n    req->setPath(\"/test-middleware-block\");\n    client->sendRequest(req,\n                        [TEST_CTX, req](ReqResult r,\n                                        const HttpResponsePtr &resp) {\n                            REQUIRE(r == ReqResult::Ok);\n                            CHECK(resp->body() == \"12block21\");\n                        });\n\n#if defined(__cpp_impl_coroutine)\n    async_run([client, TEST_CTX]() -> Task<> {\n        // Test coroutine requests\n        try\n        {\n            auto req = HttpRequest::newHttpRequest();\n            req->setPath(\"/api/v1/corotest/get\");\n            auto resp = co_await client->sendRequestCoro(req);\n            CHECK(resp->getBody() == \"DEADBEEF\");\n        }\n        catch (const std::exception &e)\n        {\n            FAIL(\"Unexpected exception, what()\" + std::string(e.what()));\n        }\n\n        // Test coroutine request with co_return\n        try\n        {\n            auto req = HttpRequest::newHttpRequest();\n            req->setPath(\"/api/v1/corotest/get2\");\n            auto resp = co_await client->sendRequestCoro(req);\n            CHECK(resp->getBody() == \"BADDBEEF\");\n        }\n        catch (const std::exception &e)\n        {\n            FAIL(\"Unexpected exception, what(): \" + std::string(e.what()));\n        }\n\n        // Test Coroutine exception\n        try\n        {\n            auto req = HttpRequest::newHttpRequest();\n            req->setPath(\"/api/v1/corotest/this_will_fail\");\n            auto resp = co_await client->sendRequestCoro(req);\n            CHECK(resp->getStatusCode() != k200OK);\n        }\n        catch (const std::exception &e)\n        {\n            FAIL(\"Unexpected exception, what(): \" + std::string(e.what()));\n        }\n\n        // Test Coroutine exception with co_return\n        try\n        {\n            auto req = HttpRequest::newHttpRequest();\n            req->setPath(\"/api/v1/corotest/this_will_fail2\");\n            auto resp = co_await client->sendRequestCoro(req);\n            CHECK(resp->getStatusCode() != k200OK);\n        }\n        catch (const std::exception &e)\n        {\n            FAIL(\"Unexpected exception, what(): \" + std::string(e.what()));\n        }\n\n        // Test coroutine filter\n        try\n        {\n            auto req = HttpRequest::newHttpRequest();\n            auto start = std::chrono::system_clock::now();\n            req->setPath(\"/api/v1/corotest/delay?secs=2\");\n            auto resp = co_await client->sendRequestCoro(req);\n            CHECK(resp->getStatusCode() == k200OK);\n            auto end = std::chrono::system_clock::now();\n            std::chrono::duration<double, std::milli> duration = end - start;\n            CHECK(duration.count() >= 2000);\n        }\n        catch (const std::exception &e)\n        {\n            FAIL(\"Unexpected exception, what(): \" + std::string(e.what()));\n        }\n\n        // Test coroutine handler with parameters\n        try\n        {\n            auto req = HttpRequest::newHttpRequest();\n            req->setPath(\"/api/v1/corotest/get_with_param/some_data\");\n            auto resp = co_await client->sendRequestCoro(req);\n            CHECK(resp->getStatusCode() == k200OK);\n            CHECK(resp->getBody() == \"some_data\");\n        }\n        catch (const std::exception &e)\n        {\n            FAIL(\"Unexpected exception, what(): \" + std::string(e.what()));\n        }\n        try\n        {\n            auto req = HttpRequest::newHttpRequest();\n            req->setPath(\"/api/v1/corotest/get_with_param2/some_data\");\n            auto resp = co_await client->sendRequestCoro(req);\n            CHECK(resp->getStatusCode() == k200OK);\n            CHECK(resp->getBody() == \"some_data\");\n        }\n        catch (const std::exception &e)\n        {\n            FAIL(\"Unexpected exception, what(): \" + std::string(e.what()));\n        }\n\n        // Test coroutine middleware\n        try\n        {\n            auto req = HttpRequest::newHttpRequest();\n            req->setPath(\"/test-middleware-coro\");\n            auto resp = co_await client->sendRequestCoro(req);\n            CHECK(resp->body() == \"12corotestcoro21\");\n        }\n        catch (const std::exception &e)\n        {\n            FAIL(\"Unexpected exception, what(): \" + std::string(e.what()));\n        }\n    });\n#endif\n}\n\nvoid loadFileLengths()\n{\n    struct stat filestat;\n    if (stat(\"index.html\", &filestat) < 0)\n    {\n        LOG_SYSERR << \"Unable to retrieve index.html file sizes\";\n        exit(1);\n    }\n    indexLen = filestat.st_size;\n    if (stat(\"a-directory/page.html\", &filestat) < 0)\n    {\n        LOG_SYSERR << \"Unable to retrieve a-directory/page.html file sizes\";\n        exit(1);\n    }\n    indexImplicitLen = filestat.st_size;\n}\n\nDROGON_TEST(HttpTest)\n{\n    auto client = HttpClient::newHttpClient(\"http://127.0.0.1:8848\");\n    client->setPipeliningDepth(10);\n    REQUIRE(client->secure() == false);\n    REQUIRE(client->port() == 8848);\n    REQUIRE(client->host() == \"127.0.0.1\");\n    REQUIRE(client->onDefaultPort() == false);\n\n    doTest(client, TEST_CTX);\n}\n\nDROGON_TEST(HttpsTest)\n{\n    if (!app().supportSSL())\n        return;\n\n    auto client = HttpClient::newHttpClient(\"https://127.0.0.1:8849\",\n                                            app().getLoop(),\n                                            false,\n                                            false);\n    client->setPipeliningDepth(10);\n    REQUIRE(client->secure() == true);\n    REQUIRE(client->port() == 8849);\n    REQUIRE(client->host() == \"127.0.0.1\");\n    REQUIRE(client->onDefaultPort() == false);\n\n    doTest(client, TEST_CTX);\n}\n\nDROGON_TEST(HttpsTimeoutTest)\n{\n    if (!app().supportSSL())\n        return;\n\n    auto client = HttpClient::newHttpClient(\"https://127.0.0.1:8849\",\n                                            app().getLoop(),\n                                            false,\n                                            false);\n    auto req = HttpRequest::newHttpRequest();\n    req->setPath(\"/api/v1/apitest/static\");\n    req->setMethod(drogon::Get);\n    auto weakClient = std::weak_ptr<HttpClient>(client);\n    auto weakReq = std::weak_ptr<HttpRequest>(req);\n    client->sendRequest(\n        req,\n        [weakClient, weakReq, TEST_CTX](ReqResult result,\n                                        const HttpResponsePtr &resp) {\n            REQUIRE(result == ReqResult::Ok);\n            CHECK(resp->getStatusCode() == k200OK);\n\n            app().getLoop()->queueInLoop([weakClient, weakReq, TEST_CTX]() {\n                CHECK(weakReq.expired());\n                CHECK(weakClient.expired());\n            });\n        },\n        60);\n}\n\nint main(int argc, char **argv)\n{\n    trantor::Logger::setLogLevel(trantor::Logger::LogLevel::kDebug);\n    loadFileLengths();\n\n    std::promise<void> p1;\n    std::future<void> f1 = p1.get_future();\n\n    std::thread thr([&p1]() {\n        app().getLoop()->queueInLoop([&p1]() { p1.set_value(); });\n        app().run();\n    });\n\n    f1.get();\n    int testStatus = test::run(argc, argv);\n    app().getLoop()->queueInLoop([]() { app().quit(); });\n    thr.join();\n    return testStatus;\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/BeginAdviceTest.cc",
    "content": "#include \"BeginAdviceTest.h\"\n\nstd::string BeginAdviceTest::content_ = \"Default content\";\n\nvoid BeginAdviceTest::asyncHandleHttpRequest(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setBody(content_);\n    callback(resp);\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/BeginAdviceTest.h",
    "content": "#pragma once\n#include <drogon/HttpSimpleController.h>\n#include <string>\n\nusing namespace drogon;\n\nclass BeginAdviceTest : public drogon::HttpSimpleController<BeginAdviceTest>\n{\n  public:\n    void asyncHandleHttpRequest(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) override;\n    PATH_LIST_BEGIN\n    // list path definitions here;\n    // PATH_ADD(\"/path\",\"filter1\",\"filter2\",...);\n    PATH_ADD(\"/test_begin_advice\", Get);\n\n    PATH_LIST_END\n    BeginAdviceTest()\n    {\n        LOG_DEBUG << \"BeginAdviceTest constructor\";\n    }\n\n    static void setContent(const std::string &content)\n    {\n        content_ = content;\n    }\n\n  private:\n    static std::string content_;\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/CoroFilter.cpp",
    "content": "//\n// Created by wanchen.he on 2022/8/16.\n//\n\n#include \"CoroFilter.h\"\n\nTask<HttpResponsePtr> CoroFilter::doFilter(const HttpRequestPtr& req)\n{\n    int secs = std::stoi(req->getParameter(\"secs\"));\n    co_await sleepCoro(trantor::EventLoop::getEventLoopOfCurrentThread(), secs);\n    co_return {};\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/CoroFilter.h",
    "content": "//\n// Created by wanchen.he on 2022/8/16.\n//\n#pragma once\n\n#include <drogon/HttpFilter.h>\nusing namespace drogon;\n\nclass CoroFilter : public drogon::HttpCoroFilter<CoroFilter>\n{\n  public:\n    Task<HttpResponsePtr> doFilter(const HttpRequestPtr &req) override;\n\n    CoroFilter()\n    {\n        LOG_DEBUG << \"CoroFilter constructor\";\n    }\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/CustomCtrl.cc",
    "content": "#include \"CustomCtrl.h\"\n\n// add definition of your processing function here\n\nvoid CustomCtrl::hello(const HttpRequestPtr &req,\n                       std::function<void(const HttpResponsePtr &)> &&callback,\n                       const std::string &userName) const\n{\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setBody(\"<P>\" + greetings_ + \", \" + userName + \"</P>\");\n    callback(resp);\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/CustomCtrl.h",
    "content": "#pragma once\n#include <drogon/HttpController.h>\nusing namespace drogon;\n\nclass CustomCtrl : public drogon::HttpController<CustomCtrl, false>\n{\n  public:\n    METHOD_LIST_BEGIN\n    // use METHOD_ADD to add your custom processing function here;\n    METHOD_ADD(CustomCtrl::hello,\n               \"/{userName}\",\n               Get,\n               \"CustomHeaderFilter\");  // path is /customctrl/{arg1}\n    METHOD_LIST_END\n\n    explicit CustomCtrl(const std::string &greetings) : greetings_(greetings)\n    {\n    }\n\n    void hello(const HttpRequestPtr &req,\n               std::function<void(const HttpResponsePtr &)> &&callback,\n               const std::string &userName) const;\n\n  private:\n    std::string greetings_;\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/CustomHeaderFilter.cc",
    "content": "/**\n *\n *  CustomHeaderFilter.cc\n *\n */\n\n#include \"CustomHeaderFilter.h\"\n\nusing namespace drogon;\n\nvoid CustomHeaderFilter::doFilter(const HttpRequestPtr &req,\n                                  FilterCallback &&fcb,\n                                  FilterChainCallback &&fccb)\n{\n    if (req->getHeader(field_) == value_)\n    {\n        // Passed\n        fccb();\n        return;\n    }\n    // Check failed\n    auto res = drogon::HttpResponse::newHttpResponse();\n    res->setStatusCode(k500InternalServerError);\n    fcb(res);\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/CustomHeaderFilter.h",
    "content": "/**\n *\n *  CustomHeaderFilter.h\n *\n */\n\n#pragma once\n\n#include <drogon/HttpFilter.h>\nusing namespace drogon;\n\nclass CustomHeaderFilter : public HttpFilter<CustomHeaderFilter, false>\n{\n  public:\n    CustomHeaderFilter(const std::string &field, const std::string &value)\n        : field_(field), value_(value)\n    {\n    }\n\n    void doFilter(const HttpRequestPtr &req,\n                  FilterCallback &&fcb,\n                  FilterChainCallback &&fccb) override;\n\n  private:\n    std::string field_;\n    std::string value_;\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/DigestAuthFilter.cc",
    "content": "#include \"DigestAuthFilter.h\"\n\n#include <drogon/utils/Utilities.h>\n#include <algorithm>\n#include <cctype>\n#include <string>\n\nstd::string method2String(HttpMethod m)\n{\n    switch (m)\n    {\n        case Get:\n            return \"GET\";\n        case Post:\n            return \"POST\";\n        case Head:\n            return \"HEAD\";\n        case Put:\n            return \"PUT\";\n        case Delete:\n            return \"DELETE\";\n        case Options:\n            return \"OPTIONS\";\n        case Patch:\n            return \"PATCH\";\n        default:\n            return \"INVALID\";\n    }\n}\n\nstd::string toLower(const std::string &in)\n{\n    std::string out = in;\n    std::transform(out.begin(), out.end(), out.begin(), [](unsigned char c) {\n        return tolower(c);\n    });\n    return out;\n}\n\nbool DigestAuthFilter::isEndOfAttributeName(size_t pos,\n                                            size_t len,\n                                            const char *data)\n{\n    if (pos >= len)\n        return true;\n    if (isspace(static_cast<unsigned char>(data[pos])))\n        return true;\n    // The reason for this complexity is that some attributes may contain\n    // trailing equal signs (like base64 tokens in Negotiate auth headers)\n    if ((pos + 1 < len) && (data[pos] == '=') &&\n        !isspace(static_cast<unsigned char>(data[pos + 1])) &&\n        (data[pos + 1] != '='))\n    {\n        return true;\n    }\n    return false;\n}\n\nvoid DigestAuthFilter::httpParseAttributes(const char *data,\n                                           size_t len,\n                                           HttpAttributeList &attributes)\n{\n    size_t pos = 0;\n    while (true)\n    {\n        // Skip leading whitespace\n        while ((pos < len) && isspace(static_cast<unsigned char>(data[pos])))\n        {\n            ++pos;\n        }\n\n        // End of attributes?\n        if (pos >= len)\n            return;\n\n        // Find end of attribute name\n        size_t start = pos;\n        while (!isEndOfAttributeName(pos, len, data))\n        {\n            ++pos;\n        }\n\n        HttpAttribute attribute;\n        attribute.first.assign(data + start, data + pos);\n\n        // Attribute has value?\n        if ((pos < len) && (data[pos] == '='))\n        {\n            ++pos;  // Skip '='\n            // Check if quoted value\n            if ((pos < len) && (data[pos] == '\"'))\n            {\n                while (++pos < len)\n                {\n                    if (data[pos] == '\"')\n                    {\n                        ++pos;\n                        break;\n                    }\n                    if ((data[pos] == '\\\\') && (pos + 1 < len))\n                        ++pos;\n                    attribute.second.append(1, data[pos]);\n                }\n            }\n            else\n            {\n                while ((pos < len) &&\n                       !isspace(static_cast<unsigned char>(data[pos])) &&\n                       (data[pos] != ','))\n                {\n                    attribute.second.append(1, data[pos++]);\n                }\n            }\n        }\n\n        attributes.push_back(attribute);\n        if ((pos < len) && (data[pos] == ','))\n            ++pos;  // Skip ','\n    }\n}\n\nbool DigestAuthFilter::httpHasAttribute(const HttpAttributeList &attributes,\n                                        const std::string &name,\n                                        std::string *value)\n{\n    for (HttpAttributeList::const_iterator it = attributes.begin();\n         it != attributes.end();\n         ++it)\n    {\n        if (it->first == name)\n        {\n            if (value)\n            {\n                *value = it->second;\n            }\n            return true;\n        }\n    }\n    return false;\n}\n\nDigestAuthFilter::DigestAuthFilter(\n    const std::map<std::string, std::string> &credentials,\n    const std::string &realm,\n    const std::string &opaque)\n    : credentials(credentials), realm(realm), opaque(opaque)\n{\n}\n\nvoid DigestAuthFilter::doFilter(const HttpRequestPtr &req,\n                                FilterCallback &&cb,\n                                FilterChainCallback &&ccb)\n{\n    if (!req->session())\n    {\n        // no session support by framework,pls enable session\n        auto resp = HttpResponse::newNotFoundResponse();\n        cb(resp);\n        return;\n    }\n\n    auto auth_header = req->getHeader(\"Authorization\");\n    if (!auth_header.empty())\n    {\n        HttpAttributeList att_list;\n        httpParseAttributes(auth_header.c_str(), auth_header.size(), att_list);\n        std::string username, realm, nonce, uri, opaque, response;\n        if (httpHasAttribute(att_list, \"username\", &username) &&\n            httpHasAttribute(att_list, \"realm\", &realm) &&\n            httpHasAttribute(att_list, \"nonce\", &nonce) &&\n            httpHasAttribute(att_list, \"uri\", &uri) &&\n            httpHasAttribute(att_list, \"opaque\", &opaque) &&\n            httpHasAttribute(att_list, \"response\", &response))\n        {\n            if (credentials.find(username) != credentials.end())\n            {\n                std::string A1 =\n                    username + \":\" + realm + \":\" + credentials.at(username);\n                std::string A2 = method2String(req->getMethod()) + \":\" + uri;\n                std::string A1_middle_A2 = toLower(utils::getMd5(A1)) + \":\" +\n                                           nonce + \":\" +\n                                           toLower(utils::getMd5(A2));\n                std::string calculated_response =\n                    toLower(utils::getMd5(A1_middle_A2));\n                if (response == calculated_response)\n                {\n                    // Passed\n                    ccb();\n                    return;\n                }\n                else\n                {\n                    LOG_DEBUG << \"invalid response \" << response\n                              << \", calculated \" << calculated_response;\n                }\n            }\n            else\n            {\n                LOG_DEBUG << \"invalid username \" << username;\n            }\n        }\n        else\n        {\n            LOG_DEBUG << \"missing attributes in WWW-Authenticate header\"\n                      << auth_header;\n        }\n    }\n    // not Passed\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setStatusCode(k401Unauthorized);\n    resp->addHeader(\"WWW-Authenticate\",\n                    \" Digest realm=\\\"\" + realm + \"\\\", nonce=\\\"\" +\n                        toLower(utils::getMd5(std::to_string(time(0)))) +\n                        \"\\\", opaque=\\\"\" + opaque + \"\\\"\");\n    cb(resp);\n    return;\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/DigestAuthFilter.h",
    "content": "#pragma once\n\n#include <drogon/HttpFilter.h>\nusing namespace drogon;\n\ntypedef std::pair<std::string, std::string> HttpAttribute;\ntypedef std::vector<HttpAttribute> HttpAttributeList;\ntypedef std::map<std::string /*username*/, std::string /*password*/>\n    CredentialsMap;\n\nclass DigestAuthFilter : public drogon::HttpFilter<DigestAuthFilter, false>\n{\n    const std::map<std::string, std::string> credentials;\n    const std::string realm;\n    const std::string opaque;\n\n    static bool isEndOfAttributeName(size_t pos, size_t len, const char *data);\n    static void httpParseAttributes(const char *data,\n                                    size_t len,\n                                    HttpAttributeList &attributes);\n\n    static bool httpHasAttribute(const HttpAttributeList &attributes,\n                                 const std::string &name,\n                                 std::string *value);\n\n  public:\n    explicit DigestAuthFilter(const CredentialsMap &credentials,\n                              const std::string &realm,\n                              const std::string &opaque);\n    void doFilter(const HttpRequestPtr &req,\n                  FilterCallback &&cb,\n                  FilterChainCallback &&ccb) override;\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/DoNothingPlugin.cc",
    "content": "/**\n *\n *  DoNothingPlugin.cc\n *\n */\n\n#include \"DoNothingPlugin.h\"\n\nusing namespace drogon;\n\nvoid DoNothingPlugin::initAndStart(const Json::Value &config)\n{\n    /// Initialize and start the plugin\n}\n\nvoid DoNothingPlugin::shutdown()\n{\n    /// Shutdown the plugin\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/DoNothingPlugin.h",
    "content": "/**\n *\n *  DoNothingPlugin.h\n *\n */\n\n#pragma once\n\n#include <drogon/plugins/Plugin.h>\nusing namespace drogon;\n\nclass DoNothingPlugin : public Plugin<DoNothingPlugin>\n{\n  public:\n    DoNothingPlugin()\n    {\n    }\n\n    /// This method must be called by drogon to initialize and start the plugin.\n    /// It must be implemented by the user.\n    void initAndStart(const Json::Value &config) override;\n\n    /// This method must be called by drogon to shutdown the plugin.\n    /// It must be implemented by the user.\n    void shutdown() override;\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/FileUpload.csp",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>File upload</title>\n    <script type=\"text/javascript\">\n        var xhr;\n        //File uploading method\n        function UpladFile() {\n            var fileObj = document.getElementById(\"file\").files[0]; // js get file object\n            var url =  \"/api/attachment/upload\"; \n\n            var form = new FormData(); // FormData object\n            form.append(\"file\", fileObj); // File object\n\n            xhr = new XMLHttpRequest();  // XMLHttpRequest object\n            xhr.open(\"post\", url, true); //post\n            xhr.onload = uploadComplete; \n            xhr.onerror =  uploadFailed; \n\n            xhr.upload.onprogress = progressFunction;\n            xhr.upload.onloadstart = function(){\n                ot = new Date().getTime();\n                oloaded = 0;\n            };\n\n            xhr.send(form); \n        }\n\n        function uploadComplete(evt) {\n            var data = JSON.parse(evt.target.responseText);\n            if(data.result == \"ok\") {\n                alert(\"Uploaded successfully!\");\n            }else{\n                alert(\"Upload failed!\");\n            }\n        }\n        \n        function uploadFailed(evt) {\n            alert(\"Upload failed!\");\n        }\n        \n        function cancleUploadFile(){\n            xhr.abort();\n        }\n\n        function progressFunction(evt) {\n            var progressBar = document.getElementById(\"progressBar\");\n            var percentageDiv = document.getElementById(\"percentage\");\n            if (evt.lengthComputable) {//\n                progressBar.max = evt.total;\n                progressBar.value = evt.loaded;\n                percentageDiv.innerHTML = Math.round(evt.loaded / evt.total * 100) + \"%\";\n            }\n            var time = document.getElementById(\"time\");\n            var nt = new Date().getTime();\n            var pertime = (nt-ot)/1000; \n            ot = new Date().getTime(); \n            var perload = evt.loaded - oloaded; \n            oloaded = evt.loaded;\n            var speed = perload/pertime;\n            var bspeed = speed;\n            var units = 'b/s';\n            if(speed/1024>1){\n                speed = speed/1024;\n                units = 'k/s';\n            }\n            if(speed/1024>1){\n                speed = speed/1024;\n                units = 'M/s';\n            }\n            speed = speed.toFixed(1);\n            var resttime = ((evt.total-evt.loaded)/bspeed).toFixed(1);\n            time.innerHTML = ',Speed: '+speed+units+', the remaining time: '+resttime+'s';\n            if(bspeed==0) time.innerHTML = 'Upload cancelled';\n        }\n    </script>\n</head>\n<body>\n<progress id=\"progressBar\" value=\"0\" max=\"100\" style=\"width: 300px;\"></progress>\n<span id=\"percentage\"></span><span id=\"time\"></span>\n<br /><br />\n<input type=\"file\" id=\"file\" name=\"myfile\" />\n<input type=\"button\" onclick=\"UpladFile()\" value=\"Upload\" />\n<input type=\"button\" onclick=\"cancleUploadFile()\" value=\"Cancel\" />\n</body>\n</html>\n"
  },
  {
    "path": "lib/tests/integration_test/server/ForwardCtrl.cc",
    "content": "#include \"ForwardCtrl.h\"\n\nvoid ForwardCtrl::asyncHandleHttpRequest(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    req->setPath(\"/repos/an-tao/drogon/git/refs/heads/master\");\n    app().forward(\n        req,\n        [callback = std::move(callback)](const HttpResponsePtr &resp) {\n            callback(resp);\n        },\n        \"https://api.github.com\");\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/ForwardCtrl.h",
    "content": "#pragma once\n#include <drogon/HttpSimpleController.h>\nusing namespace drogon;\n\nclass ForwardCtrl : public drogon::HttpSimpleController<ForwardCtrl>\n{\n  public:\n    void asyncHandleHttpRequest(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) override;\n    PATH_LIST_BEGIN\n    // list path definitions here;\n    PATH_ADD(\"/forward\", Get);\n    PATH_LIST_END\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/JsonTestController.cc",
    "content": "#include \"JsonTestController.h\"\n#include <json/json.h>\n\nvoid JsonTestController::asyncHandleHttpRequest(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    Json::Value json;\n    json[\"path\"] = \"json\";\n    json[\"name\"] = \"json test\";\n    Json::Value array;\n    for (int i = 0; i < 5; ++i)\n    {\n        Json::Value user;\n        user[\"id\"] = i;\n        user[\"name\"] = \"none\";\n        user[\"c_name\"] = \"张三\";\n        array.append(user);\n    }\n    json[\"rows\"] = array;\n    auto resp = HttpResponse::newHttpJsonResponse(json);\n    assert(resp->jsonObject().get());\n    callback(resp);\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/JsonTestController.h",
    "content": "#pragma once\n\n#include <drogon/HttpSimpleController.h>\nusing namespace drogon;\n\nclass JsonTestController\n    : public drogon::HttpSimpleController<JsonTestController>\n{\n  public:\n    // TestController(){}\n    void asyncHandleHttpRequest(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) override;\n\n    PATH_LIST_BEGIN\n    PATH_ADD(\"/json\", Get, \"drogon::LocalHostFilter\");\n    PATH_LIST_END\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/ListParaCtl.cc",
    "content": "#include \"ListParaCtl.h\"\n\nvoid ListParaCtl::asyncHandleHttpRequest(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    // write your application logic here\n    HttpViewData data;\n    data.insert(\"title\", \"list parameters\");\n    data.insert(\"parameters\", req->getParameters());\n    auto res =\n        drogon::HttpResponse::newHttpViewResponse(\"ListParaView.csp\", data);\n    callback(res);\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/ListParaCtl.h",
    "content": "#pragma once\n#include <drogon/HttpSimpleController.h>\nusing namespace drogon;\n\nclass ListParaCtl : public drogon::HttpSimpleController<ListParaCtl>\n{\n  public:\n    void asyncHandleHttpRequest(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) override;\n    PATH_LIST_BEGIN\n    // list path definitions here;\n    // PATH_ADD(\"/path\",\"filter1\",\"filter2\",...);\n    PATH_ADD(\"/listpara\", Get);\n    PATH_LIST_END\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/ListParaView.csp",
    "content": "<%inc\n#include <drogon/HttpRequest.h>\n%>\n<!DOCTYPE html>\n<html>\n<%c++\n    auto para=@@.get<SafeStringMap<std::string>>(\"parameters\");\n%>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>[[ title ]]</title>\n</head>\n<body>\n    <%view header %>\n    <%c++ if(para.size()>0){%>\n    <H1>Parameters</H1>\n    <table border=\"1\">\n      <tr>\n        <th>name</th>\n        <th>value</th>\n      </tr>\n      <%c++ for(auto iter:para){%>\n      <tr>\n        <td>{%iter.first%}</td>\n        <td><%c++ $$<<iter.second;%></td>\n      </tr>\n      <%c++}%>\n    </table>\n    <%c++ }else{%>\n    <H1>no parameter</H1>\n    <%c++}%>\n</body>\n</html>\n"
  },
  {
    "path": "lib/tests/integration_test/server/MethodTest.cc",
    "content": "#include \"MethodTest.h\"\n\nstatic void makeGetRespose(\n    const std::function<void(const HttpResponsePtr &)> &callback)\n{\n    callback(drogon::HttpResponse::newHttpJsonResponse(\"GET\"));\n}\n\nstatic void makePostRespose(\n    const std::function<void(const HttpResponsePtr &)> &callback)\n{\n    callback(drogon::HttpResponse::newHttpJsonResponse(\"POST\"));\n}\n\nvoid MethodTest::get(const HttpRequestPtr &req,\n                     std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    LOG_DEBUG;\n    makeGetRespose(callback);\n}\n\nvoid MethodTest::post(const HttpRequestPtr &req,\n                      std::function<void(const HttpResponsePtr &)> &&callback,\n                      std::string str)\n{\n    LOG_DEBUG << str;\n    makePostRespose(callback);\n}\n\nvoid MethodTest::getReg(const HttpRequestPtr &req,\n                        std::function<void(const HttpResponsePtr &)> &&callback,\n                        std::string regStr)\n{\n    LOG_DEBUG << regStr;\n    makeGetRespose(callback);\n}\n\nvoid MethodTest::postReg(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback,\n    std::string regStr,\n    std::string str)\n{\n    LOG_DEBUG << regStr;\n    LOG_DEBUG << str;\n    makePostRespose(callback);\n}\n\nvoid MethodTest::postRegex(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback,\n    std::string regStr)\n{\n    LOG_DEBUG << regStr;\n    makePostRespose(callback);\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/MethodTest.h",
    "content": "#pragma once\n#include <drogon/HttpController.h>\nusing namespace drogon;\n\nclass MethodTest : public drogon::HttpController<MethodTest>\n{\n  public:\n    METHOD_LIST_BEGIN\n    ADD_METHOD_TO(MethodTest::get, \"/api/method/test\", Get);\n    ADD_METHOD_TO(MethodTest::post, \"/api/method/test?test={}\", Post);\n    ADD_METHOD_TO(MethodTest::getReg, \"/api/method/{}/test\", Get);\n    ADD_METHOD_TO(MethodTest::postReg, \"/api/method/{}/test?test={}\", Post);\n    ADD_METHOD_VIA_REGEX(MethodTest::getReg,\n                         \"/api/method/regex/(.*)/test\",\n                         Get);\n    ADD_METHOD_VIA_REGEX(MethodTest::postRegex,\n                         \"/api/method/regex/(.*)/test\",\n                         Post);\n    METHOD_LIST_END\n\n    void get(const HttpRequestPtr &req,\n             std::function<void(const HttpResponsePtr &)> &&callback);\n    void post(const HttpRequestPtr &req,\n              std::function<void(const HttpResponsePtr &)> &&callback,\n              std::string str);\n\n    void getReg(const HttpRequestPtr &req,\n                std::function<void(const HttpResponsePtr &)> &&callback,\n                std::string regStr);\n    void postReg(const HttpRequestPtr &req,\n                 std::function<void(const HttpResponsePtr &)> &&callback,\n                 std::string regStr,\n                 std::string str);\n    void postRegex(const HttpRequestPtr &req,\n                   std::function<void(const HttpResponsePtr &)> &&callback,\n                   std::string regStr);\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/MiddlewareTest.cc",
    "content": "#include <drogon/HttpController.h>\n#include <drogon/HttpMiddleware.h>\nusing namespace drogon;\n\nclass Middleware1 : public drogon::HttpMiddleware<Middleware1>\n{\n  public:\n    Middleware1()\n    {\n        // do not omit constructor\n        void(0);\n    };\n\n    void invoke(const HttpRequestPtr &req,\n                MiddlewareNextCallback &&nextCb,\n                MiddlewareCallback &&mcb) override\n    {\n        auto ptr = std::make_shared<std::string>(\"1\");\n        req->attributes()->insert(\"test-middleware\", ptr);\n\n        nextCb([req, ptr, mcb = std::move(mcb)](const HttpResponsePtr &resp) {\n            ptr->append(\"1\");\n            resp->setBody(*ptr);\n            mcb(resp);\n        });\n    }\n};\n\nclass Middleware2 : public drogon::HttpMiddleware<Middleware2>\n{\n  public:\n    Middleware2()\n    {\n        // do not omit constructor\n        void(0);\n    };\n\n    void invoke(const HttpRequestPtr &req,\n                MiddlewareNextCallback &&nextCb,\n                MiddlewareCallback &&mcb) override\n    {\n        auto ptr = req->attributes()->get<std::shared_ptr<std::string>>(\n            \"test-middleware\");\n        ptr->append(\"2\");\n\n        nextCb([req, ptr, mcb = std::move(mcb)](const HttpResponsePtr &resp) {\n            ptr->append(\"2\");\n            resp->setBody(*ptr);\n            mcb(resp);\n        });\n    }\n};\n\nclass Middleware3 : public drogon::HttpMiddleware<Middleware3>\n{\n  public:\n    Middleware3()\n    {\n        // do not omit constructor\n        void(0);\n    };\n\n    void invoke(const HttpRequestPtr &req,\n                MiddlewareNextCallback &&nextCb,\n                MiddlewareCallback &&mcb) override\n    {\n        auto ptr = req->attributes()->get<std::shared_ptr<std::string>>(\n            \"test-middleware\");\n        ptr->append(\"3\");\n\n        nextCb([req, ptr, mcb = std::move(mcb)](const HttpResponsePtr &resp) {\n            ptr->append(\"3\");\n            resp->setBody(*ptr);\n            mcb(resp);\n        });\n    }\n};\n\nclass MiddlewareBlock : public drogon::HttpMiddleware<MiddlewareBlock>\n{\n  public:\n    MiddlewareBlock()\n    {\n        // do not omit constructor\n        void(0);\n    };\n\n    void invoke(const HttpRequestPtr &req,\n                MiddlewareNextCallback &&nextCb,\n                MiddlewareCallback &&mcb) override\n    {\n        auto ptr = req->attributes()->get<std::shared_ptr<std::string>>(\n            \"test-middleware\");\n        ptr->append(\"block\");\n\n        mcb(HttpResponse::newHttpResponse());\n    }\n};\n\n#if defined(__cpp_impl_coroutine)\nclass MiddlewareCoro : public drogon::HttpCoroMiddleware<MiddlewareCoro>\n{\n  public:\n    MiddlewareCoro()\n    {\n        // do not omit constructor\n        void(0);\n    };\n\n    Task<HttpResponsePtr> invoke(const HttpRequestPtr &req,\n                                 MiddlewareNextAwaiter &&nextAwaiter) override\n    {\n        auto ptr = req->attributes()->get<std::shared_ptr<std::string>>(\n            \"test-middleware\");\n        ptr->append(\"coro\");\n\n        auto resp = co_await nextAwaiter;\n\n        ptr->append(\"coro\");\n        resp->setBody(*ptr);\n        co_return resp;\n    }\n};\n#endif\n\nclass MiddlewareTest : public drogon::HttpController<MiddlewareTest>\n{\n  public:\n    METHOD_LIST_BEGIN\n    ADD_METHOD_TO(MiddlewareTest::handleRequest,\n                  \"/test-middleware\",\n                  Get,\n                  \"Middleware1\",\n                  \"Middleware2\",\n                  \"Middleware3\",\n                  \"Middleware4\");\n    ADD_METHOD_TO(MiddlewareTest::handleRequest,\n                  \"/test-middleware-block\",\n                  Get,\n                  \"Middleware1\",\n                  \"Middleware2\",\n                  \"MiddlewareBlock\",\n                  \"Middleware3\");\n\n#if defined(__cpp_impl_coroutine)\n    ADD_METHOD_TO(MiddlewareTest::handleRequest,\n                  \"/test-middleware-coro\",\n                  Get,\n                  \"Middleware1\",\n                  \"Middleware2\",\n                  \"MiddlewareCoro\");\n#endif\n    METHOD_LIST_END\n\n    void handleRequest(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) const\n    {\n        req->attributes()\n            ->get<std::shared_ptr<std::string>>(\"test-middleware\")\n            ->append(\"test\");\n        callback(HttpResponse::newHttpResponse());\n    }\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/PipeliningTest.cc",
    "content": "#include \"PipeliningTest.h\"\n#include <trantor/net/EventLoop.h>\n#include <atomic>\n#include <mutex>\n\nvoid PipeliningTest::normalPipe(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback) const\n{\n    static std::atomic<int> counter{0};\n    int c = counter.fetch_add(1);\n    if (c % 3 == 1)\n    {\n        auto resp = HttpResponse::newHttpResponse();\n        auto str = utils::formattedString(\"<P>the %dth response</P>\", c);\n        resp->addHeader(\"counter\", utils::formattedString(\"%d\", c));\n        resp->setBody(std::move(str));\n        callback(resp);\n        return;\n    }\n    double delay = ((double)(10 - (c % 10))) / 10.0;\n    if (c % 3 == 2)\n    {\n        // call the callback in another thread.\n        drogon::app().getLoop()->runAfter(delay, [c, callback]() {\n            auto resp = HttpResponse::newHttpResponse();\n            auto str = utils::formattedString(\"<P>the %dth response</P>\", c);\n            resp->addHeader(\"counter\", utils::formattedString(\"%d\", c));\n            resp->setBody(std::move(str));\n            callback(resp);\n        });\n        return;\n    }\n    trantor::EventLoop::getEventLoopOfCurrentThread()->runAfter(\n        delay, [c, callback]() {\n            auto resp = HttpResponse::newHttpResponse();\n            auto str = utils::formattedString(\"<P>the %dth response</P>\", c);\n            resp->addHeader(\"counter\", utils::formattedString(\"%d\", c));\n            resp->setBody(std::move(str));\n            callback(resp);\n        });\n}\n\n// Receive 1, cache 1\n// Receive 2, send 1 send 2\nvoid PipeliningTest::strangePipe1(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    static std::mutex mtx;\n    static std::vector<\n        std::pair<std::function<void(const HttpResponsePtr &)>, std::string>>\n        callbacks;\n\n    LOG_INFO << \"Receive request \" << req->body();\n    std::function<void(const HttpResponsePtr &)> cb1;\n    std::string body1;\n    std::function<void(const HttpResponsePtr &)> cb2;\n    std::string body2;\n    {\n        std::lock_guard<std::mutex> lock(mtx);\n        if (callbacks.empty())\n        {\n            callbacks.emplace_back(std::move(callback), req->getBody());\n            return;\n        }\n        auto item = std::move(callbacks.back());\n        callbacks.pop_back();\n        cb1 = std::move(item.first);\n        body1 = std::move(item.second);\n        cb2 = std::move(callback);\n        body2 = std::string{req->body()};\n    }\n    if (cb1)\n    {\n        auto resp = HttpResponse::newHttpResponse();\n        resp->setBody(body1);\n        cb1(resp);\n    }\n    if (cb2)\n    {\n        auto resp = HttpResponse::newHttpResponse();\n        resp->setBody(body2);\n        cb2(resp);\n    }\n}\n\n// Receive 1, cache 1\n// Receive 2, send 1 cache 2\n// Receive 3, send 2 send 3\nvoid PipeliningTest::strangePipe2(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    static std::mutex mtx;\n    static std::vector<\n        std::pair<std::function<void(const HttpResponsePtr &)>, std::string>>\n        callbacks;\n    static uint64_t idx{0};\n\n    LOG_INFO << \"Receive request \" << req->body();\n    std::function<void(const HttpResponsePtr &)> cb1;\n    std::string body1;\n    std::function<void(const HttpResponsePtr &)> cb2;\n    std::string body2;\n    {\n        std::lock_guard<std::mutex> lock(mtx);\n        ++idx;\n        if (idx % 3 == 1)\n        {\n            assert(callbacks.empty());\n            callbacks.emplace_back(std::move(callback), req->getBody());\n            return;\n        }\n        assert(callbacks.size() == 1);\n        if (idx % 3 == 2)\n        {\n            auto item = std::move(callbacks.back());\n            cb1 = std::move(item.first);\n            body1 = std::move(item.second);\n            callbacks.pop_back();\n            callbacks.emplace_back(std::move(callback), req->getBody());\n        }\n        else\n        {\n            auto item = std::move(callbacks.back());\n            cb1 = std::move(item.first);\n            body1 = std::move(item.second);\n            callbacks.pop_back();\n            cb2 = std::move(callback);\n            body2 = std::string{req->body()};\n        }\n    }\n    if (cb1)\n    {\n        auto resp = HttpResponse::newHttpResponse();\n        resp->setBody(body1);\n        cb1(resp);\n    }\n    if (cb2)\n    {\n        auto resp = HttpResponse::newHttpResponse();\n        resp->setBody(body2);\n        cb2(resp);\n    }\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/PipeliningTest.h",
    "content": "#pragma once\n#include <drogon/HttpController.h>\nusing namespace drogon;\n\n// class PipeliningTest : public drogon::HttpSimpleController<PipeliningTest>\n//{\n//   public:\n//     virtual void asyncHandleHttpRequest(\n//         const HttpRequestPtr &req,\n//         std::function<void(const HttpResponsePtr &)> &&callback) override;\n//     PATH_LIST_BEGIN\n//     // list path definitions here;\n//     PATH_ADD(\"/pipe\", Get);\n//     PATH_LIST_END\n// };\n\nclass PipeliningTest : public drogon::HttpController<PipeliningTest>\n{\n  public:\n    METHOD_LIST_BEGIN\n    ADD_METHOD_TO(PipeliningTest::normalPipe, \"/pipe\", Get);\n    ADD_METHOD_TO(PipeliningTest::strangePipe1, \"/pipe/strange-1\", Get);\n    ADD_METHOD_TO(PipeliningTest::strangePipe2, \"/pipe/strange-2\", Get);\n    METHOD_LIST_END\n\n    void normalPipe(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) const;\n    void strangePipe1(const HttpRequestPtr &req,\n                      std::function<void(const HttpResponsePtr &)> &&callback);\n    void strangePipe2(const HttpRequestPtr &req,\n                      std::function<void(const HttpResponsePtr &)> &&callback);\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/RangeTestController.cc",
    "content": "#include \"RangeTestController.h\"\n\n#include <fstream>\n\nsize_t RangeTestController::fileSize_ = 10000 * 100;  // 1e6 Bytes\n\nRangeTestController::RangeTestController()\n{\n    std::ofstream outfile(\"./range-test.txt\", std::ios::out | std::ios::trunc);\n    for (int i = 0; i < 10000; ++i)\n    {\n        outfile.write(\n            \"01234567890123456789\"\n            \"01234567890123456789\"\n            \"01234567890123456789\"\n            \"01234567890123456789\"\n            \"01234567890123456789\",\n            100);\n    }\n}\n\nvoid RangeTestController::getFile(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback) const\n{\n    auto resp = HttpResponse::newFileResponse(\"./range-test.txt\");\n    callback(resp);\n}\n\nvoid RangeTestController::getFileByRange(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback,\n    size_t offset,\n    size_t length) const\n{\n    auto resp =\n        HttpResponse::newFileResponse(\"./range-test.txt\", offset, length);\n    callback(resp);\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/RangeTestController.h",
    "content": "#pragma once\n#include <drogon/HttpController.h>\nusing namespace drogon;\n\nclass RangeTestController : public drogon::HttpController<RangeTestController>\n{\n  public:\n    METHOD_LIST_BEGIN\n    // path is /RangeTestController\n    METHOD_ADD(RangeTestController::getFile, \"/\", Get);\n    // path is /RangeTestController/{offset}/{length}\n    METHOD_ADD(RangeTestController::getFileByRange, \"/{offset}/{length}\", Get);\n    METHOD_LIST_END\n\n    RangeTestController();\n\n    void getFile(const HttpRequestPtr &req,\n                 std::function<void(const HttpResponsePtr &)> &&callback) const;\n\n    // We do not provide 'Range' header decoding, simply use path as range\n    // parameter.\n    void getFileByRange(const HttpRequestPtr &req,\n                        std::function<void(const HttpResponsePtr &)> &&callback,\n                        size_t offset,\n                        size_t length) const;\n\n    static size_t getFileSize()\n    {\n        return fileSize_;\n    }\n\n  private:\n    static size_t fileSize_;\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/RequestStreamTestCtrl.cc",
    "content": "#include <fstream>\n#include <drogon/HttpController.h>\n#include <drogon/HttpRequest.h>\n#include <drogon/RequestStream.h>\n\nusing namespace drogon;\n\nclass RequestStreamTestCtrl : public HttpController<RequestStreamTestCtrl>\n{\n  public:\n    METHOD_LIST_BEGIN\n    ADD_METHOD_TO(RequestStreamTestCtrl::stream_status, \"/stream_status\", Get);\n    ADD_METHOD_TO(RequestStreamTestCtrl::stream_chunk, \"/stream_chunk\", Post);\n    ADD_METHOD_TO(RequestStreamTestCtrl::stream_upload_echo,\n                  \"/stream_upload_echo\",\n                  Post);\n    METHOD_LIST_END\n\n    void stream_status(\n        const HttpRequestPtr &,\n        std::function<void(const HttpResponsePtr &)> &&callback) const\n    {\n        auto resp = HttpResponse::newHttpResponse();\n        if (app().isRequestStreamEnabled())\n        {\n            resp->setBody(\"enabled\");\n        }\n        else\n        {\n            resp->setBody(\"not enabled\");\n        }\n        callback(resp);\n    }\n\n    void stream_chunk(\n        const HttpRequestPtr &,\n        RequestStreamPtr &&stream,\n        std::function<void(const HttpResponsePtr &)> &&callback) const\n    {\n        if (!stream)\n        {\n            LOG_INFO << \"stream mode is not enabled\";\n            auto resp = HttpResponse::newHttpResponse();\n            resp->setStatusCode(k400BadRequest);\n            resp->setBody(\"no stream\");\n            callback(resp);\n            return;\n        }\n\n        auto respBody = std::make_shared<std::string>();\n        auto reader = RequestStreamReader::newReader(\n            [respBody](const char *data, size_t length) {\n                respBody->append(data, length);\n            },\n            [respBody, callback = std::move(callback)](std::exception_ptr ex) {\n                auto resp = HttpResponse::newHttpResponse();\n                if (ex)\n                {\n                    try\n                    {\n                        std::rethrow_exception(std::move(ex));\n                    }\n                    catch (const std::exception &e)\n                    {\n                        LOG_ERROR << \"stream error: \" << e.what();\n                    }\n                    resp->setStatusCode(k400BadRequest);\n                    resp->setBody(\"stream error\");\n                    callback(resp);\n                }\n                else\n                {\n                    resp->setBody(*respBody);\n                    callback(resp);\n                }\n            });\n        stream->setStreamReader(std::move(reader));\n    }\n\n    void stream_upload_echo(\n        const HttpRequestPtr &req,\n        RequestStreamPtr &&stream,\n        std::function<void(const HttpResponsePtr &)> &&callback) const\n\n    {\n        assert(drogon::app().isRequestStreamEnabled() || !stream);\n\n        if (!stream)\n        {\n            LOG_INFO << \"stream mode is not enabled\";\n            auto resp = HttpResponse::newHttpResponse();\n            resp->setStatusCode(k400BadRequest);\n            resp->setBody(\"no stream\");\n            callback(resp);\n            return;\n        }\n        if (req->contentType() != CT_MULTIPART_FORM_DATA)\n        {\n            auto resp = HttpResponse::newHttpResponse();\n            resp->setStatusCode(k400BadRequest);\n            resp->setBody(\"should upload multipart\");\n            callback(resp);\n            return;\n        }\n\n        struct Context\n        {\n            std::string firstFileContent;\n            size_t currentFileIndex_{0};\n        };\n\n        auto ctx = std::make_shared<Context>();\n        auto reader = RequestStreamReader::newMultipartReader(\n            req,\n            [ctx](MultipartHeader &&header) { ctx->currentFileIndex_++; },\n            [ctx](const char *data, size_t length) {\n                if (ctx->currentFileIndex_ == 1)\n                {\n                    ctx->firstFileContent.append(data, length);\n                }\n            },\n            [ctx, callback = std::move(callback)](std::exception_ptr ex) {\n                auto resp = HttpResponse::newHttpResponse();\n                if (ex)\n                {\n                    try\n                    {\n                        std::rethrow_exception(std::move(ex));\n                    }\n                    catch (const StreamError &e)\n                    {\n                        LOG_ERROR << \"stream error: \" << e.what();\n                    }\n                    catch (const std::exception &e)\n                    {\n                        LOG_ERROR << \"multipart error: \" << e.what();\n                    }\n                    resp->setStatusCode(k400BadRequest);\n                    resp->setBody(\"error\\n\");\n                    callback(resp);\n                }\n                else\n                {\n                    resp->setBody(ctx->firstFileContent);\n                    callback(resp);\n                }\n            });\n        stream->setStreamReader(std::move(reader));\n    }\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/TestController.cc",
    "content": "#include \"TestController.h\"\nusing namespace example;\n\nvoid TestController::asyncHandleHttpRequest(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    // write your application logic here\n    counter_->increment();\n    LOG_WARN << req->matchedPathPatternData();\n    LOG_DEBUG << \"index=\" << threadIndex_.getThreadData();\n    ++(threadIndex_.getThreadData());\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setContentTypeCodeAndCustomString(CT_TEXT_PLAIN,\n                                            \"content-type: plaintext\\r\\n\");\n    resp->setBody(\"<p>Hello, world!</p>\");\n    resp->setExpiredTime(20);\n    callback(resp);\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/TestController.h",
    "content": "#pragma once\n#include <drogon/HttpSimpleController.h>\n#include <drogon/IOThreadStorage.h>\n#include <drogon/utils/monitoring/Counter.h>\n#include <drogon/utils/monitoring/Collector.h>\n#include <drogon/plugins/PromExporter.h>\nusing namespace drogon;\n\nnamespace example\n{\nclass TestController : public drogon::HttpSimpleController<TestController>\n{\n  public:\n    void asyncHandleHttpRequest(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) override;\n    PATH_LIST_BEGIN\n    // list path definitions here;\n    // PATH_ADD(\"/path\",\"filter1\",\"filter2\",...);\n    PATH_ADD(\"/\", Get);\n    PATH_ADD(\"/Test\", \"nonFilter\");\n    PATH_ADD(\"/tpost\", Post, Options);\n    PATH_ADD(\"/slow\", \"TimeFilter\", Get);\n\n    PATH_LIST_END\n    TestController()\n    {\n        LOG_DEBUG << \"TestController constructor\";\n        auto collector = std::make_shared<\n            drogon::monitoring::Collector<drogon::monitoring::Counter>>(\n            \"test_counter\",\n            \"The counter for requests to the root url\",\n            std::vector<std::string>());\n        counter_ = collector->metric(std::vector<std::string>());\n        collector->registerTo(\n            *app().getSharedPlugin<drogon::plugin::PromExporter>());\n    }\n\n  private:\n    drogon::IOThreadStorage<int> threadIndex_;\n    std::shared_ptr<drogon::monitoring::Counter> counter_;\n};\n}  // namespace example\n"
  },
  {
    "path": "lib/tests/integration_test/server/TestPlugin.cc",
    "content": "/**\n *\n *  TestPlugin.cc\n *\n */\n\n#include \"TestPlugin.h\"\n#include <thread>\n#include <chrono>\nusing namespace std::chrono_literals;\n\nusing namespace drogon;\n\nvoid TestPlugin::initAndStart(const Json::Value &config)\n{\n    /// Initialize and start the plugin\n    if (config.isNull())\n        LOG_DEBUG << \"Configuration not defined\";\n    interval_ = config.get(\"heartbeat_interval\", 1).asInt();\n    workThread_ = std::thread([this]() {\n        while (!stop_)\n        {\n            LOG_DEBUG << \"TestPlugin heartbeat!\";\n            std::this_thread::sleep_for(std::chrono::seconds(interval_));\n        }\n    });\n}\n\nvoid TestPlugin::shutdown()\n{\n    /// Shutdown the plugin\n    stop_ = true;\n    workThread_.join();\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/TestPlugin.h",
    "content": "/**\n *\n *  TestPlugin.h\n *\n */\n\n#pragma once\n\n#include <drogon/plugins/Plugin.h>\nusing namespace drogon;\n\nclass TestPlugin : public Plugin<TestPlugin>\n{\n  public:\n    TestPlugin()\n    {\n    }\n\n    /// This method must be called by drogon to initialize and start the plugin.\n    /// It must be implemented by the user.\n    void initAndStart(const Json::Value &config) override;\n\n    /// This method must be called by drogon to shutdown the plugin.\n    /// It must be implemented by the user.\n    void shutdown() override;\n\n  private:\n    std::thread workThread_;\n    bool stop_{false};\n    int interval_{0};\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/TestView.csp",
    "content": "<%inc\n#include <iostream>\n%>\n<%c++\nstd::cout<<\"this is a Http backend rendering Test\"<<std::endl;\n%>\n<!DOCTYPE html>\n<html>\n<%c++ std::string title=@@.get<std::string>(\"title\");%>\n<head>\n    <meta charset=\"UTF-8\">\n    <title><%c++ $$<<title;%></title>\n</head>\n<body>\n<footer>\n    <span>CopyRight@2017 All Rights Reserved</span>\n</footer>\n</body>\n</html>\n"
  },
  {
    "path": "lib/tests/integration_test/server/TestViewCtl.cc",
    "content": "#include \"TestViewCtl.h\"\n\nvoid TestViewCtl::asyncHandleHttpRequest(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    // write your application logic here\n    drogon::HttpViewData data;\n    data.insert(\"title\", std::string(\"TestView\"));\n    auto res = drogon::HttpResponse::newHttpViewResponse(\"TestView\", data);\n    callback(res);\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/TestViewCtl.h",
    "content": "#pragma once\n#include <drogon/HttpSimpleController.h>\nusing namespace drogon;\n\nclass TestViewCtl : public drogon::HttpSimpleController<TestViewCtl>\n{\n  public:\n    void asyncHandleHttpRequest(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback) override;\n    PATH_LIST_BEGIN\n    // list path definitions here;\n    // PATH_ADD(\"/path\",\"filter1\",\"filter2\",...);\n    PATH_ADD(\"/view\");\n    PATH_ADD(\"/\", Post);\n    PATH_LIST_END\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/TimeFilter.cc",
    "content": "//\n// Created by antao on 2018/5/22.\n//\n\n#include \"TimeFilter.h\"\n#define VDate \"visitDate\"\n\nvoid TimeFilter::doFilter(const HttpRequestPtr &req,\n                          FilterCallback &&cb,\n                          FilterChainCallback &&ccb)\n{\n    trantor::Date now = trantor::Date::date();\n    if (!req->session())\n    {\n        // no session support by framework,pls enable session\n        auto resp = HttpResponse::newNotFoundResponse();\n        cb(resp);\n        return;\n    }\n    auto lastDate = req->session()->getOptional<trantor::Date>(VDate);\n    if (lastDate)\n    {\n        LOG_TRACE << \"last:\" << lastDate->toFormattedString(false);\n        req->session()->modify<trantor::Date>(VDate,\n                                              [now](trantor::Date &vdate) {\n                                                  vdate = now;\n                                              });\n        LOG_TRACE << \"update visitDate\";\n        if (now > lastDate->after(10))\n        {\n            // 10 sec later can visit again;\n            ccb();\n            return;\n        }\n        else\n        {\n            Json::Value json;\n            json[\"result\"] = \"error\";\n            json[\"message\"] = \"Access interval should be at least 10 seconds\";\n            auto res = HttpResponse::newHttpJsonResponse(json);\n            cb(res);\n            return;\n        }\n    }\n    LOG_TRACE << \"first visit,insert visitDate\";\n    req->session()->insert(VDate, now);\n    ccb();\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/TimeFilter.h",
    "content": "//\n// Created by antao on 2018/5/22.\n//\n\n#pragma once\n\n#include <drogon/HttpFilter.h>\nusing namespace drogon;\n\nclass TimeFilter : public drogon::HttpFilter<TimeFilter>\n{\n  public:\n    void doFilter(const HttpRequestPtr &req,\n                  FilterCallback &&cb,\n                  FilterChainCallback &&ccb) override;\n\n    TimeFilter()\n    {\n        LOG_DEBUG << \"TimeFilter constructor\";\n    }\n};\n"
  },
  {
    "path": "lib/tests/integration_test/server/WebSocketTest.cc",
    "content": "#include \"WebSocketTest.h\"\nusing namespace example;\n\nstruct Subscriber\n{\n    std::string chatRoomName_;\n    drogon::SubscriberID id_;\n};\n\nvoid WebSocketTest::handleNewMessage(const WebSocketConnectionPtr &wsConnPtr,\n                                     std::string &&message,\n                                     const WebSocketMessageType &type)\n{\n    // write your application logic here\n    LOG_DEBUG << \"new websocket message:\" << message;\n    if (type == WebSocketMessageType::Ping)\n    {\n        LOG_DEBUG << \"recv a ping\";\n    }\n    else if (type == WebSocketMessageType::Text)\n    {\n        auto &s = wsConnPtr->getContextRef<Subscriber>();\n        chatRooms_.publish(s.chatRoomName_, message);\n    }\n}\n\nvoid WebSocketTest::handleConnectionClosed(const WebSocketConnectionPtr &conn)\n{\n    LOG_DEBUG << \"websocket closed!\";\n    auto &s = conn->getContextRef<Subscriber>();\n    chatRooms_.unsubscribe(s.chatRoomName_, s.id_);\n}\n\nvoid WebSocketTest::handleNewConnection(const HttpRequestPtr &req,\n                                        const WebSocketConnectionPtr &conn)\n{\n    LOG_DEBUG << \"new websocket connection!\";\n    conn->send(\"haha!!!\");\n    Subscriber s;\n    s.chatRoomName_ = req->getParameter(\"room_name\");\n    s.id_ = chatRooms_.subscribe(s.chatRoomName_,\n                                 [conn](const std::string &topic,\n                                        const std::string &message) {\n                                     conn->send(message);\n                                 });\n    conn->setContext(std::make_shared<Subscriber>(std::move(s)));\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/WebSocketTest.h",
    "content": "#pragma once\n#include <drogon/WebSocketController.h>\n#include <drogon/PubSubService.h>\nusing namespace drogon;\n\nnamespace example\n{\nclass WebSocketTest : public drogon::WebSocketController<WebSocketTest>\n{\n  public:\n    void handleNewMessage(const WebSocketConnectionPtr &,\n                          std::string &&,\n                          const WebSocketMessageType &) override;\n    void handleConnectionClosed(const WebSocketConnectionPtr &) override;\n    void handleNewConnection(const HttpRequestPtr &,\n                             const WebSocketConnectionPtr &) override;\n    WS_PATH_LIST_BEGIN\n    // list path definitions here;\n    WS_PATH_ADD(\"/chat\", \"drogon::LocalHostFilter\", Get);\n    WS_PATH_LIST_END\n  private:\n    PubSubService<std::string> chatRooms_;\n};\n}  // namespace example\n"
  },
  {
    "path": "lib/tests/integration_test/server/a-directory/page.html",
    "content": "<p><img src=\"https://github.com/an-tao/drogon/wiki/images/drogon-white.jpg\" alt=\"\" /></p>\n\n<p><a href=\"https://travis-ci.com/an-tao/drogon\"><img src=\"https://travis-ci.com/an-tao/drogon.svg?branch=master\" alt=\"Build Status\" /></a>\n<a href=\"https://app.codacy.com/app/an-tao/drogon?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=an-tao/drogon&amp;utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://api.codacy.com/project/badge/Grade/45f8a65ca1844788b9109c0044a618f8\" alt=\"Codacy Badge\" /></a>\n<a href=\"https://lgtm.com/projects/g/an-tao/drogon/alerts/\"><img src=\"https://img.shields.io/lgtm/alerts/g/an-tao/drogon.svg?logo=lgtm&amp;logoWidth=18\" alt=\"Total alerts\" /></a>\n<a href=\"https://lgtm.com/projects/g/an-tao/drogon/context:cpp\"><img src=\"https://img.shields.io/lgtm/grade/cpp/g/an-tao/drogon.svg?logo=lgtm&amp;logoWidth=18\" alt=\"Language grade: C/C++\" /></a> \n<a href=\"https://gitter.im/drogon-web/community?utm_source=badge&amp;utm_medium=badge&amp;utm_campaign=pr-badge&amp;utm_content=badge\"><img src=\"https://badges.gitter.im/drogon-web/community.svg\" alt=\"Join the chat at https://gitter.im/drogon-web/community\" /></a>\n<a href=\"https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon\"><img src=\"https://img.shields.io/badge/Docker-image-blue.svg\" alt=\"Docker image\" /></a></p>\n\n<h3 id=\"overview\">Overview (from an implicit page)</h3>\n<p><strong>Drogon</strong> is a C++14/17-based HTTP application framework. Drogon can be used to easily build various types of web application server programs using C++. <strong>Drogon</strong> is the name of a dragon in the American TV series “Game of Thrones” that I really like.</p>\n\n<p>Drogon’s main application platform is Linux. It also supports Mac OS and FreeBSD. Currently, it does not support windows. Its main features are as follows:</p>\n\n<ul>\n  <li>Use a non-blocking I/O network lib based on epoll (kqueue under MacOS/FreeBSD) to provide high-concurrency, high-performance network IO, please visit the <a href=\"https://github.com/an-tao/drogon/wiki/benchmarks\">benchmarks</a> page for more details;</li>\n  <li>Provide a completely asynchronous programming mode;</li>\n  <li>Support Http1.0/1.1 (server side and client side);</li>\n  <li>Based on template, a simple reflection mechanism is implemented to completely decouple the main program framework, controllers and views.</li>\n  <li>Support cookies and built-in sessions;</li>\n  <li>Support back-end rendering, the controller generates the data to the view to generate the Html page, the view is described by a “JSP-like” CSP file, the C++ code is embedded into the Html page by the CSP tag, and the drogon command-line tool automatically generates the C++ code file for compilation;</li>\n  <li>Support view page dynamic loading (dynamic compilation and loading at runtime);</li>\n  <li>Provide a convenient and flexible routing solution from the path to the controller handler;</li>\n  <li>Support filter chains to facilitate the execution of unified logic (such as login verification, Http Method constraint verification, etc.) before controllers;</li>\n  <li>Support https (based on OpenSSL);</li>\n  <li>Support WebSocket (server side and client side);</li>\n  <li>Support JSON format request and response, very friendly to the Restful API application development;</li>\n  <li>Support file download and upload;</li>\n  <li>Support gzip compression transmission;</li>\n  <li>Support pipelining;</li>\n  <li>Provide a lightweight command line tool, drogon_ctl, to simplify the creation of various classes in Drogon and the generation of view code;</li>\n  <li>Support non-blocking I/O based asynchronously reading and writing database (PostgreSQL and MySQL(MariaDB) database);</li>\n  <li>Support asynchronously reading and writing sqlite3 database based on thread pool;</li>\n  <li>Support ARM Architecture;</li>\n  <li>Provide a convenient lightweight ORM implementation that supports for regular object-to-database bidirectional mapping;</li>\n  <li>Support plugins which can be installed by the configuration file at load time;</li>\n  <li>Support AOP with built-in joinpoints.</li>\n</ul>\n\n<h2 id=\"a-very-simple-example\">A very simple example</h2>\n\n<p>Unlike most C++ frameworks, the main program of the drogon application can be kept clean and simple. Drogon uses a few tricks to decouple controllers from the main program. The routing settings of controllers can be done through macros or configuration file.</p>\n\n<p>Below is the main program of a typical drogon application:</p>\n\n<p><code>c++\n#include &lt;drogon/drogon.h&gt;\nusing namespace drogon;\nint main()\n{\n    app().setLogPath(\"./\");\n    app().setLogLevel(trantor::Logger::kWarn);\n    app().addListener(\"0.0.0.0\", 80);\n    app().setThreadNum(16);\n    app().enableRunAsDaemon();\n    app().run();\n}\n</code></p>\n\n<p>It can be further simplified by using configuration file as follows:</p>\n\n<p><code>c++\n#include &lt;drogon/drogon.h&gt;\nusing namespace drogon;\nint main()\n{\n    app().loadConfigFile(\"./config.json\");\n    app().run();\n}\n</code></p>\n\n<p>Drogon provides some interfaces for adding controller logic directly in the main() function, for example, user can register a handler like this in Drogon:</p>\n\n<p><code>c++\napp.registerHandler(\"/test?username={1}\",\n                    [](const HttpRequestPtr&amp; req,\n                       const std::function&lt;void (const HttpResponsePtr &amp;)&gt; &amp; callback,\n                       const std::string &amp;name)\n                    {\n                        Json::Value json;\n                        json[\"result\"]=\"ok\";\n                        json[\"message\"]=std::string(\"hello,\")+name;\n                        auto resp=HttpResponse::newHttpJsonResponse(json);\n                        callback(resp);\n                    },\n                    {Get,\"LoginFilter\"});\n</code></p>\n\n<p>While such interfaces look intuitive, they are not suitable for complex business logic scenarios. Assuming there are tens or even hundreds of handlers that need to be registered in the framework, isn’t it a better practice to implement them separately in their respective classes? So unless your logic is very simple, we don’t recommend using above interfaces. Instead, we can create an HttpSimpleController as follows:</p>\n\n<p>```c++\n/// The TestCtrl.h file\n#pragma once\n#include &lt;drogon/HttpSimpleController.h&gt;\nusing namespace drogon;\nclass TestCtrl:public drogon::HttpSimpleController<testctrl>\n{\npublic:\n    virtual void asyncHandleHttpRequest(const HttpRequestPtr&amp; req,const std::function&lt;void (const HttpResponsePtr &amp;)&gt; &amp; callback)override;\n    PATH_LIST_BEGIN\n    PATH_ADD(\"/test\",Get);\n    PATH_LIST_END\n};</testctrl></p>\n\n<p>/// The TestCtrl.cc file\n#include “TestCtrl.h”\nvoid TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr&amp; req,\n                                      const std::function&lt;void (const HttpResponsePtr &amp;)&gt; &amp; callback)\n{\n    //write your application logic here\n    auto resp = HttpResponse::newHttpResponse();\n    resp-&gt;setBody(“&lt;p&gt;Hello, world!&lt;/p&gt;”);\n    resp-&gt;setExpiredTime(0);\n    callback(resp);\n}\n```</p>\n\n<p><strong>Most of the above programs can be automatically generated by the command line tool <code>drogon_ctl</code> provided by drogon</strong> (The command is <code>drogon_ctl create controller TestCtrl</code>). All the user needs to do is add their own business logic. In the example, the controller returns a <code>Hello, world!</code> string when the client accesses the <code>http://ip/test</code> URL.</p>\n\n<p>For JSON format response, we create the controller as follows:</p>\n\n<p>```c++\n/// The header file\n#pragma once\n#include &lt;drogon/HttpSimpleController.h&gt;\nusing namespace drogon;\nclass JsonCtrl : public drogon::HttpSimpleController<jsonctrl>\n{\n  public:\n    void asyncHandleHttpRequest(const HttpRequestPtr &amp;req, const std::function&lt;void(const HttpResponsePtr &amp;)&gt; &amp;callback) override;\n    PATH_LIST_BEGIN\n    //list path definitions here;\n    PATH_ADD(\"/json\", Get);\n    PATH_LIST_END\n};</jsonctrl></p>\n\n<p>/// The source file\n#include “JsonCtrl.h”\nvoid JsonCtrl::asyncHandleHttpRequest(const HttpRequestPtr &amp;req,\n                                      const std::function&lt;void(const HttpResponsePtr &amp;)&gt; &amp;callback)\n{\n    Json::Value ret;\n    ret[“message”] = “Hello, World!”;\n    auto resp = HttpResponse::newHttpJsonResponse(ret);\n    callback(resp);\n}\n```</p>\n\n<p>Let’s go a step further and create a demo RESTful API with the HttpController class, as shown below (Omit the source file):</p>\n\n<p><code>c++\n/// The header file\n#pragma once\n#include &lt;drogon/HttpController.h&gt;\nusing namespace drogon;\nnamespace api\n{\nnamespace v1\n{\nclass User : public drogon::HttpController&lt;User&gt;\n{\n  public:\n    METHOD_LIST_BEGIN\n    //use METHOD_ADD to add your custom processing function here;\n    METHOD_ADD(User::getInfo, \"/{1}\", Get);                  //path is /api/v1/User/{arg1}\n    METHOD_ADD(User::getDetailInfo, \"/{1}/detailinfo\", Get);  //path is /api/v1/User/{arg1}/detailinfo\n    METHOD_ADD(User::newUser, \"/{1}\", Post);                 //path is /api/v1/User/{arg1}\n    METHOD_LIST_END\n    //your declaration of processing function maybe like this:\n    void getInfo(const HttpRequestPtr &amp;req, const std::function&lt;void(const HttpResponsePtr &amp;)&gt; &amp;callback, int userId) const;\n    void getDetailInfo(const HttpRequestPtr &amp;req, const std::function&lt;void(const HttpResponsePtr &amp;)&gt; &amp;callback, int userId) const;\n    void newUser(const HttpRequestPtr &amp;req, const std::function&lt;void(const HttpResponsePtr &amp;)&gt; &amp;callback, std::string &amp;&amp;userName);\n  public:\n    User()\n    {\n        LOG_DEBUG &lt;&lt; \"User constructor!\";\n    }\n};\n} // namespace v1\n} // namespace api\n</code></p>\n\n<p>As you can see, users can use the <code>HttpController</code> to map paths and parameters at the same time. This is a very convenient way to create a RESTful API application.</p>\n\n<p>In addition, you can also find that all handler interfaces are in asynchronous mode, where the response is returned by a callback object. This design is for performance reasons because in asynchronous mode the drogon application can handle a large number of concurrent requests with a small number of threads.</p>\n\n<p>After compiling all of the above source files, we get a very simple web application. This is a good start. <strong>for more information, please visit the <a href=\"https://github.com/an-tao/drogon/wiki\">wiki</a> site</strong></p>\n"
  },
  {
    "path": "lib/tests/integration_test/server/api_Attachment.cc",
    "content": "#include \"api_Attachment.h\"\n#include <fstream>\n\nusing namespace api;\n\n// add definition of your processing function here\nvoid Attachment::get(const HttpRequestPtr &req,\n                     std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    auto resp = HttpResponse::newHttpViewResponse(\"FileUpload\", HttpViewData());\n    callback(resp);\n}\n\nvoid Attachment::upload(const HttpRequestPtr &req,\n                        std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    MultiPartParser fileUpload;\n    if (fileUpload.parse(req) == 0)\n    {\n        // LOG_DEBUG << \"upload good!\";\n        auto &files = fileUpload.getFiles();\n        // LOG_DEBUG << \"file num=\" << files.size();\n        for (auto const &file : files)\n        {\n            LOG_DEBUG << \"file:\" << file.getFileName()\n                      << \"(extension=\" << file.getFileExtension()\n                      << \",type=\" << file.getFileType()\n                      << \",len=\" << file.fileLength()\n                      << \",md5=\" << file.getMd5() << \")\";\n            file.save();\n            file.save(\"123\");\n            file.saveAs(\"456/hehe\");\n            file.saveAs(\"456/7/8/9/\" + file.getMd5());\n            file.save(\"..\");\n            file.save(\".xx\");\n            file.saveAs(\"../xxx\");\n        }\n        Json::Value json;\n        json[\"result\"] = \"ok\";\n        for (auto &param : fileUpload.getParameters())\n        {\n            json[param.first] = param.second;\n        }\n        auto resp = HttpResponse::newHttpJsonResponse(json);\n        callback(resp);\n        return;\n    }\n    LOG_DEBUG << \"upload error!\";\n    // LOG_DEBUG << req->con\n    Json::Value json;\n    json[\"result\"] = \"failed\";\n    auto resp = HttpResponse::newHttpJsonResponse(json);\n    callback(resp);\n}\n\nvoid Attachment::uploadImage(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    MultiPartParser fileUpload;\n\n    // At this endpoint, we only accept one file\n    if (fileUpload.parse(req) == 0 && fileUpload.getFiles().size() == 1)\n    {\n        // LOG_DEBUG << \"upload image good!\";\n        Json::Value json;\n\n        // Get the first file received\n        auto &file = fileUpload.getFiles()[0];\n\n        // There are 2 ways to check if the file extension is an image.\n        // First way\n        if (file.getFileType() == FT_IMAGE)\n        {\n            json[\"isImage\"] = true;\n        }\n        // Second way\n        auto fileExtension = file.getFileExtension();\n        if (fileExtension == \"png\" || fileExtension == \"jpeg\" ||\n            fileExtension == \"jpg\" || fileExtension == \"ico\" /* || etc... */)\n        {\n            json[\"isImage\"] = true;\n        }\n        else\n        {\n            json[\"isImage\"] = false;\n        }\n\n        json[\"result\"] = \"ok\";\n        for (auto &param : fileUpload.getParameters())\n        {\n            json[param.first] = param.second;\n        }\n        auto resp = HttpResponse::newHttpJsonResponse(json);\n        callback(resp);\n        return;\n    }\n    LOG_DEBUG << \"upload image error!\";\n    // LOG_DEBUG << req->con\n    Json::Value json;\n    json[\"result\"] = \"failed\";\n    auto resp = HttpResponse::newHttpJsonResponse(json);\n    callback(resp);\n}\n\nvoid Attachment::download(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    auto resp = HttpResponse::newFileResponse(\"./drogon.jpg\", \"\", CT_IMAGE_JPG);\n    callback(resp);\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/api_Attachment.h",
    "content": "#pragma once\n#include <drogon/HttpController.h>\nusing namespace drogon;\n\nnamespace api\n{\nclass Attachment : public drogon::HttpController<Attachment>\n{\n  public:\n    METHOD_LIST_BEGIN\n    // use METHOD_ADD to add your custom processing function here;\n    METHOD_ADD(Attachment::get, \"\", Get);  // Path is '/api/attachment'\n    METHOD_ADD(Attachment::upload, \"/upload\", Post);\n    METHOD_ADD(Attachment::uploadImage, \"/uploadImage\", Post);\n    METHOD_ADD(Attachment::download, \"/download\", Get);\n    METHOD_LIST_END\n    // your declaration of processing function maybe like this:\n    void get(const HttpRequestPtr &req,\n             std::function<void(const HttpResponsePtr &)> &&callback);\n    void upload(const HttpRequestPtr &req,\n                std::function<void(const HttpResponsePtr &)> &&callback);\n    void uploadImage(const HttpRequestPtr &req,\n                     std::function<void(const HttpResponsePtr &)> &&callback);\n    void download(const HttpRequestPtr &req,\n                  std::function<void(const HttpResponsePtr &)> &&callback);\n};\n}  // namespace api\n"
  },
  {
    "path": "lib/tests/integration_test/server/api_v1_ApiTest.cc",
    "content": "#include \"api_v1_ApiTest.h\"\nusing namespace api::v1;\n\n// add definition of your processing function here\nvoid ApiTest::rootGet(const HttpRequestPtr &req,\n                      std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    auto res = HttpResponse::newHttpResponse();\n    res->setBody(\"ROOT Get!!!\");\n    callback(res);\n}\n\nvoid ApiTest::rootPost(const HttpRequestPtr &req,\n                       std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    std::thread([callback = std::move(callback)]() {\n        auto res = HttpResponse::newHttpResponse();\n        res->setBody(\"ROOT Post!!!\");\n        callback(res);\n    }).detach();\n}\n\nvoid ApiTest::get(const HttpRequestPtr &req,\n                  std::function<void(const HttpResponsePtr &)> &&callback,\n                  int p1,\n                  std::string &&p2)\n{\n    HttpViewData data;\n    data.insert(\"title\", std::string(\"ApiTest::get\"));\n    SafeStringMap<std::string> para;\n    para[\"p1\"] = std::to_string(p1);\n    para[\"p2\"] = p2;\n    data.insert(\"parameters\", para);\n    auto res = HttpResponse::newHttpViewResponse(\"ListParaView.csp\", data);\n    callback(res);\n}\n\nvoid ApiTest::your_method_name(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback,\n    double p1,\n    int p2) const\n{\n    LOG_WARN << req->matchedPathPatternData();\n    HttpViewData data;\n    data.insert(\"title\", std::string(\"ApiTest::get\"));\n    SafeStringMap<std::string> para;\n    para[\"p1\"] = std::to_string(p1);\n    para[\"p2\"] = std::to_string(p2);\n    para[\"p3\"] = HttpViewData::htmlTranslate(std::string_view(\n        \"<script>alert(\\\" This should not be displayed in a browser alert \"\n        \"box.\\\");</script>\"));\n    data.insert(\"parameters\", para);\n    auto res = HttpResponse::newHttpViewResponse(\"ListParaView\", data);\n    callback(res);\n}\n\nvoid ApiTest::staticApi(const HttpRequestPtr &req,\n                        std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setBody(\"staticApi,hello!!\");\n    resp->setExpiredTime(0);  // cache the response forever;\n    callback(resp);\n}\n\nvoid ApiTest::get2(const HttpRequestPtr &req,\n                   std::function<void(const HttpResponsePtr &)> &&callback,\n                   std::string &&p1)\n{\n    // test gzip feature\n    auto res = HttpResponse::newHttpResponse();\n    res->setBody(\n        \"Applications\\n\"\n        \"Developer\\n\"\n        \"Library\\n\"\n        \"Network\\n\"\n        \"System\\n\"\n        \"Users\\n\"\n        \"Volumes\\n\"\n        \"bin\\n\"\n        \"cores\\n\"\n        \"dev\\n\"\n        \"etc\\n\"\n        \"home\\n\"\n        \"installer.failurerequests\\n\"\n        \"net\\n\"\n        \"opt\\n\"\n        \"private\\n\"\n        \"sbin\\n\"\n        \"tmp\\n\"\n        \"usb\\n\"\n        \"usr\\n\"\n        \"var\\n\"\n        \"vm\\n\"\n        \"\\n\"\n        \"/Applications:\\n\"\n        \"Adobe\\n\"\n        \"Adobe Creative Cloud\\n\"\n        \"Adobe Photoshop CC\\n\"\n        \"AirPlayer Pro.app\\n\"\n        \"Android Studio.app\\n\"\n        \"App Store.app\\n\"\n        \"Autodesk\\n\"\n        \"Automator.app\\n\"\n        \"Axure RP Pro 7.0.app\\n\"\n        \"BaiduNetdisk_mac.app\\n\"\n        \"CLion.app\\n\"\n        \"Calculator.app\\n\"\n        \"Calendar.app\\n\"\n        \"Chess.app\\n\"\n        \"CleanApp.app\\n\"\n        \"Contacts.app\\n\"\n        \"DVD Player.app\\n\"\n        \"Dashboard.app\\n\"\n        \"Dictionary.app\\n\"\n        \"Docs for Xcode.app\\n\"\n        \"FaceTime.app\\n\"\n        \"FinalShell\\n\"\n        \"Firefox.app\\n\"\n        \"Folx.app\\n\"\n        \"Font Book.app\\n\"\n        \"GitHub.app\\n\"\n        \"Google Chrome.app\\n\"\n        \"Grammarly.app\\n\"\n        \"Image Capture.app\\n\"\n        \"Lantern.app\\n\"\n        \"Launchpad.app\\n\"\n        \"License.rtf\\n\"\n        \"MacPorts\\n\"\n        \"Mail.app\\n\"\n        \"Maps.app\\n\"\n        \"Messages.app\\n\"\n        \"Microsoft Excel.app\\n\"\n        \"Microsoft Office 2011\\n\"\n        \"Microsoft OneNote.app\\n\"\n        \"Microsoft Outlook.app\\n\"\n        \"Microsoft PowerPoint.app\\n\"\n        \"Microsoft Word.app\\n\"\n        \"Mindjet MindManager.app\\n\"\n        \"Mission Control.app\\n\"\n        \"Mockplus.app\\n\"\n        \"MyEclipse 2015\\n\"\n        \"Notes.app\\n\"\n        \"OmniGraffle.app\\n\"\n        \"Pages.app\\n\"\n        \"Photo Booth.app\\n\"\n        \"Photos.app\\n\"\n        \"Preview.app\\n\"\n        \"QJVPN.app\\n\"\n        \"QQ.app\\n\"\n        \"QuickTime Player.app\\n\"\n        \"RAR Extractor Lite.app\\n\"\n        \"Reminders.app\\n\"\n        \"Remote Desktop Connection.app\\n\"\n        \"Renee Undeleter.app\\n\"\n        \"Sabaki.app\\n\"\n        \"Safari.app\\n\"\n        \"ShadowsocksX.app\\n\"\n        \"Siri.app\\n\"\n        \"SogouInputPad.app\\n\"\n        \"Stickies.app\\n\"\n        \"System Preferences.app\\n\"\n        \"TeX\\n\"\n        \"Telegram.app\\n\"\n        \"Termius.app\\n\"\n        \"Tesumego - How to Make a Professional Go Player.app\\n\"\n        \"TextEdit.app\\n\"\n        \"Thunder.app\\n\"\n        \"Time Machine.app\\n\"\n        \"Tunnelblick.app\\n\"\n        \"Utilities\\n\"\n        \"VPN Shield.appdownload\\n\"\n        \"VirtualBox.app\\n\"\n        \"WeChat.app\\n\"\n        \"WinOnX2.app\\n\"\n        \"Wireshark.app\\n\"\n        \"Xcode.app\\n\"\n        \"Yose.app\\n\"\n        \"YoudaoNote.localized\\n\"\n        \"finalshelldata\\n\"\n        \"iBooks.app\\n\"\n        \"iPhoto.app\\n\"\n        \"iTools.app\\n\"\n        \"iTunes.app\\n\"\n        \"pgAdmin 4.app\\n\"\n        \"wechatwebdevtools.app\\n\"\n        \"\\n\"\n        \"/Applications/Adobe:\\n\"\n        \"Flash Player\\n\"\n        \"\\n\"\n        \"/Applications/Adobe/Flash Player:\\n\"\n        \"AddIns\\n\"\n        \"\\n\"\n        \"/Applications/Adobe/Flash Player/AddIns:\\n\"\n        \"airappinstaller\\n\"\n        \"\\n\"\n        \"/Applications/Adobe/Flash Player/AddIns/airappinstaller:\\n\"\n        \"airappinstaller\\n\"\n        \"digest.s\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Creative Cloud:\\n\"\n        \"Adobe Creative Cloud\\n\"\n        \"Icon\\n\"\n        \"Uninstall Adobe Creative Cloud\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC:\\n\"\n        \"Adobe Photoshop CC.app\\n\"\n        \"Configuration\\n\"\n        \"Icon\\n\"\n        \"Legal\\n\"\n        \"LegalNotices.pdf\\n\"\n        \"Locales\\n\"\n        \"Plug-ins\\n\"\n        \"Presets\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop CC.app:\\n\"\n        \"Contents\\n\"\n        \"Linguistics\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop CC.app/Contents:\\n\"\n        \"Application Data\\n\"\n        \"Frameworks\\n\"\n        \"Info.plist\\n\"\n        \"MacOS\\n\"\n        \"PkgInfo\\n\"\n        \"Required\\n\"\n        \"Resources\\n\"\n        \"_CodeSignature\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data:\\n\"\n        \"Custom File Info Panels\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data/Custom File Info Panels:\\n\"\n        \"4.0\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data/Custom File Info Panels/4.0:\\n\"\n        \"bin\\n\"\n        \"custom\\n\"\n        \"panels\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data/Custom File Info Panels/4.0/bin:\\n\"\n        \"FileInfoFoundation.swf\\n\"\n        \"FileInfoUI.swf\\n\"\n        \"framework.swf\\n\"\n        \"loc\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data/Custom File Info \"\n        \"Panels/4.0/bin/loc:\\n\"\n        \"FileInfo_ar_AE.dat\\n\"\n        \"FileInfo_bg_BG.dat\\n\"\n        \"FileInfo_cs_CZ.dat\\n\"\n        \"FileInfo_da_DK.dat\\n\"\n        \"FileInfo_de_DE.dat\\n\"\n        \"FileInfo_el_GR.dat\\n\"\n        \"FileInfo_en_US.dat\\n\"\n        \"FileInfo_es_ES.dat\\n\"\n        \"FileInfo_et_EE.dat\\n\"\n        \"FileInfo_fi_FI.dat\\n\"\n        \"FileInfo_fr_FR.dat\\n\"\n        \"FileInfo_he_IL.dat\\n\"\n        \"FileInfo_hr_HR.dat\\n\"\n        \"FileInfo_hu_HU.dat\\n\"\n        \"FileInfo_it_IT.dat\\n\"\n        \"FileInfo_ja_JP.dat\\n\"\n        \"FileInfo_ko_KR.dat\\n\"\n        \"FileInfo_lt_LT.dat\\n\"\n        \"FileInfo_lv_LV.dat\\n\"\n        \"FileInfo_nb_NO.dat\\n\"\n        \"FileInfo_nl_NL.dat\\n\"\n        \"FileInfo_pl_PL.dat\\n\"\n        \"FileInfo_pt_BR.dat\\n\"\n        \"FileInfo_ro_RO.dat\\n\"\n        \"FileInfo_ru_RU.dat\\n\"\n        \"FileInfo_sk_SK.dat\\n\"\n        \"FileInfo_sl_SI.dat\\n\"\n        \"FileInfo_sv_SE.dat\\n\"\n        \"FileInfo_tr_TR.dat\\n\"\n        \"FileInfo_uk_UA.dat\\n\"\n        \"FileInfo_zh_CN.dat\\n\"\n        \"FileInfo_zh_TW.dat\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data/Custom File Info Panels/4.0/custom:\\n\"\n        \"DICOM.xml\\n\"\n        \"Mobile.xml\\n\"\n        \"loc\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data/Custom File Info \"\n        \"Panels/4.0/custom/loc:\\n\"\n        \"DICOM_ar_AE.dat\\n\"\n        \"DICOM_bg_BG.dat\\n\"\n        \"DICOM_cs_CZ.dat\\n\"\n        \"DICOM_da_DK.dat\\n\"\n        \"DICOM_de_DE.dat\\n\"\n        \"DICOM_el_GR.dat\\n\"\n        \"DICOM_en_US.dat\\n\"\n        \"DICOM_es_ES.dat\\n\"\n        \"DICOM_et_EE.dat\\n\"\n        \"DICOM_fi_FI.dat\\n\"\n        \"DICOM_fr_FR.dat\\n\"\n        \"DICOM_he_IL.dat\\n\"\n        \"DICOM_hr_HR.dat\\n\"\n        \"DICOM_hu_HU.dat\\n\"\n        \"DICOM_it_IT.dat\\n\"\n        \"DICOM_ja_JP.dat\\n\"\n        \"DICOM_ko_KR.dat\\n\"\n        \"DICOM_lt_LT.dat\\n\"\n        \"DICOM_lv_LV.dat\\n\"\n        \"DICOM_nb_NO.dat\\n\"\n        \"DICOM_nl_NL.dat\\n\"\n        \"DICOM_pl_PL.dat\\n\"\n        \"DICOM_pt_BR.dat\\n\"\n        \"DICOM_ro_RO.dat\\n\"\n        \"DICOM_ru_RU.dat\\n\"\n        \"DICOM_sk_SK.dat\\n\"\n        \"DICOM_sl_SI.dat\\n\"\n        \"DICOM_sv_SE.dat\\n\"\n        \"DICOM_tr_TR.dat\\n\"\n        \"DICOM_uk_UA.dat\\n\"\n        \"DICOM_zh_CN.dat\\n\"\n        \"DICOM_zh_TW.dat\\n\"\n        \"Mobile_ar_AE.dat\\n\"\n        \"Mobile_bg_BG.dat\\n\"\n        \"Mobile_cs_CZ.dat\\n\"\n        \"Mobile_da_DK.dat\\n\"\n        \"Mobile_de_DE.dat\\n\"\n        \"Mobile_el_GR.dat\\n\"\n        \"Mobile_en_US.dat\\n\"\n        \"Mobile_es_ES.dat\\n\"\n        \"Mobile_et_EE.dat\\n\"\n        \"Mobile_fi_FI.dat\\n\"\n        \"Mobile_fr_FR.dat\\n\"\n        \"Mobile_he_IL.dat\\n\"\n        \"Mobile_hr_HR.dat\\n\"\n        \"Mobile_hu_HU.dat\\n\"\n        \"Mobile_it_IT.dat\\n\"\n        \"Mobile_ja_JP.dat\\n\"\n        \"Mobile_ko_KR.dat\\n\"\n        \"Mobile_lt_LT.dat\\n\"\n        \"Mobile_lv_LV.dat\\n\"\n        \"Mobile_nb_NO.dat\\n\"\n        \"Mobile_nl_NL.dat\\n\"\n        \"Mobile_pl_PL.dat\\n\"\n        \"Mobile_pt_BR.dat\\n\"\n        \"Mobile_ro_RO.dat\\n\"\n        \"Mobile_ru_RU.dat\\n\"\n        \"Mobile_sk_SK.dat\\n\"\n        \"Mobile_sl_SI.dat\\n\"\n        \"Mobile_sv_SE.dat\\n\"\n        \"Mobile_tr_TR.dat\\n\"\n        \"Mobile_uk_UA.dat\\n\"\n        \"Mobile_zh_CN.dat\\n\"\n        \"Mobile_zh_TW.dat\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data/Custom File Info Panels/4.0/panels:\\n\"\n        \"IPTC\\n\"\n        \"IPTCExt\\n\"\n        \"advanced\\n\"\n        \"audioData\\n\"\n        \"camera\\n\"\n        \"categories\\n\"\n        \"description\\n\"\n        \"dicom\\n\"\n        \"gpsData\\n\"\n        \"history\\n\"\n        \"mobile\\n\"\n        \"origin\\n\"\n        \"rawpacket\");\n    res->setExpiredTime(0);\n    callback(res);\n}\n\nvoid ApiTest::jsonTest(std::shared_ptr<Json::Value> &&json,\n                       std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    Json::Value ret;\n    if (json)\n    {\n        ret[\"result\"] = \"ok\";\n    }\n    else\n    {\n        ret[\"result\"] = \"bad\";\n    }\n    auto resp = HttpResponse::newCustomHttpResponse(ret);\n    callback(resp);\n}\n\nvoid ApiTest::formTest(const HttpRequestPtr &req,\n                       std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    auto parameters = req->getParameters();\n    Json::Value ret;\n    ret[\"k1\"] = parameters[\"k1\"];\n    ret[\"k2\"] = parameters[\"k2\"];\n    ret[\"k3\"] = parameters[\"k3\"];\n    if (parameters[\"k1\"] == \"1\" && parameters[\"k2\"] == \"安\" &&\n        parameters[\"k3\"] == \"test@example.com\")\n    {\n        ret[\"result\"] = \"ok\";\n    }\n    else\n    {\n        ret[\"result\"] = \"bad\";\n    }\n    auto resp = HttpResponse::newHttpJsonResponse(ret);\n    callback(resp);\n}\n\nvoid ApiTest::attributesTest(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    AttributesPtr attributes = req->getAttributes();\n    const std::string key = \"ATTR_ADDR\";\n    Json::Value ret;\n\n    uint64_t data = (uint64_t)req.get();\n\n    if (attributes->find(key))\n    {\n        ret[\"result\"] = \"bad\";\n        callback(HttpResponse::newHttpJsonResponse(ret));\n        return;\n    }\n\n    attributes->insert(key, data);\n\n    if (!attributes->find(key) || attributes->get<uint64_t>(key) != data)\n    {\n        ret[\"result\"] = \"bad\";\n    }\n    else\n    {\n        ret[\"result\"] = \"ok\";\n    }\n\n    callback(HttpResponse::newHttpJsonResponse(ret));\n    return;\n}\n\nvoid ApiTest::regexTest(const HttpRequestPtr &req,\n                        std::function<void(const HttpResponsePtr &)> &&callback,\n                        int p1,\n                        std::string &&p2)\n{\n    Json::Value ret;\n    ret[\"p1\"] = p1;\n    ret[\"p2\"] = std::move(p2);\n    auto resp = HttpResponse::newHttpJsonResponse(std::move(ret));\n    callback(resp);\n}\n\nstatic std::mutex cacheTestMtx;\n\nvoid ApiTest::cacheTest(const HttpRequestPtr &req,\n                        std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    std::unique_lock<std::mutex> lk(cacheTestMtx);\n    static size_t callCount = 0;\n\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setBody(std::to_string(callCount));\n    resp->setContentTypeCode(CT_TEXT_PLAIN);\n    // Expire after a millennia\n    resp->setExpiredTime(31536000000);\n    callback(resp);\n    callCount++;\n}\n\nstatic std::mutex cacheTest2Mtx;\n\nvoid ApiTest::cacheTest2(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    std::unique_lock<std::mutex> lk(cacheTest2Mtx);\n    static size_t callCount = 0;\n\n    auto resp = HttpResponse::newHttpResponse();\n    LOG_ERROR << callCount;\n    resp->setBody(std::to_string(callCount));\n    resp->setContentTypeCode(CT_TEXT_PLAIN);\n    // Expire after a millennia\n    if (callCount >= 2)\n        resp->setExpiredTime(31536000000);\n    callback(resp);\n    callCount++;\n}\n\nstatic std::mutex regexCacheApiMtx;\n\nvoid ApiTest::cacheTestRegex(\n    const HttpRequestPtr &req,\n    std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    std::unique_lock<std::mutex> lk(regexCacheApiMtx);\n    static size_t callCount = 0;\n\n    auto resp = HttpResponse::newHttpResponse();\n    LOG_ERROR << callCount;\n    resp->setBody(std::to_string(callCount));\n    resp->setContentTypeCode(CT_TEXT_PLAIN);\n    // Expire after a millennia\n    if (callCount >= 2)\n        resp->setExpiredTime(31536000000);\n    callback(resp);\n    callCount++;\n}\n\nvoid ApiTest::echoBody(const HttpRequestPtr &req,\n                       std::function<void(const HttpResponsePtr &)> &&callback)\n{\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setBody(std::string(req->body()));\n    callback(resp);\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/api_v1_ApiTest.h",
    "content": "#pragma once\n#include <drogon/HttpController.h>\nusing namespace drogon;\n\nnamespace api\n{\nnamespace v1\n{\nclass ApiTest : public drogon::HttpController<ApiTest>\n{\n  public:\n    METHOD_LIST_BEGIN\n    // use METHOD_ADD to add your custom processing function here;\n    METHOD_ADD(ApiTest::rootGet,\n               \"\",\n               Get,\n               Options,\n               \"drogon::LocalHostFilter\",\n               \"drogon::IntranetIpFilter\");\n    METHOD_ADD(ApiTest::rootPost, \"\", Post, Options);\n    METHOD_ADD(ApiTest::get,\n               \"/get/{2:p2}/{1:p1}\",\n               Get);  // path is /api/v1/apitest/get/{arg2}/{arg1}\n    METHOD_ADD(ApiTest::your_method_name,\n               \"/{PI}/List?P2={}\",\n               Get);  // path is /api/v1/apitest/{arg1}/list\n    METHOD_ADD(ApiTest::staticApi, \"/static\", Get, Options);  // CORS\n    METHOD_ADD(ApiTest::staticApi, \"/static\", Post, Put, Delete);\n    METHOD_ADD(ApiTest::get2,\n               \"/get/{}\",\n               Get);  // path is /api/v1/apitest/get/{arg1}\n    ADD_METHOD_TO(ApiTest::get2,\n                  \"/absolute/{}\",\n                  Get);  // path is /absolute/{arg1}\n    ADD_METHOD_TO(ApiTest::shutdown, \"/shutdown\",\n                  Get);  // path is /shutdown\n    METHOD_ADD(ApiTest::jsonTest, \"/json\", Post);\n    METHOD_ADD(ApiTest::formTest, \"/form\", Post);\n    METHOD_ADD(ApiTest::attributesTest, \"/attrs\", Get);\n    ADD_METHOD_VIA_REGEX(ApiTest::regexTest, \"/reg/([0-9]*)/(.*)\", Get);\n    METHOD_ADD(ApiTest::cacheTest, \"/cacheTest\", Get);\n    METHOD_ADD(ApiTest::cacheTest2, \"/cacheTest2\", Get);\n    ADD_METHOD_VIA_REGEX(ApiTest::cacheTestRegex,\n                         \"/cacheTestRegex/[a-y]+\",\n                         Get);\n    METHOD_ADD(ApiTest::echoBody, \"/echoBody\", Post);\n    METHOD_LIST_END\n\n    void get(const HttpRequestPtr &req,\n             std::function<void(const HttpResponsePtr &)> &&callback,\n             int p1,\n             std::string &&p2);\n    void your_method_name(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback,\n        double p1,\n        int p2) const;\n    void staticApi(const HttpRequestPtr &req,\n                   std::function<void(const HttpResponsePtr &)> &&callback);\n    void get2(const HttpRequestPtr &req,\n              std::function<void(const HttpResponsePtr &)> &&callback,\n              std::string &&p1);\n    void rootGet(const HttpRequestPtr &req,\n                 std::function<void(const HttpResponsePtr &)> &&callback);\n    void rootPost(const HttpRequestPtr &req,\n                  std::function<void(const HttpResponsePtr &)> &&callback);\n    void jsonTest(std::shared_ptr<Json::Value> &&json,\n                  std::function<void(const HttpResponsePtr &)> &&callback);\n    void formTest(const HttpRequestPtr &req,\n                  std::function<void(const HttpResponsePtr &)> &&callback);\n    void attributesTest(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback);\n    void regexTest(const HttpRequestPtr &req,\n                   std::function<void(const HttpResponsePtr &)> &&callback,\n                   int p1,\n                   std::string &&p2);\n\n    void shutdown(const HttpRequestPtr &req,\n                  std::function<void(const HttpResponsePtr &)> &&callback)\n    {\n        app().quit();\n    }\n\n    void cacheTest(const HttpRequestPtr &req,\n                   std::function<void(const HttpResponsePtr &)> &&callback);\n    void cacheTest2(const HttpRequestPtr &req,\n                    std::function<void(const HttpResponsePtr &)> &&callback);\n    void cacheTestRegex(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback);\n    void echoBody(const HttpRequestPtr &req,\n                  std::function<void(const HttpResponsePtr &)> &&callback);\n\n  public:\n    ApiTest()\n    {\n        LOG_DEBUG << \"ApiTest constructor!\";\n    }\n};\n}  // namespace v1\n}  // namespace api\n"
  },
  {
    "path": "lib/tests/integration_test/server/api_v1_CoroTest.cc",
    "content": "#include \"api_v1_CoroTest.h\"\nusing namespace api::v1;\n\nTask<> CoroTest::get(HttpRequestPtr req,\n                     std::function<void(const HttpResponsePtr &)> callback)\n{\n    // Force co_await to test awaiting works\n    co_await drogon::sleepCoro(\n        trantor::EventLoop::getEventLoopOfCurrentThread(),\n        std::chrono::milliseconds(100));\n\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setBody(\"DEADBEEF\");\n    callback(resp);\n    co_return;\n}\n\nTask<> CoroTest::get_with_param(\n    HttpRequestPtr req,\n    std::function<void(const HttpResponsePtr &)> callback,\n    std::string param)\n{\n    // Force co_await to test awaiting works\n    co_await drogon::sleepCoro(\n        trantor::EventLoop::getEventLoopOfCurrentThread(),\n        std::chrono::milliseconds(100));\n\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setBody(param);\n    callback(resp);\n    co_return;\n}\n\nTask<HttpResponsePtr> CoroTest::get_with_param2(HttpRequestPtr req,\n                                                std::string param)\n{\n    // Force co_await to test awaiting works\n    co_await drogon::sleepCoro(\n        trantor::EventLoop::getEventLoopOfCurrentThread(),\n        std::chrono::milliseconds(100));\n\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setBody(param);\n    co_return resp;\n}\n\nTask<HttpResponsePtr> CoroTest::get2(HttpRequestPtr req)\n{\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setBody(\"BADDBEEF\");\n    co_return resp;\n}\n\nTask<> CoroTest::this_will_fail(\n    HttpRequestPtr req,\n    std::function<void(const HttpResponsePtr &)> callback)\n{\n    throw std::runtime_error(\"This is an expected exception\");\n    callback(HttpResponse::newHttpResponse());\n}\n\nTask<HttpResponsePtr> CoroTest::this_will_fail2(HttpRequestPtr req)\n{\n    throw std::runtime_error(\"This is an expected exception\");\n    co_return HttpResponse::newHttpResponse();\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/api_v1_CoroTest.h",
    "content": "#pragma once\n#include <drogon/HttpController.h>\nusing namespace drogon;\n\nnamespace api\n{\nnamespace v1\n{\nclass CoroTest : public drogon::HttpController<CoroTest>\n{\n  public:\n    METHOD_LIST_BEGIN\n    METHOD_ADD(CoroTest::get, \"/get\", Get);\n    METHOD_ADD(CoroTest::get_with_param, \"/get_with_param/{name}\", Get);\n    METHOD_ADD(CoroTest::get_with_param2, \"/get_with_param2/{name}\", Get);\n    METHOD_ADD(CoroTest::get2, \"/get2\", Get);\n    METHOD_ADD(CoroTest::get2, \"/delay\", Get, \"CoroFilter\");\n    METHOD_ADD(CoroTest::this_will_fail, \"/this_will_fail\", Get);\n    METHOD_ADD(CoroTest::this_will_fail2, \"/this_will_fail2\", Get);\n    METHOD_LIST_END\n\n    Task<> get(HttpRequestPtr req,\n               std::function<void(const HttpResponsePtr &)> callback);\n    Task<> get_with_param(HttpRequestPtr req,\n                          std::function<void(const HttpResponsePtr &)> callback,\n                          std::string name);\n    Task<HttpResponsePtr> get_with_param2(HttpRequestPtr req, std::string name);\n    Task<HttpResponsePtr> get2(HttpRequestPtr req);\n    Task<> this_will_fail(\n        HttpRequestPtr req,\n        std::function<void(const HttpResponsePtr &)> callback);\n    Task<HttpResponsePtr> this_will_fail2(HttpRequestPtr req);\n};\n}  // namespace v1\n}  // namespace api\n"
  },
  {
    "path": "lib/tests/integration_test/server/header.csp",
    "content": "<img src=\"https://github.com/an-tao/drogon/wiki/images/drogon-white.jpg\"/>\n"
  },
  {
    "path": "lib/tests/integration_test/server/index.html",
    "content": "<p><img src=\"https://github.com/an-tao/drogon/wiki/images/drogon-white.jpg\" alt=\"\" /></p>\n\n<p><a href=\"https://travis-ci.com/an-tao/drogon\"><img src=\"https://travis-ci.com/an-tao/drogon.svg?branch=master\" alt=\"Build Status\" /></a>\n<a href=\"https://app.codacy.com/app/an-tao/drogon?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=an-tao/drogon&amp;utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://api.codacy.com/project/badge/Grade/45f8a65ca1844788b9109c0044a618f8\" alt=\"Codacy Badge\" /></a>\n<a href=\"https://lgtm.com/projects/g/an-tao/drogon/alerts/\"><img src=\"https://img.shields.io/lgtm/alerts/g/an-tao/drogon.svg?logo=lgtm&amp;logoWidth=18\" alt=\"Total alerts\" /></a>\n<a href=\"https://lgtm.com/projects/g/an-tao/drogon/context:cpp\"><img src=\"https://img.shields.io/lgtm/grade/cpp/g/an-tao/drogon.svg?logo=lgtm&amp;logoWidth=18\" alt=\"Language grade: C/C++\" /></a> \n<a href=\"https://gitter.im/drogon-web/community?utm_source=badge&amp;utm_medium=badge&amp;utm_campaign=pr-badge&amp;utm_content=badge\"><img src=\"https://badges.gitter.im/drogon-web/community.svg\" alt=\"Join the chat at https://gitter.im/drogon-web/community\" /></a>\n<a href=\"https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon\"><img src=\"https://img.shields.io/badge/Docker-image-blue.svg\" alt=\"Docker image\" /></a></p>\n\n<h3 id=\"overview\">Overview</h3>\n<p><strong>Drogon</strong> is a C++14/17-based HTTP application framework. Drogon can be used to easily build various types of web application server programs using C++. <strong>Drogon</strong> is the name of a dragon in the American TV series “Game of Thrones” that I really like.</p>\n\n<p>Drogon’s main application platform is Linux. It also supports Mac OS and FreeBSD. Currently, it does not support windows. Its main features are as follows:</p>\n\n<ul>\n  <li>Use a non-blocking I/O network lib based on epoll (kqueue under MacOS/FreeBSD) to provide high-concurrency, high-performance network IO, please visit the <a href=\"https://github.com/an-tao/drogon/wiki/benchmarks\">benchmarks</a> page for more details;</li>\n  <li>Provide a completely asynchronous programming mode;</li>\n  <li>Support Http1.0/1.1 (server side and client side);</li>\n  <li>Based on template, a simple reflection mechanism is implemented to completely decouple the main program framework, controllers and views.</li>\n  <li>Support cookies and built-in sessions;</li>\n  <li>Support back-end rendering, the controller generates the data to the view to generate the Html page, the view is described by a “JSP-like” CSP file, the C++ code is embedded into the Html page by the CSP tag, and the drogon command-line tool automatically generates the C++ code file for compilation;</li>\n  <li>Support view page dynamic loading (dynamic compilation and loading at runtime);</li>\n  <li>Provide a convenient and flexible routing solution from the path to the controller handler;</li>\n  <li>Support filter chains to facilitate the execution of unified logic (such as login verification, Http Method constraint verification, etc.) before controllers;</li>\n  <li>Support https (based on OpenSSL);</li>\n  <li>Support WebSocket (server side and client side);</li>\n  <li>Support JSON format request and response, very friendly to the Restful API application development;</li>\n  <li>Support file download and upload;</li>\n  <li>Support gzip compression transmission;</li>\n  <li>Support pipelining;</li>\n  <li>Provide a lightweight command line tool, drogon_ctl, to simplify the creation of various classes in Drogon and the generation of view code;</li>\n  <li>Support non-blocking I/O based asynchronously reading and writing database (PostgreSQL and MySQL(MariaDB) database);</li>\n  <li>Support asynchronously reading and writing sqlite3 database based on thread pool;</li>\n  <li>Support ARM Architecture;</li>\n  <li>Provide a convenient lightweight ORM implementation that supports for regular object-to-database bidirectional mapping;</li>\n  <li>Support plugins which can be installed by the configuration file at load time;</li>\n  <li>Support AOP with built-in joinpoints.</li>\n</ul>\n\n<h2 id=\"a-very-simple-example\">A very simple example</h2>\n\n<p>Unlike most C++ frameworks, the main program of the drogon application can be kept clean and simple. Drogon uses a few tricks to decouple controllers from the main program. The routing settings of controllers can be done through macros or configuration file.</p>\n\n<p>Below is the main program of a typical drogon application:</p>\n\n<p><code>c++\n#include &lt;drogon/drogon.h&gt;\nusing namespace drogon;\nint main()\n{\n    app().setLogPath(\"./\");\n    app().setLogLevel(trantor::Logger::kWarn);\n    app().addListener(\"0.0.0.0\", 80);\n    app().setThreadNum(16);\n    app().enableRunAsDaemon();\n    app().run();\n}\n</code></p>\n\n<p>It can be further simplified by using configuration file as follows:</p>\n\n<p><code>c++\n#include &lt;drogon/drogon.h&gt;\nusing namespace drogon;\nint main()\n{\n    app().loadConfigFile(\"./config.json\");\n    app().run();\n}\n</code></p>\n\n<p>Drogon provides some interfaces for adding controller logic directly in the main() function, for example, user can register a handler like this in Drogon:</p>\n\n<p><code>c++\napp.registerHandler(\"/test?username={1}\",\n                    [](const HttpRequestPtr&amp; req,\n                       const std::function&lt;void (const HttpResponsePtr &amp;)&gt; &amp; callback,\n                       const std::string &amp;name)\n                    {\n                        Json::Value json;\n                        json[\"result\"]=\"ok\";\n                        json[\"message\"]=std::string(\"hello,\")+name;\n                        auto resp=HttpResponse::newHttpJsonResponse(json);\n                        callback(resp);\n                    },\n                    {Get,\"LoginFilter\"});\n</code></p>\n\n<p>While such interfaces look intuitive, they are not suitable for complex business logic scenarios. Assuming there are tens or even hundreds of handlers that need to be registered in the framework, isn’t it a better practice to implement them separately in their respective classes? So unless your logic is very simple, we don’t recommend using above interfaces. Instead, we can create an HttpSimpleController as follows:</p>\n\n<p>```c++\n/// The TestCtrl.h file\n#pragma once\n#include &lt;drogon/HttpSimpleController.h&gt;\nusing namespace drogon;\nclass TestCtrl:public drogon::HttpSimpleController<testctrl>\n{\npublic:\n    virtual void asyncHandleHttpRequest(const HttpRequestPtr&amp; req,const std::function&lt;void (const HttpResponsePtr &amp;)&gt; &amp; callback)override;\n    PATH_LIST_BEGIN\n    PATH_ADD(\"/test\",Get);\n    PATH_LIST_END\n};</testctrl></p>\n\n<p>/// The TestCtrl.cc file\n#include “TestCtrl.h”\nvoid TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr&amp; req,\n                                      const std::function&lt;void (const HttpResponsePtr &amp;)&gt; &amp; callback)\n{\n    //write your application logic here\n    auto resp = HttpResponse::newHttpResponse();\n    resp-&gt;setBody(“&lt;p&gt;Hello, world!&lt;/p&gt;”);\n    resp-&gt;setExpiredTime(0);\n    callback(resp);\n}\n```</p>\n\n<p><strong>Most of the above programs can be automatically generated by the command line tool <code>drogon_ctl</code> provided by drogon</strong> (The command is <code>drogon_ctl create controller TestCtrl</code>). All the user needs to do is add their own business logic. In the example, the controller returns a <code>Hello, world!</code> string when the client accesses the <code>http://ip/test</code> URL.</p>\n\n<p>For JSON format response, we create the controller as follows:</p>\n\n<p>```c++\n/// The header file\n#pragma once\n#include &lt;drogon/HttpSimpleController.h&gt;\nusing namespace drogon;\nclass JsonCtrl : public drogon::HttpSimpleController<jsonctrl>\n{\n  public:\n    void asyncHandleHttpRequest(const HttpRequestPtr &amp;req, const std::function&lt;void(const HttpResponsePtr &amp;)&gt; &amp;callback) override;\n    PATH_LIST_BEGIN\n    //list path definitions here;\n    PATH_ADD(\"/json\", Get);\n    PATH_LIST_END\n};</jsonctrl></p>\n\n<p>/// The source file\n#include “JsonCtrl.h”\nvoid JsonCtrl::asyncHandleHttpRequest(const HttpRequestPtr &amp;req,\n                                      const std::function&lt;void(const HttpResponsePtr &amp;)&gt; &amp;callback)\n{\n    Json::Value ret;\n    ret[“message”] = “Hello, World!”;\n    auto resp = HttpResponse::newHttpJsonResponse(ret);\n    callback(resp);\n}\n```</p>\n\n<p>Let’s go a step further and create a demo RESTful API with the HttpController class, as shown below (Omit the source file):</p>\n\n<p><code>c++\n/// The header file\n#pragma once\n#include &lt;drogon/HttpController.h&gt;\nusing namespace drogon;\nnamespace api\n{\nnamespace v1\n{\nclass User : public drogon::HttpController&lt;User&gt;\n{\n  public:\n    METHOD_LIST_BEGIN\n    //use METHOD_ADD to add your custom processing function here;\n    METHOD_ADD(User::getInfo, \"/{1}\", Get);                  //path is /api/v1/User/{arg1}\n    METHOD_ADD(User::getDetailInfo, \"/{1}/detailinfo\", Get);  //path is /api/v1/User/{arg1}/detailinfo\n    METHOD_ADD(User::newUser, \"/{1}\", Post);                 //path is /api/v1/User/{arg1}\n    METHOD_LIST_END\n    //your declaration of processing function maybe like this:\n    void getInfo(const HttpRequestPtr &amp;req, const std::function&lt;void(const HttpResponsePtr &amp;)&gt; &amp;callback, int userId) const;\n    void getDetailInfo(const HttpRequestPtr &amp;req, const std::function&lt;void(const HttpResponsePtr &amp;)&gt; &amp;callback, int userId) const;\n    void newUser(const HttpRequestPtr &amp;req, const std::function&lt;void(const HttpResponsePtr &amp;)&gt; &amp;callback, std::string &amp;&amp;userName);\n  public:\n    User()\n    {\n        LOG_DEBUG &lt;&lt; \"User constructor!\";\n    }\n};\n} // namespace v1\n} // namespace api\n</code></p>\n\n<p>As you can see, users can use the <code>HttpController</code> to map paths and parameters at the same time. This is a very convenient way to create a RESTful API application.</p>\n\n<p>In addition, you can also find that all handler interfaces are in asynchronous mode, where the response is returned by a callback object. This design is for performance reasons because in asynchronous mode the drogon application can handle a large number of concurrent requests with a small number of threads.</p>\n\n<p>After compiling all of the above source files, we get a very simple web application. This is a good start. <strong>for more information, please visit the <a href=\"https://github.com/an-tao/drogon/wiki\">wiki</a> site</strong></p>\n"
  },
  {
    "path": "lib/tests/integration_test/server/main.cc",
    "content": "\n#include \"BeginAdviceTest.h\"\n#include \"CustomCtrl.h\"\n#include \"CustomHeaderFilter.h\"\n#include \"DigestAuthFilter.h\"\n\n#include <drogon/drogon.h>\n#include <iostream>\n#include <string>\n#include <vector>\n#include <string_view>\n\nusing namespace drogon;\nusing namespace std::chrono_literals;\n\nclass A : public DrObjectBase\n{\n  public:\n    void handle(const HttpRequestPtr &req,\n                std::function<void(const HttpResponsePtr &)> &&callback,\n                int p1,\n                const std::string &p2,\n                const std::string &p3,\n                int p4) const\n    {\n        HttpViewData data;\n        data.insert(\"title\", std::string(\"ApiTest::get\"));\n        SafeStringMap<std::string> para;\n        para[\"int p1\"] = std::to_string(p1);\n        para[\"string p2\"] = p2;\n        para[\"string p3\"] = p3;\n        para[\"int p4\"] = std::to_string(p4);\n\n        data.insert(\"parameters\", para);\n        auto res = HttpResponse::newHttpViewResponse(\"ListParaView\", data);\n        callback(res);\n    }\n\n    static void staticHandle(\n        const HttpRequestPtr &req,\n        std::function<void(const HttpResponsePtr &)> &&callback,\n        int p1,\n        const std::string &p2,\n        const std::string &p3,\n        int p4)\n    {\n        HttpViewData data;\n        data.insert(\"title\", std::string(\"ApiTest::get\"));\n        SafeStringMap<std::string> para;\n        para[\"int p1\"] = std::to_string(p1);\n        para[\"string p2\"] = p2;\n        para[\"string p3\"] = p3;\n        para[\"int p4\"] = std::to_string(p4);\n\n        data.insert(\"parameters\", para);\n        auto res = HttpResponse::newHttpViewResponse(\"ListParaView\", data);\n        callback(res);\n    }\n};\n\nclass B : public DrObjectBase\n{\n  public:\n    void operator()(const HttpRequestPtr &req,\n                    std::function<void(const HttpResponsePtr &)> &&callback,\n                    int p1,\n                    int p2)\n    {\n        HttpViewData data;\n        data.insert(\"title\", std::string(\"ApiTest::get\"));\n        SafeStringMap<std::string> para;\n        para[\"p1\"] = std::to_string(p1);\n        para[\"p2\"] = std::to_string(p2);\n        data.insert(\"parameters\", para);\n        auto res = HttpResponse::newHttpViewResponse(\"ListParaView\", data);\n        callback(res);\n    }\n};\n\nclass C : public drogon::HttpController<C>\n{\n  public:\n    METHOD_LIST_BEGIN\n    ADD_METHOD_TO(C::priv, \"/priv/resource\", Get, \"DigestAuthFilter\");\n\n    METHOD_LIST_END\n    void priv(const HttpRequestPtr &req,\n              std::function<void(const HttpResponsePtr &)> &&callback) const\n    {\n        auto resp = HttpResponse::newHttpResponse();\n        resp->setBody(\"<P>private content, only for authenticated users</P>\");\n        callback(resp);\n    }\n};\n\nnamespace api\n{\nnamespace v1\n{\nclass Test : public HttpController<Test>\n{\n  public:\n    METHOD_LIST_BEGIN\n    METHOD_ADD(Test::get,\n               \"get/{2}/{1}\",\n               Get);  // path is /api/v1/test/get/{arg2}/{arg1}\n    METHOD_ADD(Test::list,\n               \"/{2}/info\",\n               Get);  // path is /api/v1/test/{arg2}/info\n\n    METHOD_LIST_END\n    void get(const HttpRequestPtr &req,\n             std::function<void(const HttpResponsePtr &)> &&callback,\n             int p1,\n             int p2) const\n    {\n        HttpViewData data;\n        data.insert(\"title\", std::string(\"ApiTest::get\"));\n        SafeStringMap<std::string> para;\n        para[\"p1\"] = std::to_string(p1);\n        para[\"p2\"] = std::to_string(p2);\n        data.insert(\"parameters\", para);\n        auto res = HttpResponse::newHttpViewResponse(\"ListParaView\", data);\n        callback(res);\n    }\n\n    void list(const HttpRequestPtr &req,\n              std::function<void(const HttpResponsePtr &)> &&callback,\n              int p1,\n              int p2) const\n    {\n        HttpViewData data;\n        data.insert(\"title\", std::string(\"ApiTest::get\"));\n        SafeStringMap<std::string> para;\n        para[\"p1\"] = std::to_string(p1);\n        para[\"p2\"] = std::to_string(p2);\n        data.insert(\"parameters\", para);\n        auto res = HttpResponse::newHttpViewResponse(\"ListParaView\", data);\n        callback(res);\n    }\n};\n}  // namespace v1\n}  // namespace api\n\nusing namespace std::placeholders;\nusing namespace drogon;\n\nnamespace drogon\n{\ntemplate <>\nstd::string_view fromRequest(const HttpRequest &req)\n{\n    return req.body();\n}\n}  // namespace drogon\n\nclass Middleware4 : public drogon::HttpMiddleware<Middleware4, false>\n{\n  public:\n    Middleware4()\n    {\n        LOG_DEBUG << \"Middleware4\\n\";\n    };\n\n    void invoke(const HttpRequestPtr &req,\n                MiddlewareNextCallback &&nextCb,\n                MiddlewareCallback &&mcb) override\n    {\n        auto ptr = req->attributes()->get<std::shared_ptr<std::string>>(\n            \"test-middleware\");\n        ptr->append(\"4\");\n\n        nextCb([req, ptr, mcb = std::move(mcb)](const HttpResponsePtr &resp) {\n            ptr->append(\"4\");\n            resp->setBody(*ptr);\n            mcb(resp);\n        });\n    }\n};\n\n/// Some examples in the main function show some common functions of drogon. In\n/// practice, we don't need such a lengthy main function.\nint main()\n{\n    std::cout << banner << std::endl;\n    // app().addListener(\"::1\", 8848); //ipv6\n    app().addListener(\"0.0.0.0\", 8848);\n    // https\n    if (app().supportSSL())\n    {\n        drogon::app()\n            .setSSLFiles(\"server.crt\", \"server.key\")\n            .addListener(\"0.0.0.0\", 8849, true);\n    }\n    // Class function example\n    app().registerHandler(\"/api/v1/handle1/{}/{}/?p3={}&p4={}\", &A::handle);\n    app().registerHandler(\n        \"/api/v1/handle11/{int p1}/{string p2}/?p3={string p3}&p4={int p4}\",\n        &A::staticHandle);\n    // Lambda example\n    app().registerHandler(\n        \"/api/v1/handle2/{int a}/{float b}\",\n        [](const HttpRequestPtr &req,\n           std::function<void(const HttpResponsePtr &)> &&callback,\n           int a,    // here the `a` parameter is converted from the number 1\n                     // parameter in the path.\n           float b,  // here the `b` parameter is converted from the number 2\n                     // parameter in the path.\n           std::string_view &&body,  // here the `body` parameter is converted\n                                     // from req->as<string_view>();\n           const std::shared_ptr<Json::Value>\n               &jsonPtr  // here the `jsonPtr` parameter is converted from\n                         // req->as<std::shared_ptr<Json::Value>>();\n        ) {\n            HttpViewData data;\n            data.insert(\"title\", std::string(\"ApiTest::get\"));\n            SafeStringMap<std::string> para;\n            para[\"a\"] = std::to_string(a);\n            para[\"b\"] = std::to_string(b);\n            data.insert(\"parameters\", para);\n            auto res = HttpResponse::newHttpViewResponse(\"ListParaView\", data);\n            callback(res);\n            LOG_DEBUG << body.data();\n            assert(!jsonPtr);\n        });\n\n    // Functor example\n    B b;\n    app().registerHandler(\"/api/v1/handle3/{1}/{2}\", b);\n\n    // API example for std::function\n    A tmp;\n    std::function<void(const HttpRequestPtr &,\n                       std::function<void(const HttpResponsePtr &)> &&,\n                       int,\n                       const std::string &,\n                       const std::string &,\n                       int)>\n        func = std::bind(&A::handle, &tmp, _1, _2, _3, _4, _5, _6);\n    app().registerHandler(\"/api/v1/handle4/{4:p4}/{3:p3}/{1:p1}\", func);\n    app().registerHandler(\n        \"/api/v1/this_will_fail\",\n        [](const HttpRequestPtr &req,\n           std::function<void(const HttpResponsePtr &)> &&callback) {\n            throw std::runtime_error(\"this should fail\");\n        });\n\n    app().setDocumentRoot(\"./\");\n    app().enableSession(60);\n\n    std::map<std::string, std::string> config_credentials;\n    std::string realm(\"drogonRealm\");\n    std::string opaque(\"drogonOpaque\");\n    // Load configuration\n    app().loadConfigFile(\"config.example.json\");\n    app().setImplicitPageEnable(true);\n    app().setImplicitPage(\"page.html\");\n    auto &json = app().getCustomConfig();\n    if (json.empty())\n    {\n        std::cout << \"empty custom config!\" << std::endl;\n    }\n    else\n    {\n        if (!json[\"realm\"].empty())\n        {\n            realm = json[\"realm\"].asString();\n        }\n        if (!json[\"opaque\"].empty())\n        {\n            opaque = json[\"opaque\"].asString();\n        }\n        for (auto &&i : json[\"credentials\"])\n        {\n            config_credentials[i[\"user\"].asString()] = i[\"password\"].asString();\n        }\n    }\n\n    // Install Digest Authentication Filter using custom config credentials,\n    // used by C HttpController (/C/priv/resource)\n    auto auth_filter =\n        std::make_shared<DigestAuthFilter>(config_credentials, realm, opaque);\n    app().registerFilter(auth_filter);\n\n    // Install custom controller\n    auto ctrlPtr = std::make_shared<CustomCtrl>(\"Hi\");\n    app().registerController(ctrlPtr);\n\n    // Install custom filter\n    auto filterPtr =\n        std::make_shared<CustomHeaderFilter>(\"custom_header\", \"yes\");\n    app().registerFilter(filterPtr);\n    app().setIdleConnectionTimeout(30s);\n\n    // Install custom Middleware\n    auto middlewarePtr = std::make_shared<Middleware4>();\n    app().registerMiddleware(middlewarePtr);\n\n    // AOP example\n    app().registerBeginningAdvice(\n        []() { LOG_DEBUG << \"Event loop is running!\"; });\n    app().registerNewConnectionAdvice([](const trantor::InetAddress &peer,\n                                         const trantor::InetAddress &local) {\n        LOG_DEBUG << \"New connection: \" << peer.toIpPort() << \"-->\"\n                  << local.toIpPort();\n        return true;\n    });\n    app().registerPreRoutingAdvice([](const drogon::HttpRequestPtr &req,\n                                      drogon::AdviceCallback &&acb,\n                                      drogon::AdviceChainCallback &&accb) {\n        LOG_DEBUG << \"preRouting1\";\n        accb();\n    });\n    app().registerPostRoutingAdvice([](const drogon::HttpRequestPtr &req,\n                                       drogon::AdviceCallback &&acb,\n                                       drogon::AdviceChainCallback &&accb) {\n        LOG_DEBUG << \"postRouting1\";\n        LOG_DEBUG << \"Matched path=\" << req->matchedPathPatternData();\n        for (auto &cookie : req->cookies())\n        {\n            LOG_DEBUG << \"cookie: \" << cookie.first << \"=\" << cookie.second;\n        }\n        accb();\n    });\n    app().registerPreHandlingAdvice([](const drogon::HttpRequestPtr &req,\n                                       drogon::AdviceCallback &&acb,\n                                       drogon::AdviceChainCallback &&accb) {\n        LOG_DEBUG << \"preHandling1\";\n        accb();\n    });\n    app().registerPostHandlingAdvice([](const drogon::HttpRequestPtr &,\n                                        const drogon::HttpResponsePtr &resp) {\n        LOG_DEBUG << \"postHandling1\";\n        resp->addHeader(\"Access-Control-Allow-Origin\", \"*\");\n    });\n    app().registerPreRoutingAdvice([](const drogon::HttpRequestPtr &req) {\n        LOG_DEBUG << \"preRouting observer\";\n    });\n    app().registerPostRoutingAdvice([](const drogon::HttpRequestPtr &req) {\n        LOG_DEBUG << \"postRouting observer\";\n    });\n    app().registerPreHandlingAdvice([](const drogon::HttpRequestPtr &req) {\n        LOG_DEBUG << \"preHanding observer\";\n    });\n    app().registerSyncAdvice([](const HttpRequestPtr &req) -> HttpResponsePtr {\n        static const HttpResponsePtr nullResp;\n        if (req->path() == \"/plaintext\")\n        {\n            auto resp = HttpResponse::newHttpResponse();\n            resp->setBody(\"Hello, World!\");\n            resp->setContentTypeCodeAndCustomString(\n                CT_TEXT_PLAIN, \"content-type: text/plain\\r\\n\");\n            return resp;\n        }\n        return nullResp;\n    });\n    app().registerSessionStartAdvice([](const std::string &sessionId) {\n        LOG_DEBUG << \"session start:\" << sessionId;\n    });\n    app().registerSessionDestroyAdvice([](const std::string &sessionId) {\n        LOG_DEBUG << \"session destroy:\" << sessionId;\n    });\n    // Output information of all handlers\n    auto handlerInfo = app().getHandlersInfo();\n    for (auto &info : handlerInfo)\n    {\n        std::cout << std::get<0>(info);\n        switch (std::get<1>(info))\n        {\n            case Get:\n                std::cout << \" (GET) \";\n                break;\n            case Post:\n                std::cout << \" (POST) \";\n                break;\n            case Delete:\n                std::cout << \" (DELETE) \";\n                break;\n            case Put:\n                std::cout << \" (PUT) \";\n                break;\n            case Options:\n                std::cout << \" (OPTIONS) \";\n                break;\n            case Head:\n                std::cout << \" (Head) \";\n                break;\n            case Patch:\n                std::cout << \" (PATCH) \";\n                break;\n            default:\n                break;\n        }\n        std::cout << std::get<2>(info) << std::endl;\n    }\n    auto resp = HttpResponse::newFileResponse(\"index.html\");\n    resp->setExpiredTime(0);\n    app().setCustom404Page(resp);\n    app().addListener(\"0.0.0.0\", 0);\n    app().enableCompressedRequest(true);\n    app().registerBeginningAdvice([]() {\n        auto addresses = app().getListeners();\n        for (auto &address : addresses)\n        {\n            LOG_INFO << address.toIpPort() << \" LISTEN\";\n        }\n    });\n    app().registerCustomExtensionMime(\"md\", \"text/markdown\");\n    app().setFileTypes({\"md\", \"html\", \"jpg\", \"cc\", \"txt\"});\n    std::cout << \"Date: \"\n              << drogon::utils::getHttpFullDateStr(trantor::Date::now())\n              << std::endl;\n\n    app().registerBeginningAdvice(\n        []() { BeginAdviceTest::setContent(\"DrogonReady\"); });\n\n    app().run();\n}\n"
  },
  {
    "path": "lib/tests/integration_test/server/test.md",
    "content": "# Test\n\nThis is an example of a Markdown file.\n\n\n"
  },
  {
    "path": "lib/tests/integration_test/server/中文.txt",
    "content": "三點一四一五九二\n"
  },
  {
    "path": "lib/tests/main_CookieSameSite.cc",
    "content": "#define DROGON_TEST_MAIN\n#include <drogon/drogon_test.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/HttpController.h>\n#include <drogon/Cookie.h>\n\n#include <trantor/utils/Logger.h>\n\nusing namespace drogon;\nusing namespace trantor;\n\nclass CookieSameSiteController\n    : public drogon::HttpController<CookieSameSiteController>\n{\n  public:\n    static const char *SESSION_SAME_SITE;\n\n    METHOD_LIST_BEGIN\n    METHOD_ADD(CookieSameSiteController::set, \"/set/{newSameSite}\", Get);\n    METHOD_LIST_END\n\n    void set(const HttpRequestPtr &req,\n             std::function<void(const HttpResponsePtr &)> &&callback,\n             std::string &&newSameSite)\n    {\n        std::string old_session_same_site = \"Null\";\n        if (req->session()->find(SESSION_SAME_SITE))\n        {\n            old_session_same_site =\n                req->session()->get<std::string>(SESSION_SAME_SITE);\n        }\n        LOG_INFO << \"Server: new sameSite == \" << newSameSite\n                 << \", old sameSite == \" << old_session_same_site;\n        drogon::HttpAppFramework::instance().enableSession(\n            0, Cookie::convertString2SameSite(newSameSite));\n\n        req->session()->modify<std::string>(\n            SESSION_SAME_SITE,\n            [newSameSite](std::string &sameSite) { sameSite = newSameSite; });\n        req->session()->changeSessionIdToClient();\n\n        Json::Value json;\n        json[\"result\"] = \"ok\";\n        json[\"old value\"] = old_session_same_site;\n        json[\"new value\"] = newSameSite;\n\n        auto resp = HttpResponse::newHttpJsonResponse(std::move(json));\n        callback(resp);\n    }\n};\n\nconst char *CookieSameSiteController::SESSION_SAME_SITE{\"session_same_site\"};\n\n// -- main\nint main(int argc, char **argv)\n{\n    trantor::Logger::setLogLevel(trantor::Logger::kInfo);\n    std::promise<void> p1;\n    std::future<void> f1 = p1.get_future();\n\n    std::thread thr([&]() {\n        app()\n            .setSSLFiles(\"server.crt\", \"server.key\")\n            .addListener(\"0.0.0.0\", 8855, true)\n            .enableSession();\n        app().getLoop()->queueInLoop([&p1]() { p1.set_value(); });\n        app().run();\n    });\n\n    f1.get();\n    std::this_thread::sleep_for(std::chrono::milliseconds(200));\n    int testStatus = test::run(argc, argv);\n    app().getLoop()->queueInLoop([]() { app().quit(); });\n    thr.join();\n    return testStatus;\n}\n"
  },
  {
    "path": "lib/tests/unittests/Base64Test.cc",
    "content": "#include <drogon/utils/Utilities.h>\n#include <drogon/drogon_test.h>\n#include <string>\n\nDROGON_TEST(Base64)\n{\n    std::string in{\"drogon framework\"};\n    auto encoded = drogon::utils::base64Encode(in);\n    auto decoded = drogon::utils::base64Decode(encoded);\n    CHECK(encoded == \"ZHJvZ29uIGZyYW1ld29yaw==\");\n    CHECK(decoded == in);\n\n    SUBSECTION(InvalidChars)\n    {\n        auto decoded =\n            drogon::utils::base64Decode(\"ZHJvZ2*9uIGZy**YW1ld2***9yaw*=*=\");\n        CHECK(decoded == in);\n    }\n\n    SUBSECTION(InvalidCharsNoPadding)\n    {\n        auto decoded =\n            drogon::utils::base64Decode(\"ZHJvZ2*9uIGZy**YW1ld2***9yaw**\");\n        CHECK(decoded == in);\n    }\n\n    SUBSECTION(Unpadded)\n    {\n        std::string in{\"drogon framework\"};\n        auto encoded = drogon::utils::base64EncodeUnpadded(in);\n        auto decoded = drogon::utils::base64Decode(encoded);\n        CHECK(encoded == \"ZHJvZ29uIGZyYW1ld29yaw\");\n        CHECK(decoded == in);\n    }\n\n    SUBSECTION(LongString)\n    {\n        std::string in;\n        in.reserve(100000);\n        for (int i = 0; i < 100000; ++i)\n        {\n            in.append(1, char(i));\n        }\n        auto out = drogon::utils::base64Encode(in);\n        auto out2 = drogon::utils::base64Decode(out);\n        auto encoded = drogon::utils::base64Encode(in);\n        auto decoded = drogon::utils::base64Decode(encoded);\n        CHECK(decoded == in);\n    }\n\n    SUBSECTION(URLSafe)\n    {\n        std::string in{\"drogon framework\"};\n        auto encoded = drogon::utils::base64Encode(in, true);\n        auto decoded = drogon::utils::base64Decode(encoded);\n        CHECK(encoded == \"ZHJvZ29uIGZyYW1ld29yaw==\");\n        CHECK(decoded == in);\n    }\n\n    SUBSECTION(UnpaddedURLSafe)\n    {\n        std::string in{\"drogon framework\"};\n        auto encoded = drogon::utils::base64EncodeUnpadded(in, true);\n        auto decoded = drogon::utils::base64Decode(encoded);\n        CHECK(encoded == \"ZHJvZ29uIGZyYW1ld29yaw\");\n        CHECK(decoded == in);\n    }\n\n    SUBSECTION(LongURLSafe)\n    {\n        std::string in;\n        in.reserve(100000);\n        for (int i = 0; i < 100000; ++i)\n        {\n            in.append(1, char(i));\n        }\n        auto encoded = drogon::utils::base64Encode(in, true);\n        auto decoded = drogon::utils::base64Decode(encoded);\n        CHECK(decoded == in);\n    }\n}\n"
  },
  {
    "path": "lib/tests/unittests/BrotliTest.cc",
    "content": "#include <drogon/utils/Utilities.h>\n#include <drogon/drogon_test.h>\n#include <string>\n#include <iostream>\nusing namespace drogon::utils;\n\nDROGON_TEST(BrotliTest)\n{\n    SUBSECTION(shortText)\n    {\n        std::string source{\"123中文顶替要枯械\"};\n        auto compressed = brotliCompress(source.data(), source.length());\n        auto decompressed =\n            brotliDecompress(compressed.data(), compressed.length());\n        CHECK(source == decompressed);\n    }\n\n    SUBSECTION(longText)\n    {\n        std::string source;\n        for (size_t i = 0; i < 100000; i++)\n        {\n            source.append(std::to_string(i));\n        }\n        auto compressed = brotliCompress(source.data(), source.length());\n        auto decompressed =\n            brotliDecompress(compressed.data(), compressed.length());\n        CHECK(source == decompressed);\n    }\n}\n"
  },
  {
    "path": "lib/tests/unittests/CacheMapTest.cc",
    "content": "#include <drogon/drogon_test.h>\n#include <drogon/CacheMap.h>\n#include <drogon/HttpAppFramework.h>\n#include <trantor/net/EventLoopThread.h>\n\n#include <chrono>\n\nusing namespace drogon;\nusing namespace std::chrono_literals;\n\nDROGON_TEST(CacheMapTest)\n{\n    trantor::EventLoopThread loopThread;\n    loopThread.run();\n    drogon::CacheMap<std::string, std::string> cache(loopThread.getLoop(),\n                                                     0.1f,\n                                                     4,\n                                                     30);\n\n    for (size_t i = 1; i < 40; i++)\n        cache.insert(std::to_string(i), \"a\", i);\n    cache.insert(\"bla\", \"\");\n    cache.insert(\"zzz\", \"-\");\n    std::this_thread::sleep_for(3s);\n    CHECK(cache.find(\"0\") == false);  // doesn't exist\n    CHECK(cache.find(\"1\") == false);  // timeout\n    CHECK(cache.find(\"15\") == true);\n    CHECK(cache.find(\"bla\") == true);\n\n    cache.erase(\"30\");\n    CHECK(cache.find(\"30\") == false);\n\n    cache.modify(\"bla\", [](std::string &s) { s = \"asd\"; });\n    CHECK(cache[\"bla\"] == \"asd\");\n\n    std::string content;\n    cache.findAndFetch(\"zzz\", content);\n    CHECK(content == \"-\");\n}\n"
  },
  {
    "path": "lib/tests/unittests/ClassNameTest.cc",
    "content": "#include <drogon/drogon_test.h>\n\nnamespace api\n{\nnamespace v1\n{\ntemplate <typename T>\nclass handler : public drogon::DrObject<T>\n{\n  public:\n    static std::string name()\n    {\n        return handler<T>::classTypeName();\n    }\n};\n\nclass hh : public handler<hh>\n{\n};\n}  // namespace v1\n}  // namespace api\n\nDROGON_TEST(ClassName)\n{\n    api::v1::hh h;\n    CHECK(h.className() == \"api::v1::hh\");\n    CHECK(api::v1::hh::classTypeName() == \"api::v1::hh\");\n    CHECK(h.name() == \"api::v1::hh\");\n}\n"
  },
  {
    "path": "lib/tests/unittests/ControllerCreationTest.cc",
    "content": "#include <drogon/HttpController.h>\n#include <drogon/HttpSimpleController.h>\n#include <drogon/WebSocketController.h>\n#include <drogon/drogon_test.h>\n\nclass Ctrl : public drogon::HttpController<Ctrl, false>\n{\n  public:\n    static void initPathRouting()\n    {\n        created = true;\n    };\n\n    static bool created;\n};\n\nclass SimpleCtrl : public drogon::HttpController<Ctrl, false>\n{\n  public:\n    static void initPathRouting()\n    {\n        created = true;\n    };\n\n    static bool created;\n};\n\nclass WsCtrl : public drogon::WebSocketController<WsCtrl, false>\n{\n  public:\n    static void initPathRouting()\n    {\n        created = true;\n    };\n\n    static bool created;\n};\n\nbool Ctrl::created = false;\nbool SimpleCtrl::created = false;\nbool WsCtrl::created = false;\n\nDROGON_TEST(ControllerCreation)\n{\n    REQUIRE(Ctrl::created == false);\n    REQUIRE(SimpleCtrl::created == false);\n    REQUIRE(WsCtrl::created == false);\n}\n"
  },
  {
    "path": "lib/tests/unittests/CookieTest.cc",
    "content": "#include <drogon/Cookie.h>\n#include <drogon/drogon_test.h>\n\nDROGON_TEST(CookieTest)\n{\n    drogon::Cookie cookie1(\"test\", \"1\");\n    CHECK(cookie1.cookieString() == \"Set-Cookie: test=1; HttpOnly\\r\\n\");\n\n    drogon::Cookie cookie2(\"test\", \"2\");\n    cookie2.setSecure(true);\n    CHECK(cookie2.cookieString() == \"Set-Cookie: test=2; Secure; HttpOnly\\r\\n\");\n\n    drogon::Cookie cookie3(\"test\", \"3\");\n    cookie3.setDomain(\"drogon.org\");\n    cookie3.setExpiresDate(trantor::Date(1621561557000000L));\n    CHECK(cookie3.cookieString() ==\n          \"Set-Cookie: test=3; Expires=Fri, 21 May 2021 01:45:57 GMT; \"\n          \"Domain=drogon.org; HttpOnly\\r\\n\");\n\n    drogon::Cookie cookie4(\"test\", \"4\");\n    cookie4.setMaxAge(3600);\n    cookie4.setSameSite(drogon::Cookie::SameSite::kStrict);\n    CHECK(cookie4.cookieString() ==\n          \"Set-Cookie: test=4; Max-Age=3600; \"\n          \"SameSite=Strict; HttpOnly\\r\\n\");\n    drogon::Cookie cookie5(\"test\", \"5\");\n    cookie5.setSameSite(drogon::Cookie::SameSite::kNone);\n    CHECK(cookie5.cookieString() ==\n          \"Set-Cookie: test=5; \"\n          \"SameSite=None; Secure; HttpOnly\\r\\n\");\n\n    CHECK(drogon::Cookie::SameSite::kLax ==\n          drogon::Cookie::convertString2SameSite(\"Lax\"));\n    CHECK(drogon::Cookie::SameSite::kStrict ==\n          drogon::Cookie::convertString2SameSite(\"Strict\"));\n    CHECK(drogon::Cookie::SameSite::kNone ==\n          drogon::Cookie::convertString2SameSite(\"None\"));\n\n    // Test for Partitioned attribute\n    drogon::Cookie cookie6(\"test\", \"6\");\n    cookie6.setPartitioned(true);\n    CHECK(cookie6.cookieString() ==\n          \"Set-Cookie: test=6; Secure; HttpOnly; Partitioned\\r\\n\");\n    // Test that partitioned attribute  automatically sets secure\n    drogon::Cookie cookie7(\"test\", \"7\");\n    cookie7.setPartitioned(true);\n    CHECK(cookie7.isSecure() == true);\n    // Test other attributes\n    drogon::Cookie cookie8(\"test\", \"8\");\n    cookie8.setPartitioned(true);\n    cookie8.setDomain(\"drogon.org\");\n    cookie8.setMaxAge(3600);\n    CHECK(cookie8.cookieString() ==\n          \"Set-Cookie: test=8; Max-Age=3600; Domain=drogon.org; Secure; \"\n          \"HttpOnly; Partitioned\\r\\n\");\n    // Teset Partitioned and SameSite can coexist\n    drogon::Cookie cookie9(\"test\", \"9\");\n    cookie9.setPartitioned(true);\n    cookie9.setSameSite(drogon::Cookie::SameSite::kLax);\n    CHECK(\n        cookie9.cookieString() ==\n        \"Set-Cookie: test=9; SameSite=Lax; Secure; HttpOnly; Partitioned\\r\\n\");\n}\n"
  },
  {
    "path": "lib/tests/unittests/CoroutineTest.cc",
    "content": "#include <drogon/drogon_test.h>\n#include <drogon/utils/coroutine.h>\n#include <drogon/HttpAppFramework.h>\n#include <trantor/net/EventLoopThread.h>\n#include <trantor/net/EventLoopThreadPool.h>\n#include <atomic>\n#include <chrono>\n#include <cstdint>\n#include <exception>\n#include <future>\n#include <memory>\n#include <mutex>\n#include <optional>\n#include <type_traits>\n\nusing namespace drogon;\n\nnamespace drogon::internal\n{\nstruct SomeStruct\n{\n    ~SomeStruct()\n    {\n        beenDestructed = true;\n    }\n\n    static bool beenDestructed;\n};\n\nbool SomeStruct::beenDestructed = false;\n\nstruct StructAwaiter : public CallbackAwaiter<std::shared_ptr<SomeStruct>>\n{\n    void await_suspend(std::coroutine_handle<> handle)\n    {\n        setValue(std::make_shared<SomeStruct>());\n        handle.resume();\n    }\n};\n\n}  // namespace drogon::internal\n\n// Workaround limitation of macros\ntemplate <typename T>\nusing is_int = std::is_same<T, int>;\ntemplate <typename T>\nusing is_void = std::is_same<T, void>;\n\nDROGON_TEST(CroutineBasics)\n{\n    // Basic checks making sure coroutine works as expected\n    STATIC_REQUIRE(is_awaitable_v<Task<>>);\n    STATIC_REQUIRE(is_awaitable_v<Task<int>>);\n    STATIC_REQUIRE(is_awaitable_v<Task<>>);\n    STATIC_REQUIRE(is_awaitable_v<Task<int>>);\n    STATIC_REQUIRE(is_int<await_result_t<Task<int>>>::value);\n    STATIC_REQUIRE(is_void<await_result_t<Task<>>>::value);\n\n    // No, you cannot await AsyncTask. By design\n    STATIC_REQUIRE(is_awaitable_v<AsyncTask> == false);\n\n    // AsyncTask should execute eagerly\n    int m = 0;\n    [&m]() -> AsyncTask {\n        m = 1;\n        co_return;\n    }();\n    REQUIRE(m == 1);\n\n    // Make sure sync_wait works\n    CHECK(sync_wait([]() -> Task<int> { co_return 1; }()) == 1);\n\n    // make sure it does affect the outside world\n    int n = 0;\n    sync_wait([&]() -> Task<> {\n        n = 1;\n        co_return;\n    }());\n    CHECK(n == 1);\n\n    // Testing that exceptions can propagate through coroutines\n    auto throw_in_task = [TEST_CTX]() -> Task<> {\n        auto f = []() -> Task<> { throw std::runtime_error(\"test error\"); };\n\n        CHECK_THROWS_AS(co_await f(), std::runtime_error);\n    };\n    sync_wait(throw_in_task());\n\n    // Test sync_wait propagates exception\n    auto throws = []() -> Task<> {\n        throw std::runtime_error(\"bla\");\n        co_return;\n    };\n    CHECK_THROWS_AS(sync_wait(throws()), std::runtime_error);\n\n    // Test co_return non-copyable object works\n    auto return_unique_ptr = [TEST_CTX]() -> Task<std::unique_ptr<int>> {\n        co_return std::make_unique<int>(42);\n    };\n    CHECK(*sync_wait(return_unique_ptr()) == 42);\n\n    // Test co_awaiting non-copyable object works\n    auto await_non_copyable = [TEST_CTX]() -> Task<> {\n        auto return_unique_ptr = []() -> Task<std::unique_ptr<int>> {\n            co_return std::make_unique<int>(123);\n        };\n        auto ptr = co_await return_unique_ptr();\n        CHECK(*ptr == 123);\n    };\n    sync_wait(await_non_copyable());\n\n    // This only works because async_run tries to run the coroutine as soon as\n    // possible and the coroutine does not wait\n    int testVar = 0;\n    async_run([&testVar]() -> Task<void> {\n        testVar = 1;\n        co_return;\n    });\n    CHECK(testVar == 1);\n    async_run([TEST_CTX]() -> Task<void> {\n        auto val =\n            co_await queueInLoopCoro<int>(app().getLoop(), []() { return 42; });\n        CHECK(val == 42);\n    });\n    async_run([TEST_CTX]() -> Task<void> {\n        co_await queueInLoopCoro<void>(app().getLoop(), []() { LOG_DEBUG; });\n    });\n}\n\nDROGON_TEST(CompilcatedCoroutineLifetime)\n{\n    auto coro = []() -> Task<Task<std::string>> {\n        auto coro2 = []() -> Task<std::string> {\n            auto coro3 = []() -> Task<std::string> {\n                co_return std::string(\"Hello, World!\");\n            };\n            auto coro4 = [coro3 = std::move(coro3)]() -> Task<std::string> {\n                auto coro5 = []() -> Task<> { co_return; };\n                co_await coro5();\n                co_return co_await coro3();\n            };\n            co_return co_await coro4();\n        };\n\n        co_return coro2();\n    };\n\n    auto task1 = coro();\n    auto task2 = sync_wait(task1);\n    std::string str = sync_wait(task2);\n\n    CHECK(str == \"Hello, World!\");\n}\n\nDROGON_TEST(CoroutineDestruction)\n{\n    // Test coroutine destruction\n    auto destruct = []() -> Task<> {\n        auto awaitStruct = []() -> Task<std::shared_ptr<internal::SomeStruct>> {\n            co_return co_await internal::StructAwaiter();\n        };\n\n        auto awaitNothing = [awaitStruct]() -> Task<> {\n            co_await awaitStruct();\n        };\n\n        co_await awaitNothing();\n    };\n    sync_wait(destruct());\n    CHECK(internal::SomeStruct::beenDestructed == true);\n}\n\nDROGON_TEST(AsyncWaitLifetime)\n{\n    app().getLoop()->queueInLoop([TEST_CTX]() {\n        async_run([TEST_CTX]() -> Task<> {\n            auto ptr = std::make_shared<std::string>(\"test\");\n            CHECK(ptr.use_count() == 1);\n            co_await sleepCoro(drogon::app().getLoop(), 0.01);\n            CHECK(ptr.use_count() == 1);\n        });\n    });\n\n    app().getLoop()->queueInLoop([TEST_CTX]() {\n        auto ptr = std::make_shared<std::string>(\"test\");\n        async_run([ptr, TEST_CTX]() -> Task<> {\n            CHECK(ptr.use_count() == 2);\n            co_await sleepCoro(drogon::app().getLoop(), 0.01);\n            CHECK(ptr.use_count() == 1);\n        });\n    });\n\n    auto ptr = std::make_shared<std::string>(\"test\");\n    app().getLoop()->queueInLoop([ptr, TEST_CTX]() {\n        async_run([ptr, TEST_CTX]() -> Task<> {\n            co_await sleepCoro(drogon::app().getLoop(), 0.01);\n            CHECK(ptr.use_count() == 1);\n        });\n    });\n\n    auto ptr2 = std::make_shared<std::string>(\"test\");\n    app().getLoop()->queueInLoop(async_func([ptr2, TEST_CTX]() -> Task<> {\n        co_await sleepCoro(drogon::app().getLoop(), 0.01);\n        CHECK(ptr2.use_count() == 1);\n    }));\n}\n\nDROGON_TEST(SwitchThread)\n{\n    trantor::EventLoopThread thread;\n    thread.getLoop()->setIndex(12345);\n    thread.run();\n\n    auto switch_thread = [TEST_CTX, &thread]() -> Task<> {\n        co_await switchThreadCoro(thread.getLoop());\n        auto currentLoop = trantor::EventLoop::getEventLoopOfCurrentThread();\n        MANDATE(currentLoop != nullptr);\n        CHECK(currentLoop->index() == 12345);\n        currentLoop->quit();\n    };\n    sync_wait(switch_thread());\n    thread.wait();\n}\n\nDROGON_TEST(Mutex)\n{\n    trantor::EventLoopThreadPool pool{3};\n    pool.start();\n    Mutex mutex;\n    async_run([&]() -> Task<> {\n        co_await switchThreadCoro(pool.getLoop(0));\n        auto guard = co_await mutex.scoped_lock();\n        co_await sleepCoro(pool.getLoop(1), std::chrono::seconds(2));\n        co_return;\n    });\n    std::this_thread::sleep_for(std::chrono::milliseconds(100));\n    std::promise<void> done;\n    async_run([&]() -> Task<> {\n        co_await switchThreadCoro(pool.getLoop(2));\n        auto id = std::this_thread::get_id();\n        co_await mutex.lock();\n        CHECK(id == std::this_thread::get_id());\n        mutex.unlock();\n        CHECK(id == std::this_thread::get_id());\n        done.set_value();\n        co_return;\n    });\n    done.get_future().wait();\n    for (int16_t i = 0; i < 3; i++)\n        pool.getLoop(i)->quit();\n    pool.wait();\n}\n\nDROGON_TEST(WhenAll)\n{\n    using TestCtx = std::shared_ptr<drogon::test::Case>;\n    [](TestCtx TEST_CTX) -> AsyncTask {\n        size_t counter = 0;\n        auto t1 = [](TestCtx TEST_CTX, size_t *counter) -> Task<> {\n            co_await drogon::sleepCoro(app().getLoop(), 0.2);\n            (*counter)++;\n        }(TEST_CTX, &counter);\n        auto t2 = [](TestCtx TEST_CTX, size_t *counter) -> Task<> {\n            co_await drogon::sleepCoro(app().getLoop(), 0.1);\n            (*counter)++;\n        }(TEST_CTX, &counter);\n        std::vector<Task<void>> tasks;\n        tasks.emplace_back(std::move(t1));\n        tasks.emplace_back(std::move(t2));\n\n        co_await when_all(std::move(tasks));\n        CHECK(counter == 2);\n    }(TEST_CTX);\n\n    [](TestCtx TEST_CTX) -> AsyncTask {\n        std::vector<Task<void>> tasks;\n        co_await when_all(std::move(tasks));\n        SUCCESS();\n    }(TEST_CTX);\n\n    [](TestCtx TEST_CTX) -> AsyncTask {\n        auto t1 = [](TestCtx TEST_CTX) -> Task<int> { co_return 1; }(TEST_CTX);\n        auto t2 = [](TestCtx TEST_CTX) -> Task<int> { co_return 2; }(TEST_CTX);\n        std::vector<Task<int>> tasks;\n        tasks.emplace_back(std::move(t1));\n        tasks.emplace_back(std::move(t2));\n\n        auto res = co_await when_all(std::move(tasks));\n        CO_REQUIRE(res.size() == 2);\n        CHECK(res[0] == 1);\n        CHECK(res[1] == 2);\n    }(TEST_CTX);\n\n    [](TestCtx TEST_CTX) -> AsyncTask {\n        auto t1 = [](TestCtx TEST_CTX) -> Task<int> { co_return 1; }(TEST_CTX);\n        auto t2 = [](TestCtx TEST_CTX) -> Task<std::string> {\n            co_return \"Hello\";\n        }(TEST_CTX);\n        auto [num, str] = co_await when_all(std::move(t1), std::move(t2));\n        CHECK(num == 1);\n        CHECK(str == \"Hello\");\n    }(TEST_CTX);\n\n    [](TestCtx TEST_CTX) -> AsyncTask {\n        size_t counter = 0;\n        // Even on corutine throws, other coroutins run to completion\n        auto t1 = [](TestCtx TEST_CTX, size_t *counter) -> Task<int> {\n            co_await drogon::sleepCoro(app().getLoop(), 0.2);\n            (*counter)++;\n            co_return 1;\n        }(TEST_CTX, &counter);\n        auto t2 = [](TestCtx TEST_CTX) -> Task<std::string> {\n            co_await drogon::sleepCoro(app().getLoop(), 0.1);\n            throw std::runtime_error(\"Test exception\");\n        }(TEST_CTX);\n        CO_REQUIRE_THROWS(co_await when_all(std::move(t1), std::move(t2)));\n        CHECK(counter == 1);\n    }(TEST_CTX);\n\n    [](TestCtx TEST_CTX) -> AsyncTask {\n        size_t counter = 0;\n        // void retuens gets mapped to std::false_type in the tuple API\n        auto t1 = [](TestCtx TEST_CTX, size_t *counter) -> Task<> {\n            (*counter)++;\n            co_return;\n        }(TEST_CTX, &counter);\n        auto [res] = co_await when_all(std::move(t1));\n        CHECK(counter == 1);\n    }(TEST_CTX);\n}\n"
  },
  {
    "path": "lib/tests/unittests/DrObjectTest.cc",
    "content": "#include <drogon/DrObject.h>\n#include <drogon/drogon_test.h>\n#include <drogon/HttpController.h>\n\nusing namespace drogon;\n\nclass TestA : public DrObject<TestA>\n{\n};\n\nnamespace test\n{\nclass TestB : public DrObject<TestB>\n{\n};\n}  // namespace test\n\nDROGON_TEST(DrObjectCreationTest)\n{\n    using PtrType = std::shared_ptr<DrObjectBase>;\n    auto obj = PtrType(DrClassMap::newObject(\"TestA\"));\n    CHECK(obj != nullptr);\n\n    auto objPtr = DrClassMap::getSingleInstance(\"TestA\");\n    CHECK(objPtr.get() != nullptr);\n\n    auto objPtr2 = DrClassMap::getSingleInstance<TestA>();\n    CHECK(objPtr2.get() != nullptr);\n    CHECK(objPtr == objPtr2);\n}\n\nDROGON_TEST(DrObjectNamespaceTest)\n{\n    using PtrType = std::shared_ptr<DrObjectBase>;\n    auto obj = PtrType(DrClassMap::newObject(\"test::TestB\"));\n    CHECK(obj != nullptr);\n\n    auto objPtr = DrClassMap::getSingleInstance(\"test::TestB\");\n    CHECK(objPtr.get() != nullptr);\n\n    auto objPtr2 = DrClassMap::getSingleInstance<::test::TestB>();\n    CHECK(objPtr2.get() != nullptr);\n    CHECK(objPtr == objPtr2);\n}\n\nclass TestC : public DrObject<TestC>\n{\n  public:\n    static constexpr bool isAutoCreation = true;\n};\n\nclass TestD : public DrObject<TestD>\n{\n  public:\n    static constexpr bool isAutoCreation = false;\n};\n\nclass TestE : public DrObject<TestE>\n{\n  public:\n    static constexpr double isAutoCreation = 3.0;\n};\n\nclass CtrlA : public HttpController<CtrlA>\n{\n  public:\n    METHOD_LIST_BEGIN\n    METHOD_LIST_END\n};\n\nclass CtrlB : public HttpController<CtrlB, false>\n{\n  public:\n    METHOD_LIST_BEGIN\n    METHOD_LIST_END\n};\n\nDROGON_TEST(IsAutoCreationClassTest)\n{\n    STATIC_REQUIRE(isAutoCreationClass<TestA>::value == false);\n    STATIC_REQUIRE(isAutoCreationClass<TestC>::value == true);\n    STATIC_REQUIRE(isAutoCreationClass<TestD>::value == false);\n    STATIC_REQUIRE(isAutoCreationClass<TestE>::value == false);\n    STATIC_REQUIRE(isAutoCreationClass<CtrlA>::value == true);\n    STATIC_REQUIRE(isAutoCreationClass<CtrlB>::value == false);\n}\n"
  },
  {
    "path": "lib/tests/unittests/FileTypeTest.cc",
    "content": "#include \"../lib/src/HttpUtils.h\"\n#include <drogon/drogon_test.h>\n#include <string>\nusing namespace drogon;\n\nDROGON_TEST(ExtensionTest)\n{\n    SUBSECTION(normal)\n    {\n        std::string str{\"drogon.jpg\"};\n        CHECK(getFileExtension(str) == \"jpg\");\n    }\n\n    SUBSECTION(negative)\n    {\n        std::string str{\"drogon.\"};\n        CHECK(getFileExtension(str) == \"\");\n        str = \"drogon\";\n        CHECK(getFileExtension(str) == \"\");\n        str = \"\";\n        CHECK(getFileExtension(str) == \"\");\n        str = \"....\";\n        CHECK(getFileExtension(str) == \"\");\n    }\n}\n\nDROGON_TEST(ContentTypeTest)\n{\n    SUBSECTION(normal)\n    {\n        for (int i = CT_NONE + 1; i < CT_CUSTOM; i++)\n        {\n            auto contentType = ContentType(i);\n            const auto mimeType = contentTypeToMime(contentType);\n            CHECK(mimeType.empty() == false);\n            CHECK(parseContentType(mimeType) == contentType);\n            auto exts = getFileExtensions(contentType);\n            bool shouldBeEmpty = (contentType == CT_APPLICATION_X_FORM) ||\n                                 (contentType == CT_APPLICATION_OCTET_STREAM) ||\n                                 (contentType == CT_MULTIPART_FORM_DATA);\n            CHECK(exts.empty() == shouldBeEmpty);\n            for (const auto &ext : exts)\n            {\n                auto dummyFile{std::string(\"dummy.\").append(ext)};\n                // Handle multiple mime types by setting the default ContentType\n                if (contentType == CT_APPLICATION_X_JAVASCRIPT)\n                    contentType = CT_TEXT_JAVASCRIPT;\n                if (contentType == CT_TEXT_XML)\n                    contentType = CT_APPLICATION_XML;\n                CHECK(getContentType(dummyFile) == contentType);\n                CHECK(parseFileType(dummyFile) != FT_UNKNOWN);\n            }\n            if (!shouldBeEmpty)\n            {\n                CHECK(getFileType(contentType) != FT_UNKNOWN);\n                CHECK(getFileType(contentType) != FT_CUSTOM);\n            }\n        }\n    }\n\n    SUBSECTION(negative)\n    {\n        CHECK(getFileType(CT_NONE) == FT_UNKNOWN);\n        CHECK(getFileType(CT_CUSTOM) == FT_CUSTOM);\n        CHECK(getFileType(CT_APPLICATION_X_FORM) == FT_UNKNOWN);\n        CHECK(getFileType(CT_APPLICATION_OCTET_STREAM) == FT_UNKNOWN);\n        CHECK(getFileType(CT_MULTIPART_FORM_DATA) == FT_UNKNOWN);\n        CHECK(contentTypeToMime(CT_NONE).empty());\n        CHECK(contentTypeToMime(CT_CUSTOM) ==\n              contentTypeToMime(CT_APPLICATION_OCTET_STREAM));\n        CHECK(parseContentType(\"\") == CT_NONE);\n        CHECK(parseContentType(\"application/x-www-form-urlencoded\") ==\n              CT_APPLICATION_X_FORM);\n        CHECK(parseContentType(\"multipart/form-data\") ==\n              CT_MULTIPART_FORM_DATA);\n        CHECK(parseFileType(\"any.thing\") == FT_CUSTOM);\n        CHECK(getContentType(\"any.thing\") == CT_APPLICATION_OCTET_STREAM);\n        CHECK(getFileExtensions(CT_NONE).empty());\n        CHECK(getFileExtensions(CT_APPLICATION_X_FORM).empty());\n        CHECK(getFileExtensions(CT_APPLICATION_OCTET_STREAM).empty());\n        CHECK(getFileExtensions(CT_MULTIPART_FORM_DATA).empty());\n        CHECK(getFileExtensions(CT_CUSTOM).empty());\n    }\n}\n\nDROGON_TEST(FileTypeTest)\n{\n    SUBSECTION(normal)\n    {\n        CHECK(parseFileType(\"jpg\") == FT_IMAGE);\n        CHECK(parseFileType(\"mp4\") == FT_MEDIA);\n        CHECK(parseFileType(\"csp\") == FT_CUSTOM);\n        CHECK(parseFileType(\"html\") == FT_DOCUMENT);\n    }\n\n    SUBSECTION(negative)\n    {\n        CHECK(parseFileType(\"\") == FT_UNKNOWN);\n        CHECK(parseFileType(\"don'tknow\") == FT_CUSTOM);\n    }\n}\n"
  },
  {
    "path": "lib/tests/unittests/GzipTest.cc",
    "content": "#include <drogon/drogon_test.h>\n#include <drogon/utils/Utilities.h>\n\nusing namespace drogon;\n\nDROGON_TEST(Gzip)\n{\n    const std::string inStr =\n        \"Applications\\n\"\n        \"Developer\\n\"\n        \"Library\\n\"\n        \"Network\\n\"\n        \"System\\n\"\n        \"Users\\n\"\n        \"Volumes\\n\"\n        \"bin\\n\"\n        \"cores\\n\"\n        \"dev\\n\"\n        \"etc\\n\"\n        \"home\\n\"\n        \"installer.failurerequests\\n\"\n        \"net\\n\"\n        \"opt\\n\"\n        \"private\\n\"\n        \"sbin\\n\"\n        \"tmp\\n\"\n        \"usb\\n\"\n        \"usr\\n\"\n        \"var\\n\"\n        \"vm\\n\"\n        \"\\n\"\n        \"/Applications:\\n\"\n        \"Adobe\\n\"\n        \"Adobe Creative Cloud\\n\"\n        \"Adobe Photoshop CC\\n\"\n        \"AirPlayer Pro.app\\n\"\n        \"Android Studio.app\\n\"\n        \"App Store.app\\n\"\n        \"Autodesk\\n\"\n        \"Automator.app\\n\"\n        \"Axure RP Pro 7.0.app\\n\"\n        \"BaiduNetdisk_mac.app\\n\"\n        \"CLion.app\\n\"\n        \"Calculator.app\\n\"\n        \"Calendar.app\\n\"\n        \"Chess.app\\n\"\n        \"CleanApp.app\\n\"\n        \"Contacts.app\\n\"\n        \"DVD Player.app\\n\"\n        \"Dashboard.app\\n\"\n        \"Dictionary.app\\n\"\n        \"Docs for Xcode.app\\n\"\n        \"FaceTime.app\\n\"\n        \"FinalShell\\n\"\n        \"Firefox.app\\n\"\n        \"Folx.app\\n\"\n        \"Font Book.app\\n\"\n        \"GitHub.app\\n\"\n        \"Google Chrome.app\\n\"\n        \"Grammarly.app\\n\"\n        \"Image Capture.app\\n\"\n        \"Lantern.app\\n\"\n        \"Launchpad.app\\n\"\n        \"License.rtf\\n\"\n        \"MacPorts\\n\"\n        \"Mail.app\\n\"\n        \"Maps.app\\n\"\n        \"Messages.app\\n\"\n        \"Microsoft Excel.app\\n\"\n        \"Microsoft Office 2011\\n\"\n        \"Microsoft OneNote.app\\n\"\n        \"Microsoft Outlook.app\\n\"\n        \"Microsoft PowerPoint.app\\n\"\n        \"Microsoft Word.app\\n\"\n        \"Mindjet MindManager.app\\n\"\n        \"Mission Control.app\\n\"\n        \"Mockplus.app\\n\"\n        \"MyEclipse 2015\\n\"\n        \"Notes.app\\n\"\n        \"OmniGraffle.app\\n\"\n        \"Pages.app\\n\"\n        \"Photo Booth.app\\n\"\n        \"Photos.app\\n\"\n        \"Preview.app\\n\"\n        \"QJVPN.app\\n\"\n        \"QQ.app\\n\"\n        \"QuickTime Player.app\\n\"\n        \"RAR Extractor Lite.app\\n\"\n        \"Reminders.app\\n\"\n        \"Remote Desktop Connection.app\\n\"\n        \"Renee Undeleter.app\\n\"\n        \"Sabaki.app\\n\"\n        \"Safari.app\\n\"\n        \"ShadowsocksX.app\\n\"\n        \"Siri.app\\n\"\n        \"SogouInputPad.app\\n\"\n        \"Stickies.app\\n\"\n        \"System Preferences.app\\n\"\n        \"TeX\\n\"\n        \"Telegram.app\\n\"\n        \"Termius.app\\n\"\n        \"Tesumego - How to Make a Professional Go Player.app\\n\"\n        \"TextEdit.app\\n\"\n        \"Thunder.app\\n\"\n        \"Time Machine.app\\n\"\n        \"Tunnelblick.app\\n\"\n        \"Utilities\\n\"\n        \"VPN Shield.appdownload\\n\"\n        \"VirtualBox.app\\n\"\n        \"WeChat.app\\n\"\n        \"WinOnX2.app\\n\"\n        \"Wireshark.app\\n\"\n        \"Xcode.app\\n\"\n        \"Yose.app\\n\"\n        \"YoudaoNote.localized\\n\"\n        \"finalshelldata\\n\"\n        \"iBooks.app\\n\"\n        \"iPhoto.app\\n\"\n        \"iTools.app\\n\"\n        \"iTunes.app\\n\"\n        \"pgAdmin 4.app\\n\"\n        \"wechatwebdevtools.app\\n\"\n        \"\\n\"\n        \"/Applications/Adobe:\\n\"\n        \"Flash Player\\n\"\n        \"\\n\"\n        \"/Applications/Adobe/Flash Player:\\n\"\n        \"AddIns\\n\"\n        \"\\n\"\n        \"/Applications/Adobe/Flash Player/AddIns:\\n\"\n        \"airappinstaller\\n\"\n        \"\\n\"\n        \"/Applications/Adobe/Flash Player/AddIns/airappinstaller:\\n\"\n        \"airappinstaller\\n\"\n        \"digest.s\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Creative Cloud:\\n\"\n        \"Adobe Creative Cloud\\n\"\n        \"Icon\\n\"\n        \"Uninstall Adobe Creative Cloud\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC:\\n\"\n        \"Adobe Photoshop CC.app\\n\"\n        \"Configuration\\n\"\n        \"Icon\\n\"\n        \"Legal\\n\"\n        \"LegalNotices.pdf\\n\"\n        \"Locales\\n\"\n        \"Plug-ins\\n\"\n        \"Presets\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop CC.app:\\n\"\n        \"Contents\\n\"\n        \"Linguistics\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop CC.app/Contents:\\n\"\n        \"Application Data\\n\"\n        \"Frameworks\\n\"\n        \"Info.plist\\n\"\n        \"MacOS\\n\"\n        \"PkgInfo\\n\"\n        \"Required\\n\"\n        \"Resources\\n\"\n        \"_CodeSignature\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data:\\n\"\n        \"Custom File Info Panels\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data/Custom File Info Panels:\\n\"\n        \"4.0\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data/Custom File Info Panels/4.0:\\n\"\n        \"bin\\n\"\n        \"custom\\n\"\n        \"panels\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data/Custom File Info Panels/4.0/bin:\\n\"\n        \"FileInfoFoundation.swf\\n\"\n        \"FileInfoUI.swf\\n\"\n        \"framework.swf\\n\"\n        \"loc\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data/Custom File Info \"\n        \"Panels/4.0/bin/loc:\\n\"\n        \"FileInfo_ar_AE.dat\\n\"\n        \"FileInfo_bg_BG.dat\\n\"\n        \"FileInfo_cs_CZ.dat\\n\"\n        \"FileInfo_da_DK.dat\\n\"\n        \"FileInfo_de_DE.dat\\n\"\n        \"FileInfo_el_GR.dat\\n\"\n        \"FileInfo_en_US.dat\\n\"\n        \"FileInfo_es_ES.dat\\n\"\n        \"FileInfo_et_EE.dat\\n\"\n        \"FileInfo_fi_FI.dat\\n\"\n        \"FileInfo_fr_FR.dat\\n\"\n        \"FileInfo_he_IL.dat\\n\"\n        \"FileInfo_hr_HR.dat\\n\"\n        \"FileInfo_hu_HU.dat\\n\"\n        \"FileInfo_it_IT.dat\\n\"\n        \"FileInfo_ja_JP.dat\\n\"\n        \"FileInfo_ko_KR.dat\\n\"\n        \"FileInfo_lt_LT.dat\\n\"\n        \"FileInfo_lv_LV.dat\\n\"\n        \"FileInfo_nb_NO.dat\\n\"\n        \"FileInfo_nl_NL.dat\\n\"\n        \"FileInfo_pl_PL.dat\\n\"\n        \"FileInfo_pt_BR.dat\\n\"\n        \"FileInfo_ro_RO.dat\\n\"\n        \"FileInfo_ru_RU.dat\\n\"\n        \"FileInfo_sk_SK.dat\\n\"\n        \"FileInfo_sl_SI.dat\\n\"\n        \"FileInfo_sv_SE.dat\\n\"\n        \"FileInfo_tr_TR.dat\\n\"\n        \"FileInfo_uk_UA.dat\\n\"\n        \"FileInfo_zh_CN.dat\\n\"\n        \"FileInfo_zh_TW.dat\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data/Custom File Info \"\n        \"Panels/4.0/custom:\\n\"\n        \"DICOM.xml\\n\"\n        \"Mobile.xml\\n\"\n        \"loc\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data/Custom File Info \"\n        \"Panels/4.0/custom/loc:\\n\"\n        \"DICOM_ar_AE.dat\\n\"\n        \"DICOM_bg_BG.dat\\n\"\n        \"DICOM_cs_CZ.dat\\n\"\n        \"DICOM_da_DK.dat\\n\"\n        \"DICOM_de_DE.dat\\n\"\n        \"DICOM_el_GR.dat\\n\"\n        \"DICOM_en_US.dat\\n\"\n        \"DICOM_es_ES.dat\\n\"\n        \"DICOM_et_EE.dat\\n\"\n        \"DICOM_fi_FI.dat\\n\"\n        \"DICOM_fr_FR.dat\\n\"\n        \"DICOM_he_IL.dat\\n\"\n        \"DICOM_hr_HR.dat\\n\"\n        \"DICOM_hu_HU.dat\\n\"\n        \"DICOM_it_IT.dat\\n\"\n        \"DICOM_ja_JP.dat\\n\"\n        \"DICOM_ko_KR.dat\\n\"\n        \"DICOM_lt_LT.dat\\n\"\n        \"DICOM_lv_LV.dat\\n\"\n        \"DICOM_nb_NO.dat\\n\"\n        \"DICOM_nl_NL.dat\\n\"\n        \"DICOM_pl_PL.dat\\n\"\n        \"DICOM_pt_BR.dat\\n\"\n        \"DICOM_ro_RO.dat\\n\"\n        \"DICOM_ru_RU.dat\\n\"\n        \"DICOM_sk_SK.dat\\n\"\n        \"DICOM_sl_SI.dat\\n\"\n        \"DICOM_sv_SE.dat\\n\"\n        \"DICOM_tr_TR.dat\\n\"\n        \"DICOM_uk_UA.dat\\n\"\n        \"DICOM_zh_CN.dat\\n\"\n        \"DICOM_zh_TW.dat\\n\"\n        \"Mobile_ar_AE.dat\\n\"\n        \"Mobile_bg_BG.dat\\n\"\n        \"Mobile_cs_CZ.dat\\n\"\n        \"Mobile_da_DK.dat\\n\"\n        \"Mobile_de_DE.dat\\n\"\n        \"Mobile_el_GR.dat\\n\"\n        \"Mobile_en_US.dat\\n\"\n        \"Mobile_es_ES.dat\\n\"\n        \"Mobile_et_EE.dat\\n\"\n        \"Mobile_fi_FI.dat\\n\"\n        \"Mobile_fr_FR.dat\\n\"\n        \"Mobile_he_IL.dat\\n\"\n        \"Mobile_hr_HR.dat\\n\"\n        \"Mobile_hu_HU.dat\\n\"\n        \"Mobile_it_IT.dat\\n\"\n        \"Mobile_ja_JP.dat\\n\"\n        \"Mobile_ko_KR.dat\\n\"\n        \"Mobile_lt_LT.dat\\n\"\n        \"Mobile_lv_LV.dat\\n\"\n        \"Mobile_nb_NO.dat\\n\"\n        \"Mobile_nl_NL.dat\\n\"\n        \"Mobile_pl_PL.dat\\n\"\n        \"Mobile_pt_BR.dat\\n\"\n        \"Mobile_ro_RO.dat\\n\"\n        \"Mobile_ru_RU.dat\\n\"\n        \"Mobile_sk_SK.dat\\n\"\n        \"Mobile_sl_SI.dat\\n\"\n        \"Mobile_sv_SE.dat\\n\"\n        \"Mobile_tr_TR.dat\\n\"\n        \"Mobile_uk_UA.dat\\n\"\n        \"Mobile_zh_CN.dat\\n\"\n        \"Mobile_zh_TW.dat\\n\"\n        \"\\n\"\n        \"/Applications/Adobe Photoshop CC/Adobe Photoshop \"\n        \"CC.app/Contents/Application Data/Custom File Info \"\n        \"Panels/4.0/panels:\\n\"\n        \"IPTC\\n\"\n        \"IPTCExt\\n\"\n        \"advanced\\n\"\n        \"audioData\\n\"\n        \"camera\\n\"\n        \"categories\\n\"\n        \"description\\n\"\n        \"dicom\\n\"\n        \"gpsData\\n\"\n        \"history\\n\"\n        \"mobile\\n\"\n        \"origin\\n\"\n        \"rawpacket\";\n    auto ret = utils::gzipCompress(inStr.c_str(), inStr.length());\n    REQUIRE(ret.empty() == false);\n\n    auto decompressStr = utils::gzipDecompress(ret.data(), ret.length());\n    CHECK(inStr == decompressStr);\n}\n"
  },
  {
    "path": "lib/tests/unittests/HttpDateTest.cc",
    "content": "#include <drogon/utils/Utilities.h>\n#include <drogon/drogon_test.h>\nusing namespace drogon;\n\nDROGON_TEST(HttpDate)\n{\n    // RFC 850\n    auto date = utils::getHttpDate(\"Fri, 05-Jun-20 09:19:38 GMT\");\n    CHECK(date.microSecondsSinceEpoch() /\n              trantor::Date::MICRO_SECONDS_PER_SEC ==\n          1591348778);\n\n    // Reddit format\n    date = utils::getHttpDate(\"Fri, 05-Jun-2020 09:19:38 GMT\");\n    CHECK(date.microSecondsSinceEpoch() /\n              trantor::Date::MICRO_SECONDS_PER_SEC ==\n          1591348778);\n\n    // Invalid\n    date = utils::getHttpDate(\"Fri, this format is invalid\");\n    CHECK(date.microSecondsSinceEpoch() == std::numeric_limits<int64_t>::max());\n\n    // ASC Time\n    auto epoch = time(nullptr);\n    auto str = asctime(gmtime(&epoch));\n    date = utils::getHttpDate(str);\n    CHECK(date.microSecondsSinceEpoch() /\n              trantor::Date::MICRO_SECONDS_PER_SEC ==\n          epoch);\n}\n"
  },
  {
    "path": "lib/tests/unittests/HttpFileTest.cc",
    "content": "#include \"../../lib/src/HttpFileImpl.h\"\n#include <drogon/drogon_test.h>\n#include <filesystem>\n\nusing namespace drogon;\nusing namespace std;\n\nDROGON_TEST(HttpFile)\n{\n    SUBSECTION(SavePathUsingDefaultConfigPath)\n    {\n        HttpFileImpl file;\n        file.setFileName(\"test_file_name\");\n        file.setFile(\"test\", 4);\n        auto out = file.save();\n\n        CHECK(out == 0);\n        CHECK(filesystem::exists(\"./uploads/test_file_name\"));\n\n        filesystem::remove_all(\"./uploads/test_file_name\");\n    }\n\n    SUBSECTION(SavePathWithSpecificRelativePath)\n    {\n        HttpFileImpl file;\n        file.setFileName(\"test_file_name\");\n        file.setFile(\"test\", 4);\n        auto out = file.save(\"./test_uploads_dir\");\n\n        CHECK(out == 0);\n        CHECK(filesystem::exists(\"./test_uploads_dir/test_file_name\"));\n\n        filesystem::remove_all(\"./test_uploads_dir\");\n    }\n\n    SUBSECTION(SavePathWithSpecificAbsolutePath)\n    {\n        auto uploadPath = filesystem::current_path() / \"test_uploads_dir\";\n\n        HttpFileImpl file;\n        file.setFileName(\"test_file_name\");\n        file.setFile(\"test\", 4);\n        auto out = file.save(uploadPath.string());\n\n        CHECK(out == 0);\n        CHECK(filesystem::exists(uploadPath / \"test_file_name\"));\n\n        filesystem::remove_all(uploadPath.string());\n    }\n\n    SUBSECTION(FileNameWithRelativePath)\n    {\n        auto uploadPath = filesystem::current_path() / \"test_uploads_dir\";\n\n        HttpFileImpl file;\n        file.setFileName(\"../test_malicious_file_name\");\n        file.setFile(\"test\", 4);\n        auto out = file.save(uploadPath.string());\n\n        CHECK(out == -1);\n        CHECK(!filesystem::exists(uploadPath / \"../test_malicious_file_name\"));\n\n        filesystem::remove_all(uploadPath);\n        filesystem::remove(uploadPath / \"../test_malicious_file_name\");\n    }\n\n    SUBSECTION(FileNameWithAbsolutePath)\n    {\n        auto fileName = filesystem::current_path() / \"test_malicious_file_name\";\n\n        HttpFileImpl file;\n        file.setFileName(fileName.string());\n        file.setFile(\"test\", 4);\n        auto out = file.save(\"./test_uploads_dir\");\n\n        CHECK(out == -1);\n        CHECK(!filesystem::exists(fileName.string()));\n\n        filesystem::remove_all(\"test_uploads_dir\");\n        filesystem::remove(fileName.string());\n    }\n}\n"
  },
  {
    "path": "lib/tests/unittests/HttpFullDateTest.cc",
    "content": "#include <drogon/utils/Utilities.h>\n#include <drogon/drogon_test.h>\n#include <string>\n#include <iostream>\n\nusing namespace drogon;\n\nDROGON_TEST(HttpFullDateTest)\n{\n    auto str = utils::getHttpFullDateStr();\n    auto date = utils::getHttpDate(str);\n    CHECK(utils::getHttpFullDate(date) == str);\n}\n"
  },
  {
    "path": "lib/tests/unittests/HttpHeaderTest.cc",
    "content": "#include <drogon/drogon_test.h>\n#include <drogon/HttpRequest.h>\n#include <drogon/HttpResponse.h>\n#include \"../../lib/src/HttpResponseImpl.h\"\n\nusing namespace drogon;\n\nDROGON_TEST(HttpHeaderRequest)\n{\n    auto req = HttpRequest::newHttpRequest();\n    req->addHeader(\"Abc\", \"abc\");\n    CHECK(req->getHeader(\"Abc\") == \"abc\");\n    CHECK(req->getHeader(\"abc\") == \"abc\");\n\n    req->removeHeader(\"Abc\");\n    CHECK(req->getHeader(\"abc\") == \"\");\n}\n\nDROGON_TEST(HttpHeaderResponse)\n{\n    auto resp = std::dynamic_pointer_cast<HttpResponseImpl>(\n        HttpResponse::newHttpResponse());\n    REQUIRE(resp != nullptr);\n    resp->addHeader(\"Abc\", \"abc\");\n    CHECK(resp->getHeader(\"Abc\") == \"abc\");\n    CHECK(resp->getHeader(\"abc\") == \"abc\");\n    resp->makeHeaderString();\n\n    auto buffer = resp->renderToBuffer();\n    auto str = std::string{buffer->peek(), buffer->readableBytes()};\n    CHECK(str.find(\"abc\") != std::string::npos);\n\n    resp->removeHeader(\"Abc\");\n    buffer = resp->renderToBuffer();\n    str = std::string{buffer->peek(), buffer->readableBytes()};\n    CHECK(str.find(\"abc\") == std::string::npos);\n    CHECK(resp->getHeader(\"abc\") == \"\");\n}\n\nDROGON_TEST(ResponseSetCustomContentTypeString)\n{\n    auto resp = HttpResponse::newHttpResponse();\n    resp->setContentTypeString(\"text/html\");\n    CHECK(resp->getContentType() == CT_TEXT_HTML);\n\n    resp = HttpResponse::newHttpResponse();\n    resp->setContentTypeString(\"image/bmp\");\n    CHECK(resp->getContentType() == CT_IMAGE_BMP);\n\n    resp = HttpResponse::newHttpResponse();\n    resp->setContentTypeString(\"thisdoesnotexist/unknown\");\n    CHECK(resp->getContentType() == CT_CUSTOM);\n}\n\nDROGON_TEST(ResquestSetCustomContentTypeString)\n{\n    auto req = HttpRequest::newHttpRequest();\n    req->setContentTypeString(\"text/html\");\n    CHECK(req->getContentType() == CT_TEXT_HTML);\n\n    req = HttpRequest::newHttpRequest();\n    req->setContentTypeString(\"image/bmp\");\n    CHECK(req->getContentType() == CT_IMAGE_BMP);\n\n    req = HttpRequest::newHttpRequest();\n    req->setContentTypeString(\"thisdoesnotexist/unknown\");\n    CHECK(req->getContentType() == CT_CUSTOM);\n}\n"
  },
  {
    "path": "lib/tests/unittests/HttpViewDataTest.cc",
    "content": "#include <drogon/HttpViewData.h>\n#include <drogon/drogon_test.h>\n#include <iostream>\n\nusing namespace drogon;\n\nDROGON_TEST(HttpViewData)\n{\n    HttpViewData data;\n    data.insert(\"1\", 1);\n    data.insertAsString(\"2\", 2.0);\n    data.insertFormattedString(\"3\", \"third value is %d\", 3);\n    data.insertAsString(\"4\", \"4\");\n    data.insert(\"5\", 5);\n    data.insert(\"5\", std::string(\"5!!!!!!!\"));  // Overrides the old value\n    char six = 6;\n    data.insert(\"6\", six);\n\n    CHECK(data.get<int>(\"1\") == 1);\n    CHECK(data.get<std::string>(\"2\") == \"2\");\n    CHECK(data.get<std::string>(\"3\") == \"third value is 3\");\n    CHECK(data.get<std::string>(\"4\") == \"4\");\n    CHECK(data.get<std::string>(\"5\") == \"5!!!!!!!\");\n    CHECK(data.get<char>(\"6\") == 6);\n    CHECK(data.get<int>(\"1\") == 1);  // get a second time\n\n    // Bad key returns a default constructed value\n    CHECK_NOTHROW(data.get<int>(\"this_does_not_exist\"));\n\n    SUBSECTION(Translate)\n    {\n        CHECK(HttpViewData::needTranslation(\"\") == false);\n        CHECK(HttpViewData::needTranslation(\"!)(*#\") == false);\n        CHECK(HttpViewData::needTranslation(\"#include <iostream>\") == true);\n        CHECK(HttpViewData::needTranslation(\"<body></body>\") == true);\n\n        CHECK(HttpViewData::htmlTranslate(\"#include <iostream>\") ==\n              \"#include &lt;iostream&gt;\");\n        CHECK(HttpViewData::htmlTranslate(\"&gt;\") == \"&amp;gt;\");\n    }\n}\n"
  },
  {
    "path": "lib/tests/unittests/MD5Test.cc",
    "content": "#include <drogon/utils/Utilities.h>\n#include <drogon/drogon_test.h>\n#include <string>\n\nDROGON_TEST(Md5Test)\n{\n    CHECK(drogon::utils::getMd5(\"123456789012345678901234567890123456789012345\"\n                                \"678901234567890123456789012345678901234567890\"\n                                \"1234567890\") ==\n          \"49CB3608E2B33FAD6B65DF8CB8F49668\");\n    CHECK(drogon::utils::getMd5(\"1\") == \"C4CA4238A0B923820DCC509A6F75849B\");\n    CHECK(drogon::utils::getMd5(\"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\"\n                                \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\"\n                                \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\") ==\n          \"59F761506DFA597B0FAF1968F7CCA867\");\n}\n"
  },
  {
    "path": "lib/tests/unittests/MainLoopTest.cc",
    "content": "#include <drogon/HttpAppFramework.h>\n#include <drogon/drogon_test.h>\n#include <string>\n#include <iostream>\n\nusing namespace drogon;\n\nstruct TestCookie\n{\n    TestCookie(std::shared_ptr<test::CaseBase> ctx) : TEST_CTX(ctx)\n    {\n    }\n\n    ~TestCookie()\n    {\n        if (!taken)\n            FAIL(\"Test cookie not taken\");\n        else\n            SUCCESS();\n    }\n\n    void take()\n    {\n        taken = true;\n    }\n\n  protected:\n    bool taken = false;\n    std::shared_ptr<test::CaseBase> TEST_CTX;\n};\n\nDROGON_TEST(MainLoopTest)\n{\n    auto cookie = std::make_shared<TestCookie>(TEST_CTX);\n    drogon::app().getLoop()->queueInLoop([cookie]() { cookie->take(); });\n\n    std::thread t([TEST_CTX]() {\n        auto cookie2 = std::make_shared<TestCookie>(TEST_CTX);\n        drogon::app().getLoop()->queueInLoop([cookie2]() { cookie2->take(); });\n    });\n    t.join();\n}\n"
  },
  {
    "path": "lib/tests/unittests/MsgBufferTest.cc",
    "content": "#include <trantor/utils/MsgBuffer.h>\n#include <drogon/drogon_test.h>\n#include <string>\n#include <iostream>\n\nusing namespace trantor;\n\nDROGON_TEST(MsgBufferTest)\n{\n    SUBSECTION(readableTest)\n    {\n        MsgBuffer buffer;\n\n        CHECK(buffer.readableBytes() == 0UL);\n        buffer.append(std::string(128, 'a'));\n        CHECK(buffer.readableBytes() == 128UL);\n        buffer.retrieve(100);\n        CHECK(buffer.readableBytes() == 28UL);\n        CHECK(buffer.peekInt8() == 'a');\n        buffer.retrieveAll();\n        CHECK(buffer.readableBytes() == 0UL);\n    }\n\n    SUBSECTION(writableTest)\n    {\n        MsgBuffer buffer(100);\n\n        CHECK(buffer.writableBytes() == 100UL);\n        buffer.append(\"abcde\");\n        CHECK(buffer.writableBytes() == 95UL);\n        buffer.append(std::string(100, 'x'));\n        CHECK(buffer.writableBytes() == 111UL);\n        buffer.retrieve(100);\n        CHECK(buffer.writableBytes() == 111UL);\n        buffer.append(std::string(112, 'c'));\n        CHECK(buffer.writableBytes() == 99UL);\n        buffer.retrieveAll();\n        CHECK(buffer.writableBytes() == 216UL);\n    }\n\n    SUBSECTION(addInFrontTest)\n    {\n        MsgBuffer buffer(100);\n\n        CHECK(buffer.writableBytes() == 100UL);\n        buffer.addInFrontInt8('a');\n        CHECK(buffer.writableBytes() == 100UL);\n        buffer.addInFrontInt64(123);\n        CHECK(buffer.writableBytes() == 92UL);\n        buffer.addInFrontInt64(100);\n        CHECK(buffer.writableBytes() == 84UL);\n        buffer.addInFrontInt8(1);\n        CHECK(buffer.writableBytes() == 84UL);\n    }\n\n    SUBSECTION(MoveAssignmentOperator)\n    {\n        MsgBuffer buf(100);\n        const char *bufptr = buf.peek();\n        size_t writable = buf.writableBytes();\n        MsgBuffer buffnew(1000);\n        buffnew = std::move(buf);\n        CHECK(bufptr == buffnew.peek());\n        CHECK(writable == buffnew.writableBytes());\n    }\n}\n"
  },
  {
    "path": "lib/tests/unittests/MultiPartParserTest.cc",
    "content": "#include <drogon/MultiPart.h>\n#include <drogon/drogon_test.h>\n#include <drogon/HttpRequest.h>\n#include \"../../lib/src/MultipartStreamParser.h\"\n\nDROGON_TEST(MultiPartParser)\n{\n    drogon::MultiPartParser parser1;\n    auto req = drogon::HttpRequest::newHttpRequest();\n    req->setMethod(drogon::Post);\n    req->addHeader(\"content-type\", \"multipart/form-data; boundary=\\\"12345\\\"\");\n    req->setBody(\n        \"--12345\\r\\n\"\n        \"Content-Disposition: form-data; name=\\\"somekey\\\"\\r\\n\"\n        \"\\r\\n\"\n        \"Hello; World\\r\\n\"\n        \"--12345--\");\n    CHECK(0 == parser1.parse(req));\n    CHECK(parser1.getParameters().size() == 1);\n    CHECK(parser1.getParameters().at(\"somekey\") == \"Hello; World\");\n    req->setBody(\n        \"--12345\\r\\n\"\n        \"Content-Disposition: form-data; name=\\\"somekey\\\"; \"\n        \"filename=\\\"test\\\"\\r\\n\"\n        \"\\r\\n\"\n        \"Hello; World\\r\\n\"\n        \"--12345--\");\n    drogon::MultiPartParser parser2;\n    CHECK(0 == parser2.parse(req));\n    auto filesMap = parser2.getFilesMap();\n    CHECK(filesMap.size() == 1);\n    CHECK(filesMap.at(\"somekey\").getFileName() == \"test\");\n    CHECK(filesMap.at(\"somekey\").fileContent() == \"Hello; World\");\n    req->setBody(\n        \"--12345\\r\\n\"\n        \"Content-Disposition: form-data; name=\\\"name of pdf\\\"; \"\n        \"filename=\\\"pdf-file.pdf\\\"\\r\\n\"\n        \"Content-Type: application/octet-stream\\r\\n\"\n        \"content-transfer-encoding: quoted-printable\\r\\n\"\n        \"\\r\\n\"\n        \"bytes of pdf file\\r\\n\"\n        \"--12345--\");\n    drogon::MultiPartParser parser3;\n    CHECK(0 == parser3.parse(req));\n    filesMap = parser3.getFilesMap();\n    CHECK(filesMap.size() == 1);\n    CHECK(filesMap.at(\"name of pdf\").getFileName() == \"pdf-file.pdf\");\n    CHECK(filesMap.at(\"name of pdf\").fileContent() == \"bytes of pdf file\");\n    CHECK(filesMap.at(\"name of pdf\").getContentType() ==\n          drogon::CT_APPLICATION_OCTET_STREAM);\n    CHECK(filesMap.at(\"name of pdf\").getContentTransferEncoding() ==\n          \"quoted-printable\");\n    req->setBody(\n        \"--12345\\r\\n\"\n        \"Content-Disposition: form-data; name=\\\"some;key\\\"\\r\\n\"\n        \"\\r\\n\"\n        \"Hello; World\\r\\n\"\n        \"--12345--\");\n    drogon::MultiPartParser parser4;\n    CHECK(0 == parser4.parse(req));\n    CHECK(parser4.getParameters().size() == 1);\n    CHECK(parser4.getParameters().at(\"some;key\") == \"Hello; World\");\n}\n\nDROGON_TEST(MultiPartStreamParser)\n{\n    static const std::string ct = \"multipart/form-data; boundary=\\\"12345\\\"\";\n    static const std::string_view data =\n        \"--12345\\r\\n\"\n        \"Content-Disposition: form-data; name=\\\"key1\\\"; filename=\\\"file1\\\"\\r\\n\"\n        \"\\r\\n\"\n        \"Hello; World\\r\\n\"\n        \"--12345\\r\\n\"\n        \"Content-Disposition: form-data; name=\\\"key2\\\"\\r\\n\"\n        \"\\r\\n\"\n        \"value2\\r\\n\"\n        \"--12345--\";\n\n    struct Entry\n    {\n        drogon::MultipartHeader header;\n        std::string value;\n        std::string fileContent;\n    };\n\n    auto check = [TEST_CTX](size_t step) {\n        drogon::MultipartStreamParser parser(ct);\n\n        auto entries = std::make_shared<std::vector<Entry>>();\n        auto headerCb = [TEST_CTX, entries](drogon::MultipartHeader hdr) {\n            entries->emplace_back(Entry{std::move(hdr)});\n        };\n        auto dataCb = [TEST_CTX, entries](const char *data, size_t length) {\n            MANDATE(!entries->empty());\n            if (length == 0)\n            {\n                // Field finished\n                return;\n            }\n            if (entries->back().header.filename.empty())\n            {\n                entries->back().value.append(data, length);\n            }\n            else\n            {\n                entries->back().fileContent.append(data, length);\n            }\n        };\n\n        size_t i = 0;\n        while (i < data.length() && parser.isValid())\n        {\n            size_t end = i + step < data.length() ? i + step : data.length();\n            parser.parse(data.data() + i, end - i, headerCb, dataCb);\n            CHECK(parser.isValid());\n            i = end;\n        }\n        MANDATE(i == data.length());\n        MANDATE(parser.isFinished());\n\n        MANDATE(entries->size() == 2);\n        CHECK(entries->at(0).header.name == \"key1\");\n        CHECK(entries->at(0).fileContent == \"Hello; World\");\n        CHECK(entries->at(1).header.name == \"key2\");\n        CHECK(entries->at(1).value == \"value2\");\n    };\n\n    check(1);\n    check(3);\n    check(7);\n    check(20);\n}\n"
  },
  {
    "path": "lib/tests/unittests/OStringStreamTest.cc",
    "content": "#include <drogon/utils/OStringStream.h>\n#include <drogon/drogon_test.h>\n#include <string>\n#include <string_view>\n#include <iostream>\n\nDROGON_TEST(OStringStreamTest)\n{\n    SUBSECTION(CanConvertToString)\n    {\n        using drogon::internal::CanConvertToString;\n\n        static_assert(CanConvertToString<int>::value);\n        static_assert(CanConvertToString<long>::value);\n        static_assert(CanConvertToString<long long>::value);\n        static_assert(CanConvertToString<unsigned>::value);\n        static_assert(CanConvertToString<unsigned long>::value);\n        static_assert(CanConvertToString<unsigned long long>::value);\n        static_assert(CanConvertToString<float>::value);\n        static_assert(CanConvertToString<double>::value);\n        static_assert(CanConvertToString<long double>::value);\n        static_assert(!CanConvertToString<std::string>::value);\n        static_assert(!CanConvertToString<std::string_view>::value);\n    }\n\n    SUBSECTION(integer)\n    {\n        drogon::OStringStream ss;\n        ss << 12;\n        ss << 345L;\n        CHECK(ss.str() == \"12345\");\n    }\n\n    SUBSECTION(float_number)\n    {\n        drogon::OStringStream ss;\n        ss << 3.14f;\n        ss << 3.1416;\n        CHECK(ss.str() == \"3.143.1416\");\n    }\n\n    SUBSECTION(literal_string)\n    {\n        drogon::OStringStream ss;\n        ss << \"hello\";\n        ss << \" world\";\n        CHECK(ss.str() == \"hello world\");\n    }\n\n    SUBSECTION(std::string_view)\n    {\n        drogon::OStringStream ss;\n        ss << std::string_view(\"hello\");\n        ss << std::string_view(\" world\");\n        CHECK(ss.str() == \"hello world\");\n    }\n\n    SUBSECTION(std_string)\n    {\n        drogon::OStringStream ss;\n        ss << std::string(\"hello\");\n        ss << std::string(\" world\");\n        CHECK(ss.str() == \"hello world\");\n    }\n\n    SUBSECTION(mix)\n    {\n        drogon::OStringStream ss;\n        ss << std::string(\"hello\");\n        ss << std::string_view(\" world\");\n        ss << \"!\";\n        ss << 123;\n        ss << 3.14f;\n\n        CHECK(ss.str() == \"hello world!1233.14\");\n    }\n}\n"
  },
  {
    "path": "lib/tests/unittests/PubSubServiceUnittest.cc",
    "content": "#include <drogon/PubSubService.h>\n#include <drogon/drogon_test.h>\n\nDROGON_TEST(PubSubServiceTest)\n{\n    drogon::PubSubService<std::string> service;\n    auto id = service.subscribe(\"topic1\",\n                                [TEST_CTX](const std::string &topic,\n                                           const std::string &message) {\n                                    CHECK(topic == \"topic1\");\n                                    CHECK(message == \"hello world\");\n                                });\n    service.publish(\"topic1\", \"hello world\");\n    service.publish(\"topic2\", \"hello world\");\n    CHECK(service.size() == 1UL);\n    service.unsubscribe(\"topic1\", id);\n    CHECK(service.size() == 0UL);\n}\n"
  },
  {
    "path": "lib/tests/unittests/Sha1Test.cc",
    "content": "#include <drogon/utils/Utilities.h>\n#include <drogon/drogon_test.h>\n#include <string>\n\nDROGON_TEST(SHA1Test)\n{\n    char in[] =\n        \"1234567890123456789012345678901234567890123456789012345\"\n        \"678901234567890123456789012345678901234567890\";\n    auto str = drogon::utils::getSha1(in, strlen((const char *)in));\n    CHECK(str == \"FECFD28BBC9345891A66D7C1B8FF46E60192D284\");\n}\n"
  },
  {
    "path": "lib/tests/unittests/SlashRemoverTest.cc",
    "content": "#include <drogon/drogon_test.h>\n#include <../../lib/src/SlashRemover.cc>\n#include <string>\n\nusing std::string;\n\nDROGON_TEST(SlashRemoverTest)\n{\n    string cleanUrl;\n\n    {  // Regular URL\n        const string urlNoTrail = \"///home//page\", urlNoDup = \"/home/page/\",\n                     urlNoExcess = \"/home/page\";\n\n        SUBSECTION(Full)\n        {\n            const string url = \"///home//page//\";\n            removeTrailingSlashes(cleanUrl, findTrailingSlashes(url), url);\n            CHECK(cleanUrl == urlNoTrail);\n\n            cleanUrl = url;\n            removeDuplicateSlashes(cleanUrl, findDuplicateSlashes(url));\n            CHECK(cleanUrl == urlNoDup);\n\n            removeExcessiveSlashes(cleanUrl, findExcessiveSlashes(url), url);\n            CHECK(cleanUrl == urlNoExcess);\n        }\n\n        SUBSECTION(Partial)\n        {\n            removeExcessiveSlashes(cleanUrl,\n                                   findExcessiveSlashes(urlNoTrail),\n                                   urlNoTrail);\n            CHECK(cleanUrl == urlNoExcess);\n\n            removeExcessiveSlashes(cleanUrl,\n                                   findExcessiveSlashes(urlNoDup),\n                                   urlNoDup);\n            CHECK(cleanUrl == urlNoExcess);\n        }\n    }\n\n    SUBSECTION(Root)\n    {\n        const string urlNoExcess = \"/\";\n\n        {  // Overlapping indices\n            const string url = \"//\";\n\n            cleanUrl = url;\n            removeTrailingSlashes(cleanUrl, findTrailingSlashes(url), url);\n            CHECK(cleanUrl == urlNoExcess);\n\n            cleanUrl = url;\n            removeDuplicateSlashes(cleanUrl, findDuplicateSlashes(url));\n            CHECK(cleanUrl == urlNoExcess);\n\n            removeExcessiveSlashes(cleanUrl, findExcessiveSlashes(url), url);\n            CHECK(cleanUrl == urlNoExcess);\n        }\n\n        {  // Intersecting indices\n            const string url = \"///\";\n\n            cleanUrl = url;\n            removeTrailingSlashes(cleanUrl, findTrailingSlashes(url), url);\n            CHECK(cleanUrl == urlNoExcess);\n\n            cleanUrl = url;\n            removeDuplicateSlashes(cleanUrl, findDuplicateSlashes(url));\n            CHECK(cleanUrl == urlNoExcess);\n\n            removeExcessiveSlashes(cleanUrl, findExcessiveSlashes(url), url);\n            CHECK(cleanUrl == urlNoExcess);\n        }\n    }\n\n    SUBSECTION(Overlap)\n    {\n        const string urlNoDup = \"/a/\", urlNoExcess = \"/a\";\n\n        {  // Overlapping indices\n            const string url = \"/a//\";\n\n            removeTrailingSlashes(cleanUrl, findTrailingSlashes(url), url);\n            CHECK(cleanUrl == urlNoExcess);\n\n            cleanUrl = url;\n            removeDuplicateSlashes(cleanUrl, findDuplicateSlashes(url));\n            CHECK(cleanUrl == urlNoDup);\n\n            removeExcessiveSlashes(cleanUrl, findExcessiveSlashes(url), url);\n            CHECK(cleanUrl == urlNoExcess);\n        }\n\n        {  // Intersecting indices\n            const string url = \"/a///\";\n\n            removeTrailingSlashes(cleanUrl, findTrailingSlashes(url), url);\n            CHECK(cleanUrl == urlNoExcess);\n\n            cleanUrl = url;\n            removeDuplicateSlashes(cleanUrl, findDuplicateSlashes(url));\n            CHECK(cleanUrl == urlNoDup);\n\n            removeExcessiveSlashes(cleanUrl, findExcessiveSlashes(url), url);\n            CHECK(cleanUrl == urlNoExcess);\n        }\n    }\n\n    SUBSECTION(NoTrail)\n    {\n        const string url = \"//a\", urlNoExcess = \"/a\";\n\n        cleanUrl.clear();\n        {\n            auto find = findTrailingSlashes(url);\n            if (find != string::npos)\n                removeTrailingSlashes(cleanUrl, find, url);\n        }\n        CHECK(cleanUrl.empty());\n\n        cleanUrl = url;\n        removeDuplicateSlashes(cleanUrl, findDuplicateSlashes(url));\n        CHECK(cleanUrl == urlNoExcess);\n\n        removeExcessiveSlashes(cleanUrl, findExcessiveSlashes(url), url);\n        CHECK(cleanUrl == urlNoExcess);\n    }\n\n    SUBSECTION(NoDuplicate)\n    {\n        const string url = \"/a/\", urlNoExcess = \"/a\";\n\n        removeTrailingSlashes(cleanUrl, findTrailingSlashes(url), url);\n        CHECK(cleanUrl == urlNoExcess);\n\n        cleanUrl = url;\n        {\n            auto find = findDuplicateSlashes(cleanUrl);\n            if (find != string::npos)\n                removeDuplicateSlashes(cleanUrl, find);\n        }\n        CHECK(cleanUrl == url);\n\n        cleanUrl = url;\n        removeExcessiveSlashes(cleanUrl, findExcessiveSlashes(url), url);\n        CHECK(cleanUrl == urlNoExcess);\n    }\n\n    SUBSECTION(None)\n    {\n        const string url = \"/a\";\n\n        cleanUrl.clear();\n        {\n            auto find = findTrailingSlashes(url);\n            if (find != string::npos)\n                removeTrailingSlashes(cleanUrl, find, url);\n        }\n        CHECK(cleanUrl.empty());\n\n        cleanUrl = url;\n        {\n            auto find = findDuplicateSlashes(cleanUrl);\n            if (find != string::npos)\n                removeDuplicateSlashes(cleanUrl, find);\n        }\n        CHECK(cleanUrl == url);\n\n        cleanUrl.clear();\n        {\n            auto find = findExcessiveSlashes(url);\n            if (find.first != string::npos || find.second != string::npos)\n                removeExcessiveSlashes(cleanUrl, find, url);\n        }\n        CHECK(cleanUrl.empty());\n    }\n}\n"
  },
  {
    "path": "lib/tests/unittests/StringOpsTest.cc",
    "content": "#include <drogon/utils/Utilities.h>\n#include <drogon/drogon_test.h>\n#include <string>\n\nstruct SameContent\n{\n    SameContent(const std::vector<std::string> &container)\n        : container_(container.begin(), container.end())\n    {\n    }\n\n    std::vector<std::string> container_;\n};\n\ntemplate <typename Container1>\ninline bool operator==(const Container1 &a, const SameContent &wrapper)\n{\n    const auto &b = wrapper.container_;\n    if (a.size() != b.size())\n        return false;\n\n    auto ait = a.begin();\n    auto bit = b.begin();\n\n    while (ait != a.end() && bit != b.end())\n    {\n        if (*ait != *bit)\n            break;\n        ait++;\n        bit++;\n    }\n    return ait == a.end() && bit == b.end();\n}\n\nusing namespace drogon;\n\nDROGON_TEST(StringOpsTest)\n{\n    SUBSECTION(SplitString)\n    {\n        std::string str = \"1,2,3,3,,4\";\n        CHECK(utils::splitString(str, \",\") ==\n              SameContent({\"1\", \"2\", \"3\", \"3\", \"4\"}));\n        CHECK(utils::splitString(str, \",\", true) ==\n              SameContent({\"1\", \"2\", \"3\", \"3\", \"\", \"4\"}));\n        CHECK(utils::splitString(str, \"|\", true) ==\n              SameContent({\"1,2,3,3,,4\"}));\n\n        str = \"a||b||c||||\";\n        CHECK(utils::splitString(str, \"||\") == SameContent({\"a\", \"b\", \"c\"}));\n        CHECK(utils::splitString(str, \"||\", true) ==\n              SameContent({\"a\", \"b\", \"c\", \"\", \"\"}));\n\n        str = \"aabbbaabbbb\";\n        CHECK(utils::splitString(str, \"bb\") == SameContent({\"aa\", \"baa\"}));\n        CHECK(utils::splitString(str, \"bb\", true) ==\n              SameContent({\"aa\", \"baa\", \"\", \"\"}));\n\n        str = \"\";\n        CHECK(utils::splitString(str, \",\") == SameContent({}));\n        CHECK(utils::splitString(str, \",\", true) == SameContent({\"\"}));\n    }\n\n    SUBSECTION(SplitStringToSet)\n    {\n        // splitStringToSet ignores empty strings\n        std::string str = \"1,2,3,3,,4\";\n        auto s = utils::splitStringToSet(str, \",\");\n        CHECK(s.size() == 4UL);\n        CHECK(s.count(\"1\") == 1UL);\n        CHECK(s.count(\"2\") == 1UL);\n        CHECK(s.count(\"3\") == 1UL);\n        CHECK(s.count(\"4\") == 1UL);\n\n        str = \"a|||a\";\n        s = utils::splitStringToSet(str, \"||\");\n        CHECK(s.size() == 2UL);\n        CHECK(s.count(\"a\") == 1UL);\n        CHECK(s.count(\"|a\") == 1UL);\n    }\n\n    SUBSECTION(ReplaceAll)\n    {\n        std::string str = \"3.14159\";\n        utils::replaceAll(str, \"1\", \"a\");\n        CHECK(str == \"3.a4a59\");\n\n        str = \"aaxxxaaxxxxaaxxxxx\";\n        utils::replaceAll(str, \"xx\", \"oo\");\n        CHECK(str == \"aaooxaaooooaaoooox\");\n    }\n}\n"
  },
  {
    "path": "lib/tests/unittests/UrlCodecTest.cc",
    "content": "#include <string_view>\n#include <drogon/utils/Utilities.h>\n#include <iostream>\n#include <drogon/drogon_test.h>\n\nDROGON_TEST(URLCodec)\n{\n    std::string input = \"k1=1&k2=安\";\n    auto encoded = drogon::utils::urlEncode(input);\n    auto decoded = drogon::utils::urlDecode(encoded);\n\n    CHECK(encoded == \"k1=1&k2=%E5%AE%89\");\n    CHECK(input == decoded);\n}\n"
  },
  {
    "path": "lib/tests/unittests/UtilitiesTest.cc",
    "content": "#include <string>\n#include <string_view>\n#include <istream>\n#include <array>\n#include <vector>\n#include <drogon/utils/Utilities.h>\n#include <drogon/drogon_test.h>\n\nstruct ConvertibleFromStringStream\n{\n    friend std::istream &operator>>(std::istream &os,\n                                    ConvertibleFromStringStream &)\n    {\n        return os;\n    }\n};\n\nstruct NotConvertibleFromStringStream\n{\n};\n\nDROGON_TEST(CanConvertFromStringStream)\n{\n    using drogon::internal::CanConvertFromStringStream;\n\n    static_assert(CanConvertFromStringStream<unsigned short>::value);\n    static_assert(CanConvertFromStringStream<unsigned int>::value);\n    static_assert(CanConvertFromStringStream<long>::value);\n    static_assert(CanConvertFromStringStream<unsigned long>::value);\n    static_assert(CanConvertFromStringStream<long long>::value);\n    static_assert(CanConvertFromStringStream<unsigned long long>::value);\n    static_assert(CanConvertFromStringStream<float>::value);\n    static_assert(CanConvertFromStringStream<double>::value);\n    static_assert(CanConvertFromStringStream<long double>::value);\n    static_assert(CanConvertFromStringStream<bool>::value);\n    static_assert(CanConvertFromStringStream<void *>::value);\n    static_assert(CanConvertFromStringStream<short>::value);\n    static_assert(CanConvertFromStringStream<int>::value);\n\n    static_assert(\n        CanConvertFromStringStream<ConvertibleFromStringStream>::value);\n    static_assert(\n        !CanConvertFromStringStream<NotConvertibleFromStringStream>::value);\n}\n\nstruct ConstructibleFromString\n{\n    ConstructibleFromString(const std::string &)\n    {\n    }\n};\n\nstruct NotConstructibleFromString\n{\n};\n\nDROGON_TEST(CanConstructFromString)\n{\n    using drogon::internal::CanConstructFromString;\n\n    static_assert(CanConstructFromString<ConstructibleFromString>::value);\n    static_assert(!CanConstructFromString<NotConstructibleFromString>::value);\n    static_assert(!CanConstructFromString<int>::value);\n    static_assert(!CanConstructFromString<double>::value);\n    static_assert(CanConstructFromString<std::string>::value);\n    static_assert(CanConstructFromString<std::string_view>::value);\n    static_assert(CanConstructFromString<const std::string>::value);\n    static_assert(!CanConstructFromString<std::string &>::value);\n    static_assert(CanConstructFromString<const std::string &>::value);\n    static_assert(!CanConstructFromString<std::string *>::value);\n}\n\nstruct ConvertibleFromString\n{\n    ConvertibleFromString &operator=(const std::string &)\n    {\n        return *this;\n    }\n};\n\nstruct NotConvertibleFromString\n{\n};\n\nDROGON_TEST(CanConvertFromString)\n{\n    using drogon::internal::CanConvertFromString;\n\n    static_assert(CanConvertFromString<ConvertibleFromString>::value);\n    static_assert(!CanConvertFromString<NotConvertibleFromString>::value);\n    static_assert(CanConvertFromString<std::string>::value);\n    static_assert(!CanConvertFromString<const std::string>::value);\n    static_assert(!CanConvertFromString<const std::string &>::value);\n    static_assert(!CanConvertFromString<std::string *>::value);\n    static_assert(CanConvertFromString<std::string_view>::value);\n    static_assert(!CanConvertFromString<const char *>::value);\n    static_assert(!CanConvertFromString<std::vector<char>>::value);\n    static_assert(!CanConvertFromString<std::array<char, 5>>::value);\n    static_assert(!CanConvertFromString<int>::value);\n    static_assert(!CanConvertFromString<double>::value);\n}\n"
  },
  {
    "path": "lib/tests/unittests/UuidUnittest.cc",
    "content": "#include <string_view>\n#include <drogon/utils/Utilities.h>\n#include <iostream>\n#include <drogon/drogon_test.h>\n\nDROGON_TEST(UuidTest)\n{\n    auto uuid = drogon::utils::getUuid();\n\n    std::cout << \"uuid: \" << uuid << std::endl;\n\n    CHECK(uuid[8] == '-');\n    CHECK(uuid[13] == '-');\n    CHECK(uuid[18] == '-');\n    CHECK(uuid[23] == '-');\n    CHECK(uuid.size() == 36);\n}\n"
  },
  {
    "path": "lib/tests/unittests/WebsocketResponseTest.cc",
    "content": "#include <drogon/drogon_test.h>\n\n#include \"../../lib/src/HttpRequestImpl.h\"\n#include \"../../lib/src/HttpResponseImpl.h\"\n#include \"../../lib/src/HttpControllerBinder.h\"\n\nusing namespace drogon;\n\nDROGON_TEST(WebsocketReponseTest)\n{\n    WebsocketControllerBinder binder;\n\n    auto reqPtr = std::make_shared<HttpRequestImpl>(nullptr);\n\n    // Value from rfc6455-1.3\n    reqPtr->addHeader(\"sec-websocket-key\", \"dGhlIHNhbXBsZSBub25jZQ==\");\n\n    binder.handleRequest(reqPtr, [&](const HttpResponsePtr &resp) {\n        CHECK(resp->statusCode() == k101SwitchingProtocols);\n        CHECK(resp->headers().size() == 3);\n        CHECK(resp->getHeader(\"Upgrade\") == \"websocket\");\n        CHECK(resp->getHeader(\"Connection\") == \"Upgrade\");\n\n        // Value from rfc6455-1.3\n        CHECK(resp->getHeader(\"Sec-WebSocket-Accept\") ==\n              \"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\");\n\n        auto implPtr = std::dynamic_pointer_cast<HttpResponseImpl>(resp);\n\n        implPtr->makeHeaderString();\n        auto buffer = implPtr->renderToBuffer();\n        auto str = std::string{buffer->peek(), buffer->readableBytes()};\n\n        CHECK(str.find(\"upgrade: websocket\") != std::string::npos);\n        CHECK(str.find(\"connection: Upgrade\") != std::string::npos);\n        CHECK(str.find(\"sec-websocket-accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\") !=\n              std::string::npos);\n        CHECK(str.find(\"content-length:\") == std::string::npos);\n    });\n}\n"
  },
  {
    "path": "lib/tests/unittests/main.cc",
    "content": "#define DROGON_TEST_MAIN\n#include <drogon/drogon_test.h>\n#include <drogon/HttpAppFramework.h>\n\nusing namespace drogon;\nusing namespace trantor;\n\nDROGON_TEST(TestFrameworkSelfTest)\n{\n    CHECK(TEST_CTX->name() == \"TestFrameworkSelfTest\");\n    CHECK(true);\n    CHECK(false != true);\n    CHECK(1 * 2 == 1 + 1);\n    CHECK(42 < 100);\n    CHECK(0xff <= 255);\n    CHECK('a' >= 'a');\n    CHECK(3.14159 > 2.71828);\n    CHECK(nullptr == nullptr);\n    CHECK_THROWS(throw std::runtime_error(\"test exception\"));\n    CHECK_THROWS_AS(throw std::domain_error(\"test exception\"),\n                    std::domain_error);\n    CHECK_NOTHROW([] { return 0; }());\n    STATIC_REQUIRE(std::is_standard_layout<int>::value);\n    STATIC_REQUIRE(std::is_default_constructible<test::Case>::value == false);\n\n    auto child_test = std::make_shared<test::Case>(TEST_CTX, \"ChildTest\");\n    CHECK(child_test->fullname() == \"TestFrameworkSelfTest.ChildTest\");\n\n    // Unlike Catch2, a subsection in drogon test does not provide a fixture\n    // It's only a way to signify testing different for things\n    SUBSECTION(Subsection)\n    {\n        CHECK(TEST_CTX->fullname() == \"TestFrameworkSelfTest.Subsection\");\n    }\n}\n\nint main(int argc, char **argv)\n{\n    std::promise<void> p1;\n    std::future<void> f1 = p1.get_future();\n\n    std::thread thr([&]() {\n        p1.set_value();\n        app().run();\n    });\n\n    f1.get();\n    int testStatus = test::run(argc, argv);\n    app().getLoop()->queueInLoop([]() { app().quit(); });\n    thr.join();\n    return testStatus;\n}\n"
  },
  {
    "path": "nosql_lib/redis/inc/drogon/nosql/RedisClient.h",
    "content": "/**\n *\n *  @file RedisClient.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include <drogon/exports.h>\n#include <drogon/nosql/RedisResult.h>\n#include <drogon/nosql/RedisException.h>\n#include <drogon/nosql/RedisSubscriber.h>\n#include <string_view>\n#include <trantor/net/InetAddress.h>\n#include <trantor/utils/Logger.h>\n#include <memory>\n#include <functional>\n#include <future>\n#ifdef __cpp_impl_coroutine\n#include <drogon/utils/coroutine.h>\n#endif\n\nnamespace drogon\n{\nnamespace nosql\n{\n#ifdef __cpp_impl_coroutine\nclass RedisClient;\nclass RedisTransaction;\n\nnamespace internal\n{\nstruct [[nodiscard]] RedisAwaiter : public CallbackAwaiter<RedisResult>\n{\n    using RedisFunction =\n        std::function<void(RedisResultCallback &&, RedisExceptionCallback &&)>;\n\n    explicit RedisAwaiter(RedisFunction &&function)\n        : function_(std::move(function))\n    {\n    }\n\n    void await_suspend(std::coroutine_handle<> handle)\n    {\n        function_(\n            [handle, this](const RedisResult &result) {\n                this->setValue(result);\n                handle.resume();\n            },\n            [handle, this](const RedisException &e) {\n                LOG_ERROR << e.what();\n                this->setException(std::make_exception_ptr(e));\n                handle.resume();\n            });\n    }\n\n  private:\n    RedisFunction function_;\n};\n\nstruct [[nodiscard]] RedisTransactionAwaiter\n    : public CallbackAwaiter<std::shared_ptr<RedisTransaction>>\n{\n    RedisTransactionAwaiter(RedisClient *client) : client_(client)\n    {\n    }\n\n    void await_suspend(std::coroutine_handle<> handle);\n\n  private:\n    RedisClient *client_;\n};\n}  // namespace internal\n#endif\n\nclass RedisTransaction;\n\n/**\n * @brief This class represents a redis client that contains several connections\n * to a redis server.\n *\n */\nclass DROGON_EXPORT RedisClient\n{\n  public:\n    /**\n     * @brief Create a new redis client with multiple connections;\n     *\n     * @param serverAddress The server address.\n     * @param numberOfConnections The number of connections. 1 by default.\n     * @param username The username to authenticate if necessary.\n     * @param password The password to authenticate if necessary.\n     * @return std::shared_ptr<RedisClient>\n     */\n    static std::shared_ptr<RedisClient> newRedisClient(\n        const trantor::InetAddress &serverAddress,\n        size_t numberOfConnections = 1,\n        const std::string &password = \"\",\n        unsigned int db = 0,\n        const std::string &username = \"\");\n    /**\n     * @brief Execute a redis command\n     *\n     * @param resultCallback The callback is called when a redis reply is\n     * received successfully.\n     * @param exceptionCallback The callback is called when an error occurs.\n     * @note When a redis reply with REDIS_REPLY_ERROR code is received, this\n     * callback is called.\n     * @param command The command to be executed. the command string can contain\n     * some placeholders for parameters, such as '%s', '%d', etc.\n     * @param ... The command parameters.\n     * For example:\n     * @code\n       redisClientPtr->execCommandAsync([](const RedisResult &r){\n           std::cout << r.getStringForDisplaying() << std::endl;\n       },[](const std::exception &err){\n           std::cerr << err.what() << std::endl;\n       }, \"get %s\", key.data());\n       @endcode\n     */\n    virtual void execCommandAsync(RedisResultCallback &&resultCallback,\n                                  RedisExceptionCallback &&exceptionCallback,\n                                  std::string_view command,\n                                  ...) noexcept = 0;\n\n    /**\n     * @brief Execute a redis command synchronously\n     *\n     * @param processFunc Function to extract data from redis result.\n     * received successfully.\n     * @param command The command to be executed. the command string can contain\n     * some placeholders for parameters, such as '%s', '%d', etc.\n     * @param ... The command parameters.\n     * @return Returns the same value as process callback.\n     * For example:\n     * @code\n       try\n       {\n           std::string res = redisClientPtr->execCommandSync<std::string>(\n               [](const RedisResult &r){\n                   return r.asString();\n               },\n               \"get %s\",\n               key.data()\n           );\n       }\n       catch (const RedisException & err)\n       {\n       }\n       catch (const std::exception & err)\n       {\n       }\n       @endcode\n     */\n    template <typename T, typename... Args>\n    T execCommandSync(std::function<T(const RedisResult &)> &&processFunc,\n                      std::string_view command,\n                      Args &&...args)\n    {\n        return execCommandSync<std::decay_t<decltype(processFunc)>>(\n            std::move(processFunc), command, std::forward<Args>(args)...);\n    }\n\n    /**\n     * @brief Execute a redis command synchronously\n     * Return type can be deduced automatically in this version.\n     */\n    template <typename F, typename... Args>\n    std::invoke_result_t<F, const RedisResult &> execCommandSync(\n        F &&processFunc,\n        std::string_view command,\n        Args &&...args)\n    {\n        using Ret = std::invoke_result_t<F, const RedisResult &>;\n        std::promise<Ret> prom;\n        execCommandAsync(\n            [&processFunc, &prom](const RedisResult &result) {\n                try\n                {\n                    prom.set_value(processFunc(result));\n                }\n                catch (...)\n                {\n                    prom.set_exception(std::current_exception());\n                }\n            },\n            [&prom](const RedisException &err) {\n                prom.set_exception(std::make_exception_ptr(err));\n            },\n            command,\n            std::forward<Args>(args)...);\n\n        return prom.get_future().get();\n    }\n\n    /**\n     * @brief Create a subscriber for redis subscribe commands.\n     *\n     * @return std::shared_ptr<RedisSubscriber>\n     * @note This subscriber creates a new redis connection dedicated to\n     * subscribe commands. This connection is managed by RedisClient.\n     */\n    virtual std::shared_ptr<RedisSubscriber> newSubscriber() noexcept = 0;\n\n    /**\n     * @brief Create a redis transaction object.\n     *\n     * @return std::shared_ptr<RedisTransaction>\n     * @note An exception with kTimeout code is thrown if the operation is\n     * timed out. see RedisException.h\n     */\n    virtual std::shared_ptr<RedisTransaction> newTransaction() noexcept(\n        false) = 0;\n\n    /**\n     * @brief Create a transaction object in asynchronous mode.\n     *\n     * @return std::shared_ptr<RedisTransaction>\n     * @note An empty shared_ptr object is returned via the callback if the\n     * operation is timed out.\n     */\n    virtual void newTransactionAsync(\n        const std::function<void(const std::shared_ptr<RedisTransaction> &)>\n            &callback) = 0;\n    /**\n     * @brief Set the Timeout value of execution of a command.\n     *\n     * @param timeout in seconds, if the result is not returned from the\n     * server within the timeout, a RedisException with \"Command execution\n     * timeout\" string is generated and returned to the caller.\n     * @note set the timeout value to zero or negative for no limit on time.\n     * The default value is -1.0, this means there is no time limit if this\n     * method is not called.\n     */\n    virtual void setTimeout(double timeout) = 0;\n\n    virtual ~RedisClient() = default;\n\n    /**\n     * @brief Close all connections in the client. usually used by Drogon in the\n     * quit() method.\n     * */\n    virtual void closeAll() = 0;\n\n#ifdef __cpp_impl_coroutine\n    /**\n     * @brief Send a Redis command and await the RedisResult in a coroutine.\n     *\n     * @tparam Arguments\n     * @param command\n     * @param args\n     * @return internal::RedisAwaiter that can be awaited in a coroutine.\n     * For example:\n     * @code\n        try\n        {\n            auto result = co_await redisClient->execCommandCoro(\"get %s\",\n     \"keyname\");\n            std::cout << result.getStringForDisplaying() << \"\\n\";\n        }\n        catch(const RedisException &err)\n        {\n            std::cout << err.what() << \"\\n\";\n        }\n       @endcode\n     */\n    template <typename... Arguments>\n    internal::RedisAwaiter execCommandCoro(std::string_view command,\n                                           Arguments... args)\n    {\n        return internal::RedisAwaiter(\n            [command,\n             this,\n             args...](RedisResultCallback &&commandCallback,\n                      RedisExceptionCallback &&exceptionCallback) {\n                execCommandAsync(std::move(commandCallback),\n                                 std::move(exceptionCallback),\n                                 command,\n                                 args...);\n            });\n    }\n\n    /**\n     * @brief await a RedisTransactionPtr in a coroutine.\n     *\n     * @return internal::RedisTransactionAwaiter that can be awaited in a\n     * coroutine.\n     * For example:\n     * @code\n        try\n        {\n            auto transPtr = co_await redisClient->newTransactionCoro();\n            ...\n        }\n        catch(const RedisException &err)\n        {\n            std::cout << err.what() << \"\\n\";\n        }\n       @endcode\n     */\n    internal::RedisTransactionAwaiter newTransactionCoro()\n    {\n        return internal::RedisTransactionAwaiter(this);\n    }\n#endif\n};\n\nclass DROGON_EXPORT RedisTransaction : public RedisClient\n{\n  public:\n    // virtual void cancel() = 0;\n    virtual void execute(RedisResultCallback &&resultCallback,\n                         RedisExceptionCallback &&exceptionCallback) = 0;\n#ifdef __cpp_impl_coroutine\n    /**\n     * @brief Send a \"exec\" command to execute the transaction and await a\n     * RedisResult in a coroutine.\n     *\n     * @return internal::RedisAwaiter that can be awaited in a coroutine.\n     * For example:\n     * @code\n        try\n        {\n            auto transPtr = co_await redisClient->newTransactionCoro();\n            ...\n            auto result = co_await transPtr->executeCoro();\n            std::cout << result.getStringForDisplaying() << \"\\n\";\n        }\n        catch(const RedisException &err)\n        {\n            std::cout << err.what() << \"\\n\";\n        }\n       @endcode\n     */\n    internal::RedisAwaiter executeCoro()\n    {\n        return internal::RedisAwaiter(\n            [this](RedisResultCallback &&resultCallback,\n                   RedisExceptionCallback &&exceptionCallback) {\n                execute(std::move(resultCallback),\n                        std::move(exceptionCallback));\n            });\n    }\n#endif\n    void closeAll() override\n    {\n    }\n};\n\nusing RedisClientPtr = std::shared_ptr<RedisClient>;\nusing RedisTransactionPtr = std::shared_ptr<RedisTransaction>;\n\n#ifdef __cpp_impl_coroutine\ninline void internal::RedisTransactionAwaiter::await_suspend(\n    std::coroutine_handle<> handle)\n{\n    assert(client_ != nullptr);\n    client_->newTransactionAsync(\n        [this, handle](const std::shared_ptr<RedisTransaction> &transaction) {\n            if (transaction == nullptr)\n                setException(std::make_exception_ptr(RedisException(\n                    RedisErrorCode::kTimeout,\n                    \"Timeout, no connection available for transaction\")));\n            else\n                setValue(transaction);\n            handle.resume();\n        });\n}\n#endif\n}  // namespace nosql\n}  // namespace drogon\n"
  },
  {
    "path": "nosql_lib/redis/inc/drogon/nosql/RedisException.h",
    "content": "/**\r\n *\r\n *  @file RedisException.h\r\n *  @author An Tao\r\n *\r\n *  Copyright 2018, An Tao.  All rights reserved.\r\n *  https://github.com/an-tao/drogon\r\n *  Use of this source code is governed by a MIT license\r\n *  that can be found in the License file.\r\n *\r\n *  Drogon\r\n *\r\n */\r\n#pragma once\r\n\r\n#include <exception>\r\n#include <functional>\r\n#include <string>\r\n\r\nnamespace drogon\r\n{\r\nnamespace nosql\r\n{\r\nenum class RedisErrorCode\r\n{\r\n    kNone = 0,\r\n    kUnknown,\r\n    kConnectionBroken,\r\n    kNoConnectionAvailable,\r\n    kRedisError,\r\n    kInternalError,\r\n    kTransactionCancelled,\r\n    kBadType,\r\n    kTimeout\r\n};\r\n\r\nclass RedisException final : public std::exception\r\n{\r\n  public:\r\n    const char *what() const noexcept override\r\n    {\r\n        return message_.data();\r\n    }\r\n\r\n    RedisErrorCode code() const\r\n    {\r\n        return code_;\r\n    }\r\n\r\n    RedisException(RedisErrorCode code, const std::string &message)\r\n        : message_(message), code_(code)\r\n    {\r\n    }\r\n\r\n    RedisException(RedisErrorCode code, std::string &&message)\r\n        : message_(std::move(message)), code_(code)\r\n    {\r\n    }\r\n\r\n    RedisException() = delete;\r\n\r\n  private:\r\n    std::string message_;\r\n    RedisErrorCode code_{RedisErrorCode::kNone};\r\n};\r\n\r\nusing RedisExceptionCallback = std::function<void(const RedisException &)>;\r\n}  // namespace nosql\r\n}  // namespace drogon\r\n"
  },
  {
    "path": "nosql_lib/redis/inc/drogon/nosql/RedisResult.h",
    "content": "/**\n *\n *  @file RedisResult.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <vector>\n#include <string>\n#include <memory>\n#include <functional>\n\nstruct redisReply;\n\nnamespace drogon\n{\nnamespace nosql\n{\nenum class RedisResultType\n{\n    kInteger = 0,\n    kString,\n    kArray,\n    kStatus,\n    kNil,\n    kError\n};\n\n/**\n * @brief This class represents a redis reply with no error.\n * @note Limited by the hiredis library, the RedisResult object is only\n * available in the context of the result callback, one can't hold or copy or\n * move a RedisResult object for later use after the callback is returned.\n */\nclass DROGON_EXPORT RedisResult\n{\n  public:\n    explicit RedisResult(redisReply *result) : result_(result)\n    {\n    }\n\n    ~RedisResult() = default;\n\n    /**\n     * @brief Return the type of the result_\n     * @return RedisResultType\n     * @note The kError type is never obtained here.\n     */\n    RedisResultType type() const noexcept;\n\n    /**\n     * @brief Get the string value of the result.\n     *\n     * @return std::string\n     * @note Calling the method of a result object which is the kArray type\n     * throws a runtime exception.\n     */\n    std::string asString() const noexcept(false);\n\n    /**\n     * @brief Get the array value of the result.\n     *\n     * @return std::vector<RedisResult>\n     * @note Calling the method of a result object whose type is not kArray type\n     * throws a runtime exception.\n     */\n    std::vector<RedisResult> asArray() const noexcept(false);\n\n    /**\n     * @brief Get the integer value of the result.\n     *\n     * @return long long\n     * @note Calling the method of a result object whose type is not kInteger\n     * type throws a runtime exception.\n     */\n    long long asInteger() const noexcept(false);\n\n    /**\n     * @brief Get the string for displaying the result.\n     *\n     * @return std::string\n     */\n    std::string getStringForDisplaying() const noexcept;\n\n    /**\n     * @brief Get the string for displaying with indent.\n     *\n     * @param indent The indent value.\n     * @return std::string\n     */\n    std::string getStringForDisplayingWithIndent(\n        size_t indent = 0) const noexcept;\n\n    /**\n     * @brief return true if the result object is nil.\n     *\n     * @return true\n     * @return false\n     */\n    bool isNil() const noexcept;\n\n    /**\n     * @brief Check if the result object is not nil.\n     *\n     * @return true\n     * @return false\n     */\n    explicit operator bool() const\n    {\n        return !isNil();\n    }\n\n  private:\n    redisReply *result_;\n};\n\nusing RedisResultCallback = std::function<void(const RedisResult &)>;\nusing RedisMessageCallback =\n    std::function<void(const std::string &channel, const std::string &message)>;\n\n}  // namespace nosql\n}  // namespace drogon\n"
  },
  {
    "path": "nosql_lib/redis/inc/drogon/nosql/RedisSubscriber.h",
    "content": "/**\n *\n *  @file RedisSubscriber.h\n *  @author Nitromelon\n *\n *  Copyright 2022, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include <string>\n#include <drogon/nosql/RedisResult.h>\n\nnamespace drogon::nosql\n{\nclass RedisSubscriber\n{\n  public:\n    /**\n     * @brief Subscribe to a channel. The subscriber will keep the channel\n     * subscribed, until unsubscribe() is called on this channel, or the\n     * subscriber or RedisClient who creates it no longer exists.\n     * This method will not block.\n     *\n     * @param messageCallback The callback is called when a message is received\n     * from the channel.\n     * @param channel The channel to subscribe to.\n     *\n     * @note: Subscribing to same channel multiple times is supported. All\n     * message callbacks will be called in subscription order.\n     * One unsubscribe() call will remove all callbacks.\n     */\n    virtual void subscribe(const std::string &channel,\n                           RedisMessageCallback &&messageCallback) noexcept = 0;\n\n    // Subscribe to channel pattern\n    virtual void psubscribe(\n        const std::string &pattern,\n        RedisMessageCallback &&messageCallback) noexcept = 0;\n\n    /**\n     * @brief Unsubscribe from a channel. Once this function returns, the\n     * messageCallback registered through subscribe() will no longer be called.\n     * This method will not block.\n     *\n     * @param channel The channel to subscribe to.\n     */\n    virtual void unsubscribe(const std::string &channel) noexcept = 0;\n\n    // Unsubscribe from channel pattern\n    virtual void punsubscribe(const std::string &pattern) noexcept = 0;\n\n    virtual ~RedisSubscriber() = default;\n};\n\n}  // namespace drogon::nosql\n"
  },
  {
    "path": "nosql_lib/redis/src/RedisClientImpl.cc",
    "content": "/**\n *\n *  @file RedisClientImpl.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"RedisConnection.h\"\n#include \"RedisClientImpl.h\"\n#include \"RedisSubscriberImpl.h\"\n#include \"RedisTransactionImpl.h\"\n#include \"../../lib/src/TaskTimeoutFlag.h\"\n\nusing namespace drogon::nosql;\n\nstd::shared_ptr<RedisClient> RedisClient::newRedisClient(\n    const trantor::InetAddress &serverAddress,\n    size_t connectionNumber,\n    const std::string &password,\n    unsigned int db,\n    const std::string &username)\n{\n    auto client = std::make_shared<RedisClientImpl>(\n        serverAddress, connectionNumber, username, password, db);\n    client->init();\n    return client;\n}\n\nRedisClientImpl::RedisClientImpl(const trantor::InetAddress &serverAddress,\n                                 size_t numberOfConnections,\n                                 std::string username,\n                                 std::string password,\n                                 unsigned int db)\n    : loops_(numberOfConnections < std::thread::hardware_concurrency()\n                 ? numberOfConnections\n                 : std::thread::hardware_concurrency(),\n             \"RedisLoop\"),\n      serverAddr_(serverAddress),\n      username_(std::move(username)),\n      password_(std::move(password)),\n      db_(db),\n      numberOfConnections_(numberOfConnections)\n{\n}\n\nvoid RedisClientImpl::init()\n{\n    loops_.start();\n    for (size_t i = 0; i < numberOfConnections_; ++i)\n    {\n        auto loop = loops_.getNextLoop();\n        loop->queueInLoop([this, loop]() {\n            std::lock_guard<std::mutex> lock(connectionsMutex_);\n            connections_.insert(newConnection(loop));\n        });\n    }\n}\n\nRedisConnectionPtr RedisClientImpl::newConnection(trantor::EventLoop *loop)\n{\n    auto conn = std::make_shared<RedisConnection>(\n        serverAddr_, username_, password_, db_, loop);\n    std::weak_ptr<RedisClientImpl> thisWeakPtr = shared_from_this();\n    conn->setConnectCallback([thisWeakPtr](RedisConnectionPtr &&conn) {\n        auto thisPtr = thisWeakPtr.lock();\n        if (thisPtr)\n        {\n            {\n                std::lock_guard<std::mutex> lock(thisPtr->connectionsMutex_);\n                thisPtr->readyConnections_.push_back(conn);\n            }\n            thisPtr->handleNextTask(conn);\n        }\n    });\n    conn->setDisconnectCallback([thisWeakPtr](RedisConnectionPtr &&conn) {\n        // assert(status == REDIS_CONNECTED);\n        auto thisPtr = thisWeakPtr.lock();\n        if (thisPtr)\n        {\n            std::lock_guard<std::mutex> lock(thisPtr->connectionsMutex_);\n            thisPtr->connections_.erase(conn);\n            for (auto iter = thisPtr->readyConnections_.begin();\n                 iter != thisPtr->readyConnections_.end();\n                 ++iter)\n            {\n                if (*iter == conn)\n                {\n                    thisPtr->readyConnections_.erase(iter);\n                    break;\n                }\n            }\n            auto loop = trantor::EventLoop::getEventLoopOfCurrentThread();\n            assert(loop);\n            loop->runAfter(2.0, [thisPtr, loop, conn]() {\n                std::lock_guard<std::mutex> lock(thisPtr->connectionsMutex_);\n                thisPtr->connections_.insert(thisPtr->newConnection(loop));\n            });\n        }\n    });\n    conn->setIdleCallback([thisWeakPtr](const RedisConnectionPtr &connPtr) {\n        auto thisPtr = thisWeakPtr.lock();\n        if (thisPtr)\n        {\n            thisPtr->handleNextTask(connPtr);\n        }\n    });\n    return conn;\n}\n\nRedisConnectionPtr RedisClientImpl::newSubscribeConnection(\n    trantor::EventLoop *loop,\n    const std::shared_ptr<RedisSubscriberImpl> &subscriber)\n{\n    auto conn = std::make_shared<RedisConnection>(\n        serverAddr_, username_, password_, db_, loop);\n    std::weak_ptr<RedisClientImpl> weakThis = shared_from_this();\n    std::weak_ptr<RedisSubscriberImpl> weakSub(subscriber);\n    conn->setConnectCallback([weakThis, weakSub](RedisConnectionPtr &&conn) {\n        auto thisPtr = weakThis.lock();\n        if (!thisPtr)\n            return;\n        auto subPtr = weakSub.lock();\n\n        std::lock_guard<std::mutex> lock(thisPtr->connectionsMutex_);\n        if (subPtr)\n        {\n            subPtr->setConnection(conn);\n            subPtr->subscribeAll();\n        }\n        else\n        {\n            thisPtr->connections_.erase(conn);\n        }\n    });\n    conn->setDisconnectCallback([weakThis, weakSub](RedisConnectionPtr &&conn) {\n        // assert(status == REDIS_CONNECTED);\n        auto thisPtr = weakThis.lock();\n        if (!thisPtr)\n            return;\n        std::lock_guard<std::mutex> lock(thisPtr->connectionsMutex_);\n        thisPtr->connections_.erase(conn);\n        auto subPtr = weakSub.lock();\n        if (!subPtr)\n            return;\n        subPtr->clearConnection();\n\n        auto loop = trantor::EventLoop::getEventLoopOfCurrentThread();\n        assert(loop);\n        loop->runAfter(2.0, [thisPtr, loop, subPtr]() {\n            std::lock_guard<std::mutex> lock(thisPtr->connectionsMutex_);\n            thisPtr->connections_.insert(\n                thisPtr->newSubscribeConnection(loop, subPtr));\n        });\n    });\n    conn->setIdleCallback([weakThis, weakSub](const RedisConnectionPtr &) {\n        auto thisPtr = weakThis.lock();\n        if (!thisPtr)\n            return;\n        auto subPtr = weakSub.lock();\n        if (!subPtr)\n            return;\n        subPtr->subscribeNext();\n    });\n    return conn;\n}\n\nvoid RedisClientImpl::execCommandAsync(\n    RedisResultCallback &&resultCallback,\n    RedisExceptionCallback &&exceptionCallback,\n    std::string_view command,\n    ...) noexcept\n{\n    if (timeout_ > 0.0)\n    {\n        va_list args;\n        va_start(args, command);\n        execCommandAsyncWithTimeout(command,\n                                    std::move(resultCallback),\n                                    std::move(exceptionCallback),\n                                    args);\n        va_end(args);\n        return;\n    }\n    RedisConnectionPtr connPtr;\n    {\n        std::lock_guard<std::mutex> lock(connectionsMutex_);\n        if (!readyConnections_.empty())\n        {\n            if (connectionPos_ >= readyConnections_.size())\n            {\n                connPtr = readyConnections_[0];\n                connectionPos_ = 1;\n            }\n            else\n            {\n                connPtr = readyConnections_[connectionPos_++];\n            }\n        }\n    }\n    if (connPtr)\n    {\n        va_list args;\n        va_start(args, command);\n        connPtr->sendvCommand(command,\n                              std::move(resultCallback),\n                              std::move(exceptionCallback),\n                              args);\n        va_end(args);\n    }\n    else\n    {\n        LOG_TRACE << \"no connection available, push command to buffer\";\n        va_list args;\n        va_start(args, command);\n        auto formattedCmd = RedisConnection::getFormattedCommand(command, args);\n        va_end(args);\n        std::lock_guard<std::mutex> lock(connectionsMutex_);\n        tasks_.emplace_back(\n            std::make_shared<std::function<void(const RedisConnectionPtr &)>>(\n                [resultCallback = std::move(resultCallback),\n                 exceptionCallback = std::move(exceptionCallback),\n                 formattedCmd = std::move(formattedCmd)](\n                    const RedisConnectionPtr &connPtr) mutable {\n                    connPtr->sendFormattedCommand(std::move(formattedCmd),\n                                                  std::move(resultCallback),\n                                                  std::move(exceptionCallback));\n                }));\n    }\n}\n\nRedisClientImpl::~RedisClientImpl()\n{\n    closeAll();\n}\n\nvoid RedisClientImpl::closeAll()\n{\n    std::lock_guard<std::mutex> lock(connectionsMutex_);\n    for (auto &conn : connections_)\n    {\n        conn->disconnect();\n    }\n    readyConnections_.clear();\n    connections_.clear();\n}\n\nvoid RedisClientImpl::newTransactionAsync(\n    const std::function<void(const std::shared_ptr<RedisTransaction> &)>\n        &callback)\n{\n    RedisConnectionPtr connPtr;\n    {\n        std::lock_guard<std::mutex> lock(connectionsMutex_);\n        if (!readyConnections_.empty())\n        {\n            connPtr = readyConnections_[readyConnections_.size() - 1];\n            readyConnections_.resize(readyConnections_.size() - 1);\n        }\n    }\n    if (connPtr)\n    {\n        callback(makeTransaction(connPtr));\n    }\n    else\n    {\n        if (timeout_ <= 0.0)\n        {\n            std::weak_ptr<RedisClientImpl> thisWeakPtr = shared_from_this();\n            std::lock_guard<std::mutex> lock(connectionsMutex_);\n            tasks_.emplace_back(\n                std::make_shared<\n                    std::function<void(const RedisConnectionPtr &)>>(\n                    [callback,\n                     thisWeakPtr](const RedisConnectionPtr & /*connPtr*/) {\n                        auto thisPtr = thisWeakPtr.lock();\n                        if (thisPtr)\n                        {\n                            thisPtr->newTransactionAsync(callback);\n                        }\n                    }));\n        }\n        else\n        {\n            auto callbackPtr = std::make_shared<\n                std::function<void(const std::shared_ptr<RedisTransaction> &)>>(\n                callback);\n            auto transCbPtr = std::make_shared<std::weak_ptr<\n                std::function<void(const RedisConnectionPtr &)>>>();\n            auto timeoutFlagPtr = std::make_shared<TaskTimeoutFlag>(\n                loops_.getNextLoop(),\n                std::chrono::duration<double>(timeout_),\n                [callbackPtr, transCbPtr, this]() {\n                    auto cbPtr = (*transCbPtr).lock();\n                    if (cbPtr)\n                    {\n                        std::lock_guard<std::mutex> lock(connectionsMutex_);\n                        for (auto iter = tasks_.begin(); iter != tasks_.end();\n                             ++iter)\n                        {\n                            if (cbPtr == *iter)\n                            {\n                                tasks_.erase(iter);\n                                break;\n                            }\n                        }\n                    }\n                    (*callbackPtr)(nullptr);\n                });\n            std::weak_ptr<RedisClientImpl> thisWeakPtr = shared_from_this();\n            auto bufferCbPtr = std::make_shared<\n                std::function<void(const RedisConnectionPtr &)>>(\n                [callbackPtr, timeoutFlagPtr, thisWeakPtr](\n                    const RedisConnectionPtr & /*connPtr*/) {\n                    auto thisPtr = thisWeakPtr.lock();\n                    if (thisPtr)\n                    {\n                        if (timeoutFlagPtr->done())\n                        {\n                            return;\n                        }\n                        thisPtr->newTransactionAsync(*callbackPtr);\n                    }\n                });\n            {\n                std::lock_guard<std::mutex> lock(connectionsMutex_);\n                tasks_.emplace_back(bufferCbPtr);\n            }\n            (*transCbPtr) = bufferCbPtr;\n            timeoutFlagPtr->runTimer();\n        }\n    }\n}\n\nstd::shared_ptr<RedisTransaction> RedisClientImpl::makeTransaction(\n    const RedisConnectionPtr &connPtr)\n{\n    std::weak_ptr<RedisClientImpl> thisWeakPtr = shared_from_this();\n    auto trans = std::shared_ptr<RedisTransactionImpl>(\n        new RedisTransactionImpl(connPtr),\n        [thisWeakPtr, connPtr](RedisTransactionImpl *p) {\n            delete p;\n            auto thisPtr = thisWeakPtr.lock();\n            if (thisPtr)\n            {\n                {\n                    std::lock_guard<std::mutex> lock(\n                        thisPtr->connectionsMutex_);\n                    thisPtr->readyConnections_.push_back(connPtr);\n                }\n                thisPtr->handleNextTask(connPtr);\n            }\n        });\n    trans->doBegin();\n    return trans;\n}\n\nvoid RedisClientImpl::handleNextTask(const RedisConnectionPtr &connPtr)\n{\n    std::shared_ptr<std::function<void(const RedisConnectionPtr &)>> taskPtr;\n    {\n        std::lock_guard<std::mutex> lock(connectionsMutex_);\n        if (!tasks_.empty())\n        {\n            taskPtr = std::move(tasks_.front());\n            tasks_.pop_front();\n        }\n    }\n    if (taskPtr && (*taskPtr))\n    {\n        (*taskPtr)(connPtr);\n    }\n}\n\nvoid RedisClientImpl::execCommandAsyncWithTimeout(\n    std::string_view command,\n    RedisResultCallback &&resultCallback,\n    RedisExceptionCallback &&exceptionCallback,\n    va_list ap)\n{\n    auto expCbPtr =\n        std::make_shared<RedisExceptionCallback>(std::move(exceptionCallback));\n    auto bufferCbPtr = std::make_shared<\n        std::weak_ptr<std::function<void(const RedisConnectionPtr &)>>>();\n    auto timeoutFlagPtr = std::make_shared<TaskTimeoutFlag>(\n        loops_.getNextLoop(),\n        std::chrono::duration<double>(timeout_),\n        [expCbPtr, bufferCbPtr, this]() {\n            auto bfCbPtr = (*bufferCbPtr).lock();\n            if (bfCbPtr)\n            {\n                std::lock_guard<std::mutex> lock(connectionsMutex_);\n                for (auto iter = tasks_.begin(); iter != tasks_.end(); ++iter)\n                {\n                    if (bfCbPtr == *iter)\n                    {\n                        tasks_.erase(iter);\n                        break;\n                    }\n                }\n            }\n            if (*expCbPtr)\n            {\n                (*expCbPtr)(RedisException(RedisErrorCode::kTimeout,\n                                           \"Command execution timeout\"));\n            }\n        });\n    auto newResultCallback = [resultCallback = std::move(resultCallback),\n                              timeoutFlagPtr](const RedisResult &result) {\n        if (timeoutFlagPtr->done())\n        {\n            return;\n        }\n        if (resultCallback)\n        {\n            resultCallback(result);\n        }\n    };\n    auto newExceptionCallback = [expCbPtr,\n                                 timeoutFlagPtr](const RedisException &err) {\n        if (timeoutFlagPtr->done())\n        {\n            return;\n        }\n        if (*expCbPtr)\n        {\n            (*expCbPtr)(err);\n        }\n    };\n    RedisConnectionPtr connPtr;\n    {\n        std::lock_guard<std::mutex> lock(connectionsMutex_);\n        if (!readyConnections_.empty())\n        {\n            if (connectionPos_ >= readyConnections_.size())\n            {\n                connPtr = readyConnections_[0];\n                connectionPos_ = 1;\n            }\n            else\n            {\n                connPtr = readyConnections_[connectionPos_++];\n            }\n        }\n    }\n    if (connPtr)\n    {\n        connPtr->sendvCommand(command,\n                              std::move(newResultCallback),\n                              std::move(newExceptionCallback),\n                              ap);\n    }\n    else\n    {\n        LOG_TRACE << \"no connection available, push command to buffer\";\n        auto formattedCmd = RedisConnection::getFormattedCommand(command, ap);\n        auto bfCbPtr =\n            std::make_shared<std::function<void(const RedisConnectionPtr &)>>(\n                [resultCallback = std::move(newResultCallback),\n                 exceptionCallback = std::move(newExceptionCallback),\n                 formattedCmd = std::move(formattedCmd)](\n                    const RedisConnectionPtr &connPtr) mutable {\n                    connPtr->sendFormattedCommand(std::move(formattedCmd),\n                                                  std::move(resultCallback),\n                                                  std::move(exceptionCallback));\n                });\n        (*bufferCbPtr) = bfCbPtr;\n        std::lock_guard<std::mutex> lock(connectionsMutex_);\n        tasks_.emplace_back(bfCbPtr);\n    }\n    timeoutFlagPtr->runTimer();\n}\n\nstd::shared_ptr<RedisSubscriber> RedisClientImpl::newSubscriber() noexcept\n{\n    auto subscriber = std::make_shared<RedisSubscriberImpl>();\n    auto loop = loops_.getNextLoop();\n    loop->queueInLoop([this, loop, subscriber]() {\n        std::lock_guard<std::mutex> lock(connectionsMutex_);\n        connections_.insert(newSubscribeConnection(loop, subscriber));\n    });\n\n    return subscriber;\n}\n"
  },
  {
    "path": "nosql_lib/redis/src/RedisClientImpl.h",
    "content": "/**\n *\n *  @file RedisClientImpl.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include \"RedisConnection.h\"\n#include \"RedisSubscriberImpl.h\"\n#include \"SubscribeContext.h\"\n#include <drogon/nosql/RedisClient.h>\n#include <trantor/utils/NonCopyable.h>\n#include <trantor/net/EventLoopThreadPool.h>\n#include <vector>\n#include <unordered_set>\n#include <list>\n#include <future>\n\nnamespace drogon\n{\nnamespace nosql\n{\nclass RedisConnection;\nusing RedisConnectionPtr = std::shared_ptr<RedisConnection>;\n\nclass RedisClientImpl final\n    : public RedisClient,\n      public trantor::NonCopyable,\n      public std::enable_shared_from_this<RedisClientImpl>\n{\n  public:\n    RedisClientImpl(const trantor::InetAddress &serverAddress,\n                    size_t numberOfConnections,\n                    std::string username = \"\",\n                    std::string password = \"\",\n                    unsigned int db = 0);\n    void execCommandAsync(RedisResultCallback &&resultCallback,\n                          RedisExceptionCallback &&exceptionCallback,\n                          std::string_view command,\n                          ...) noexcept override;\n    ~RedisClientImpl() override;\n    std::shared_ptr<RedisSubscriber> newSubscriber() noexcept override;\n\n    RedisTransactionPtr newTransaction() noexcept(false) override\n    {\n        std::promise<RedisTransactionPtr> prom;\n        auto f = prom.get_future();\n        newTransactionAsync([&prom](const RedisTransactionPtr &transPtr) {\n            prom.set_value(transPtr);\n        });\n        auto trans = f.get();\n        if (!trans)\n        {\n            throw RedisException(\n                RedisErrorCode::kTimeout,\n                \"Timeout, no connection available for transaction\");\n        }\n        return trans;\n    }\n\n    void newTransactionAsync(\n        const std::function<void(const RedisTransactionPtr &)> &callback)\n        override;\n\n    void setTimeout(double timeout) override\n    {\n        timeout_ = timeout;\n    }\n\n    void init();\n    void closeAll() override;\n\n  private:\n    trantor::EventLoopThreadPool loops_;\n    std::mutex connectionsMutex_;\n    std::unordered_set<RedisConnectionPtr> connections_;\n    std::vector<RedisConnectionPtr> readyConnections_;\n    size_t connectionPos_{0};\n    const trantor::InetAddress serverAddr_;\n    const std::string username_;\n    const std::string password_;\n    const unsigned int db_;\n    const size_t numberOfConnections_;\n    double timeout_{-1.0};\n    std::list<std::shared_ptr<std::function<void(const RedisConnectionPtr &)>>>\n        tasks_;\n\n    RedisConnectionPtr newConnection(trantor::EventLoop *loop);\n    RedisConnectionPtr newSubscribeConnection(\n        trantor::EventLoop *loop,\n        const std::shared_ptr<RedisSubscriberImpl> &subscriber);\n\n    std::shared_ptr<RedisTransaction> makeTransaction(\n        const RedisConnectionPtr &connPtr);\n    void handleNextTask(const RedisConnectionPtr &connPtr);\n    void execCommandAsyncWithTimeout(std::string_view command,\n                                     RedisResultCallback &&resultCallback,\n                                     RedisExceptionCallback &&exceptionCallback,\n                                     va_list ap);\n};\n}  // namespace nosql\n}  // namespace drogon\n"
  },
  {
    "path": "nosql_lib/redis/src/RedisClientLockFree.cc",
    "content": "/**\n *\n *  @file RedisClientLockFree.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"RedisConnection.h\"\n#include \"RedisClientLockFree.h\"\n#include \"RedisSubscriberImpl.h\"\n#include \"RedisTransactionImpl.h\"\n#include \"../../lib/src/TaskTimeoutFlag.h\"\nusing namespace drogon::nosql;\n\nRedisClientLockFree::RedisClientLockFree(\n    const trantor::InetAddress &serverAddress,\n    size_t numberOfConnections,\n    trantor::EventLoop *loop,\n    std::string username,\n    std::string password,\n    unsigned int db)\n    : loop_(loop),\n      serverAddr_(serverAddress),\n      username_(std::move(username)),\n      password_(std::move(password)),\n      db_(db),\n      numberOfConnections_(numberOfConnections)\n{\n    assert(loop_);\n    for (size_t i = 0; i < numberOfConnections_; ++i)\n    {\n        loop_->queueInLoop([this]() { connections_.insert(newConnection()); });\n    }\n}\n\nRedisConnectionPtr RedisClientLockFree::newConnection()\n{\n    loop_->assertInLoopThread();\n    auto conn = std::make_shared<RedisConnection>(\n        serverAddr_, username_, password_, db_, loop_);\n    std::weak_ptr<RedisClientLockFree> thisWeakPtr = shared_from_this();\n    conn->setConnectCallback([thisWeakPtr](RedisConnectionPtr &&conn) {\n        auto thisPtr = thisWeakPtr.lock();\n        if (thisPtr)\n        {\n            thisPtr->readyConnections_.push_back(conn);\n            thisPtr->handleNextTask(conn);\n        }\n    });\n    conn->setDisconnectCallback([thisWeakPtr](RedisConnectionPtr &&conn) {\n        // assert(status == REDIS_CONNECTED);\n        auto thisPtr = thisWeakPtr.lock();\n        if (thisPtr)\n        {\n            thisPtr->connections_.erase(conn);\n            for (auto iter = thisPtr->readyConnections_.begin();\n                 iter != thisPtr->readyConnections_.end();\n                 ++iter)\n            {\n                if (*iter == conn)\n                {\n                    thisPtr->readyConnections_.erase(iter);\n                    break;\n                }\n            }\n            thisPtr->loop_->runAfter(2.0, [thisPtr, conn]() {\n                thisPtr->connections_.insert(thisPtr->newConnection());\n            });\n        }\n    });\n    conn->setIdleCallback([thisWeakPtr](const RedisConnectionPtr &connPtr) {\n        auto thisPtr = thisWeakPtr.lock();\n        if (thisPtr)\n        {\n            thisPtr->handleNextTask(connPtr);\n        }\n    });\n    return conn;\n}\n\nRedisConnectionPtr RedisClientLockFree::newSubscribeConnection(\n    const std::shared_ptr<RedisSubscriberImpl> &subscriber)\n{\n    loop_->assertInLoopThread();\n    auto conn = std::make_shared<RedisConnection>(\n        serverAddr_, username_, password_, db_, loop_);\n    std::weak_ptr<RedisClientLockFree> weakThis = shared_from_this();\n    std::weak_ptr<RedisSubscriberImpl> weakSub(subscriber);\n    conn->setConnectCallback([weakThis, weakSub](RedisConnectionPtr &&conn) {\n        conn->getLoop()->assertInLoopThread();  // TODO: remove\n        auto thisPtr = weakThis.lock();\n        if (!thisPtr)\n            return;\n        thisPtr->loop_->assertInLoopThread();  // TODO: remove\n        auto subPtr = weakSub.lock();\n        if (subPtr)\n        {\n            subPtr->setConnection(conn);\n            subPtr->subscribeAll();\n        }\n        else\n        {\n            thisPtr->connections_.erase(conn);\n        }\n    });\n    conn->setDisconnectCallback([weakThis, weakSub](RedisConnectionPtr &&conn) {\n        // assert(status == REDIS_CONNECTED);\n        auto thisPtr = weakThis.lock();\n        if (!thisPtr)\n            return;\n        thisPtr->connections_.erase(conn);\n        auto subPtr = weakSub.lock();\n        if (!subPtr)\n            return;\n        subPtr->clearConnection();\n\n        thisPtr->loop_->runAfter(2.0, [thisPtr, subPtr]() {\n            thisPtr->connections_.insert(\n                thisPtr->newSubscribeConnection(subPtr));\n        });\n    });\n    conn->setIdleCallback(\n        [weakThis, weakSub](const RedisConnectionPtr &connPtr) {\n            auto thisPtr = weakThis.lock();\n            if (!thisPtr)\n                return;\n            auto subPtr = weakSub.lock();\n            if (!subPtr)\n                return;\n            subPtr->subscribeNext();\n        });\n    return conn;\n}\n\nvoid RedisClientLockFree::execCommandAsync(\n    RedisResultCallback &&resultCallback,\n    RedisExceptionCallback &&exceptionCallback,\n    std::string_view command,\n    ...) noexcept\n{\n    loop_->assertInLoopThread();\n    if (timeout_ > 0.0)\n    {\n        va_list args;\n        va_start(args, command);\n        execCommandAsyncWithTimeout(command,\n                                    std::move(resultCallback),\n                                    std::move(exceptionCallback),\n                                    args);\n        va_end(args);\n        return;\n    }\n    RedisConnectionPtr connPtr;\n    {\n        if (!readyConnections_.empty())\n        {\n            if (connectionPos_ >= readyConnections_.size())\n            {\n                connPtr = readyConnections_[0];\n                connectionPos_ = 1;\n            }\n            else\n            {\n                connPtr = readyConnections_[connectionPos_++];\n            }\n        }\n    }\n    if (connPtr)\n    {\n        va_list args;\n        va_start(args, command);\n        connPtr->sendvCommand(command,\n                              std::move(resultCallback),\n                              std::move(exceptionCallback),\n                              args);\n        va_end(args);\n    }\n    else\n    {\n        LOG_TRACE << \"no connection available, push command to buffer\";\n        std::weak_ptr<RedisClientLockFree> thisWeakPtr = shared_from_this();\n        va_list args;\n        va_start(args, command);\n        auto formattedCmd = RedisConnection::getFormattedCommand(command, args);\n        va_end(args);\n        tasks_.emplace_back(\n            std::make_shared<std::function<void(const RedisConnectionPtr &)>>(\n                [thisWeakPtr,\n                 resultCallback = std::move(resultCallback),\n                 exceptionCallback = std::move(exceptionCallback),\n                 formattedCmd = std::move(formattedCmd)](\n                    const RedisConnectionPtr &connPtr) mutable {\n                    connPtr->sendFormattedCommand(std::move(formattedCmd),\n                                                  std::move(resultCallback),\n                                                  std::move(exceptionCallback));\n                }));\n    }\n}\n\nRedisClientLockFree::~RedisClientLockFree()\n{\n    closeAll();\n}\n\nvoid RedisClientLockFree::closeAll()\n{\n    for (auto &conn : connections_)\n    {\n        conn->disconnect();\n    }\n    readyConnections_.clear();\n    connections_.clear();\n}\n\nvoid RedisClientLockFree::newTransactionAsync(\n    const std::function<void(const std::shared_ptr<RedisTransaction> &)>\n        &callback)\n{\n    loop_->assertInLoopThread();\n    RedisConnectionPtr connPtr;\n\n    if (!readyConnections_.empty())\n    {\n        connPtr = readyConnections_[readyConnections_.size() - 1];\n        readyConnections_.resize(readyConnections_.size() - 1);\n    }\n\n    if (connPtr)\n    {\n        callback(makeTransaction(connPtr));\n    }\n    else\n    {\n        if (timeout_ <= 0.0)\n        {\n            std::weak_ptr<RedisClientLockFree> thisWeakPtr = shared_from_this();\n            tasks_.emplace_back(\n                std::make_shared<\n                    std::function<void(const RedisConnectionPtr &)>>(\n                    [callback,\n                     thisWeakPtr](const RedisConnectionPtr & /*connPtr*/) {\n                        auto thisPtr = thisWeakPtr.lock();\n                        if (thisPtr)\n                        {\n                            thisPtr->newTransactionAsync(callback);\n                        }\n                    }));\n        }\n        else\n        {\n            auto callbackPtr = std::make_shared<\n                std::function<void(const std::shared_ptr<RedisTransaction> &)>>(\n                callback);\n            auto transCbPtr = std::make_shared<std::weak_ptr<\n                std::function<void(const RedisConnectionPtr &)>>>();\n            auto timeoutFlagPtr = std::make_shared<TaskTimeoutFlag>(\n                loop_,\n                std::chrono::duration<double>(timeout_),\n                [callbackPtr, transCbPtr, this]() {\n                    auto cbPtr = (*transCbPtr).lock();\n                    if (cbPtr)\n                    {\n                        for (auto iter = tasks_.begin(); iter != tasks_.end();\n                             ++iter)\n                        {\n                            if (cbPtr == *iter)\n                            {\n                                tasks_.erase(iter);\n                                break;\n                            }\n                        }\n                    }\n                    (*callbackPtr)(nullptr);\n                });\n            std::weak_ptr<RedisClientLockFree> thisWeakPtr = shared_from_this();\n            auto bufferCbPtr = std::make_shared<\n                std::function<void(const RedisConnectionPtr &)>>(\n                [callbackPtr, timeoutFlagPtr, thisWeakPtr](\n                    const RedisConnectionPtr & /*connPtr*/) {\n                    auto thisPtr = thisWeakPtr.lock();\n                    if (thisPtr)\n                    {\n                        if (timeoutFlagPtr->done())\n                        {\n                            return;\n                        }\n                        thisPtr->newTransactionAsync(*callbackPtr);\n                    }\n                });\n            tasks_.emplace_back(bufferCbPtr);\n\n            (*transCbPtr) = bufferCbPtr;\n            timeoutFlagPtr->runTimer();\n        }\n    }\n}\n\nstd::shared_ptr<RedisTransaction> RedisClientLockFree::makeTransaction(\n    const RedisConnectionPtr &connPtr)\n{\n    std::weak_ptr<RedisClientLockFree> thisWeakPtr = shared_from_this();\n    auto trans = std::shared_ptr<RedisTransactionImpl>(\n        new RedisTransactionImpl(connPtr),\n        [thisWeakPtr, connPtr](RedisTransactionImpl *p) {\n            delete p;\n            auto thisPtr = thisWeakPtr.lock();\n            if (thisPtr)\n            {\n                thisPtr->readyConnections_.push_back(connPtr);\n                thisPtr->handleNextTask(connPtr);\n            }\n        });\n    trans->doBegin();\n    return trans;\n}\n\nvoid RedisClientLockFree::handleNextTask(const RedisConnectionPtr &connPtr)\n{\n    loop_->assertInLoopThread();\n    std::shared_ptr<std::function<void(const RedisConnectionPtr &)>> taskPtr;\n\n    if (!tasks_.empty())\n    {\n        taskPtr = std::move(tasks_.front());\n        tasks_.pop_front();\n    }\n\n    if (taskPtr && (*taskPtr))\n    {\n        (*taskPtr)(connPtr);\n    }\n}\n\nvoid RedisClientLockFree::execCommandAsyncWithTimeout(\n    std::string_view command,\n    RedisResultCallback &&resultCallback,\n    RedisExceptionCallback &&exceptionCallback,\n    va_list ap)\n{\n    auto expCbPtr =\n        std::make_shared<RedisExceptionCallback>(std::move(exceptionCallback));\n    auto bufferCbPtr = std::make_shared<\n        std::weak_ptr<std::function<void(const RedisConnectionPtr &)>>>();\n    auto timeoutFlagPtr = std::make_shared<TaskTimeoutFlag>(\n        loop_,\n        std::chrono::duration<double>(timeout_),\n        [expCbPtr, bufferCbPtr, this]() {\n            auto bfCbPtr = (*bufferCbPtr).lock();\n            if (bfCbPtr)\n            {\n                for (auto iter = tasks_.begin(); iter != tasks_.end(); ++iter)\n                {\n                    if (bfCbPtr == *iter)\n                    {\n                        tasks_.erase(iter);\n                        break;\n                    }\n                }\n            }\n            if (*expCbPtr)\n            {\n                (*expCbPtr)(RedisException(RedisErrorCode::kTimeout,\n                                           \"Command execution timeout\"));\n            }\n        });\n    auto newResultCallback = [resultCallback = std::move(resultCallback),\n                              timeoutFlagPtr](const RedisResult &result) {\n        if (timeoutFlagPtr->done())\n        {\n            return;\n        }\n        if (resultCallback)\n        {\n            resultCallback(result);\n        }\n    };\n    auto newExceptionCallback = [expCbPtr,\n                                 timeoutFlagPtr](const RedisException &err) {\n        if (timeoutFlagPtr->done())\n        {\n            return;\n        }\n        if (*expCbPtr)\n        {\n            (*expCbPtr)(err);\n        }\n    };\n    RedisConnectionPtr connPtr;\n    {\n        if (!readyConnections_.empty())\n        {\n            if (connectionPos_ >= readyConnections_.size())\n            {\n                connPtr = readyConnections_[0];\n                connectionPos_ = 1;\n            }\n            else\n            {\n                connPtr = readyConnections_[connectionPos_++];\n            }\n        }\n    }\n    if (connPtr)\n    {\n        connPtr->sendvCommand(command,\n                              std::move(newResultCallback),\n                              std::move(newExceptionCallback),\n                              ap);\n    }\n    else\n    {\n        LOG_TRACE << \"no connection available, push command to buffer\";\n        auto formattedCmd = RedisConnection::getFormattedCommand(command, ap);\n        auto bfCbPtr =\n            std::make_shared<std::function<void(const RedisConnectionPtr &)>>(\n                [resultCallback = std::move(newResultCallback),\n                 exceptionCallback = std::move(newExceptionCallback),\n                 formattedCmd = std::move(formattedCmd)](\n                    const RedisConnectionPtr &connPtr) mutable {\n                    connPtr->sendFormattedCommand(std::move(formattedCmd),\n                                                  std::move(resultCallback),\n                                                  std::move(exceptionCallback));\n                });\n        (*bufferCbPtr) = bfCbPtr;\n        tasks_.emplace_back(bfCbPtr);\n    }\n    timeoutFlagPtr->runTimer();\n}\n\nstd::shared_ptr<RedisSubscriber> RedisClientLockFree::newSubscriber() noexcept\n{\n    auto subscriber = std::make_shared<RedisSubscriberImpl>();\n    loop_->runInLoop([this, subscriber]() {\n        connections_.insert(newSubscribeConnection(subscriber));\n    });\n\n    return subscriber;\n}\n"
  },
  {
    "path": "nosql_lib/redis/src/RedisClientLockFree.h",
    "content": "/**\n *\n *  @file RedisClientLockFree.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include \"RedisConnection.h\"\n#include \"RedisSubscriberImpl.h\"\n#include <drogon/nosql/RedisClient.h>\n#include <trantor/utils/NonCopyable.h>\n#include <trantor/net/EventLoopThreadPool.h>\n#include <vector>\n#include <unordered_set>\n#include <list>\n#include <future>\n\nnamespace drogon\n{\nnamespace nosql\n{\nclass RedisConnection;\nusing RedisConnectionPtr = std::shared_ptr<RedisConnection>;\n\nclass RedisClientLockFree final\n    : public RedisClient,\n      public trantor::NonCopyable,\n      public std::enable_shared_from_this<RedisClientLockFree>\n{\n  public:\n    RedisClientLockFree(const trantor::InetAddress &serverAddress,\n                        size_t numberOfConnections,\n                        trantor::EventLoop *loop,\n                        std::string username = \"\",\n                        std::string password = \"\",\n                        unsigned int db = 0);\n    void execCommandAsync(RedisResultCallback &&resultCallback,\n                          RedisExceptionCallback &&exceptionCallback,\n                          std::string_view command,\n                          ...) noexcept override;\n    ~RedisClientLockFree() override;\n    std::shared_ptr<RedisSubscriber> newSubscriber() noexcept override;\n\n    RedisTransactionPtr newTransaction() override\n    {\n        LOG_ERROR\n            << \"You can't use the synchronous interface in the fast redis \"\n               \"client, please use the asynchronous version \"\n               \"(newTransactionAsync)\";\n        assert(0);\n        return nullptr;\n    }\n\n    void newTransactionAsync(\n        const std::function<void(const RedisTransactionPtr &)> &callback)\n        override;\n\n    void setTimeout(double timeout) override\n    {\n        timeout_ = timeout;\n    }\n\n    void closeAll() override;\n\n  private:\n    trantor::EventLoop *loop_;\n    std::unordered_set<RedisConnectionPtr> connections_;\n    std::vector<RedisConnectionPtr> readyConnections_;\n    size_t connectionPos_{0};\n    const trantor::InetAddress serverAddr_;\n    const std::string username_;\n    const std::string password_;\n    const unsigned int db_;\n    const size_t numberOfConnections_;\n    std::list<std::shared_ptr<std::function<void(const RedisConnectionPtr &)>>>\n        tasks_;\n    double timeout_{-1.0};\n\n    RedisConnectionPtr newConnection();\n    RedisConnectionPtr newSubscribeConnection(\n        const std::shared_ptr<RedisSubscriberImpl> &);\n    std::shared_ptr<RedisTransaction> makeTransaction(\n        const RedisConnectionPtr &connPtr);\n    void handleNextTask(const RedisConnectionPtr &connPtr);\n    void execCommandAsyncWithTimeout(std::string_view command,\n                                     RedisResultCallback &&resultCallback,\n                                     RedisExceptionCallback &&exceptionCallback,\n                                     va_list ap);\n};\n}  // namespace nosql\n}  // namespace drogon\n"
  },
  {
    "path": "nosql_lib/redis/src/RedisClientManager.cc",
    "content": "/**\n *\n *  @file RedisClientManager.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"../../lib/src/RedisClientManager.h\"\n#include \"RedisClientLockFree.h\"\n#include \"RedisClientImpl.h\"\n\n#include <algorithm>\n\nusing namespace drogon::nosql;\nusing namespace drogon;\n\nvoid RedisClientManager::createRedisClients(\n    const std::vector<trantor::EventLoop *> &ioLoops)\n{\n    assert(redisClientsMap_.empty());\n    assert(redisFastClientsMap_.empty());\n    for (auto &redisInfo : redisInfos_)\n    {\n        if (redisInfo.isFast_)\n        {\n            redisFastClientsMap_[redisInfo.name_] =\n                IOThreadStorage<RedisClientPtr>();\n            redisFastClientsMap_[redisInfo.name_].init([&](RedisClientPtr &c,\n                                                           size_t idx) {\n                assert(idx == ioLoops[idx]->index());\n                LOG_TRACE << \"create fast redis client for the thread \" << idx;\n                c = std::make_shared<RedisClientLockFree>(\n                    trantor::InetAddress(redisInfo.addr_, redisInfo.port_),\n                    redisInfo.connectionNumber_,\n                    ioLoops[idx],\n                    redisInfo.username_,\n                    redisInfo.password_,\n                    redisInfo.db_);\n                if (redisInfo.timeout_ > 0.0)\n                {\n                    c->setTimeout(redisInfo.timeout_);\n                }\n            });\n        }\n        else\n        {\n            auto clientPtr = std::make_shared<RedisClientImpl>(\n                trantor::InetAddress(redisInfo.addr_, redisInfo.port_),\n                redisInfo.connectionNumber_,\n                redisInfo.username_,\n                redisInfo.password_,\n                redisInfo.db_);\n            if (redisInfo.timeout_ > 0.0)\n            {\n                clientPtr->setTimeout(redisInfo.timeout_);\n            }\n            clientPtr->init();\n            redisClientsMap_[redisInfo.name_] = std::move(clientPtr);\n        }\n    }\n}\n\nvoid RedisClientManager::createRedisClient(const std::string &name,\n                                           const std::string &addr,\n                                           unsigned short port,\n                                           const std::string &username,\n                                           const std::string &password,\n                                           const size_t connectionNum,\n                                           const bool isFast,\n                                           double timeout,\n                                           unsigned int db)\n{\n    RedisInfo info;\n    info.name_ = name;\n    info.addr_ = addr;\n    info.port_ = port;\n    info.username_ = username;\n    info.password_ = password;\n    info.connectionNumber_ = connectionNum;\n    info.isFast_ = isFast;\n    info.timeout_ = timeout;\n    info.db_ = db;\n\n    redisInfos_.emplace_back(std::move(info));\n}\n\n// bool RedisClientManager::areAllRedisClientsAvailable() const noexcept\n//{\n//    for (auto const &pair : redisClientsMap_)\n//    {\n//        if (!(pair.second)->hasAvailableConnections())\n//            return false;\n//    }\n//    auto loop = trantor::EventLoop::getEventLoopOfCurrentThread();\n//    if (loop && loop->index() < app().getThreadNum())\n//    {\n//        for (auto const &pair : redisFastClientsMap_)\n//        {\n//            if (!(*(pair.second))->hasAvailableConnections())\n//                return false;\n//        }\n//    }\n//    return true;\n//}\n\nRedisClientManager::~RedisClientManager()\n{\n    for (auto &pair : redisClientsMap_)\n    {\n        pair.second->closeAll();\n    }\n    for (auto &pair : redisFastClientsMap_)\n    {\n        pair.second.init([](RedisClientPtr &clientPtr, size_t index) {\n            // the main loop;\n            std::promise<void> p;\n            auto f = p.get_future();\n            drogon::getIOThreadStorageLoop(index)->runInLoop(\n                [&clientPtr, &p]() {\n                    clientPtr->closeAll();\n                    p.set_value();\n                });\n            f.get();\n        });\n    }\n}\n"
  },
  {
    "path": "nosql_lib/redis/src/RedisConnection.cc",
    "content": "/**\n *\n *  @file RedisConnection.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"RedisConnection.h\"\n#include <drogon/nosql/RedisResult.h>\n#include <future>\n#include <string.h>\n\n#ifdef _MSC_VER\n#define strcasecmp _stricmp\n#endif\n\nusing namespace drogon::nosql;\n\nRedisConnection::RedisConnection(const trantor::InetAddress &serverAddress,\n                                 const std::string &username,\n                                 const std::string &password,\n                                 unsigned int db,\n                                 trantor::EventLoop *loop)\n    : serverAddr_(serverAddress),\n      username_(username),\n      password_(password),\n      db_(db),\n      loop_(loop)\n{\n    assert(loop_);\n    loop_->queueInLoop([this]() { startConnectionInLoop(); });\n}\n\nvoid RedisConnection::startConnectionInLoop()\n{\n    loop_->assertInLoopThread();\n    assert(!redisContext_);\n\n    redisContext_ =\n        ::redisAsyncConnect(serverAddr_.toIp().c_str(), serverAddr_.toPort());\n    status_ = ConnectStatus::kConnecting;\n    if (redisContext_->err)\n    {\n        LOG_ERROR << \"Error: \" << redisContext_->errstr;\n\n        if (disconnectCallback_)\n        {\n            disconnectCallback_(shared_from_this());\n        }\n\n        // Strange things have happened. In some kinds of connection errors,\n        // such as setsockopt errors, hiredis already set redisContext_->c.fd to\n        // -1, but the tcp connection stays in ESTABLISHED status. And there is\n        // no way for us to obtain the fd of that socket nor close it. This\n        // probably is a bug of hiredis.\n        return;\n    }\n\n    redisContext_->ev.addWrite = addWrite;\n    redisContext_->ev.delWrite = delWrite;\n    redisContext_->ev.addRead = addRead;\n    redisContext_->ev.delRead = delRead;\n    redisContext_->ev.cleanup = cleanup;\n    redisContext_->ev.data = this;\n\n    channel_ = std::make_unique<trantor::Channel>(loop_, redisContext_->c.fd);\n    channel_->setReadCallback([this]() { handleRedisRead(); });\n    channel_->setWriteCallback([this]() { handleRedisWrite(); });\n    redisAsyncSetConnectCallback(\n        redisContext_, [](const redisAsyncContext *context, int status) {\n            auto thisPtr = static_cast<RedisConnection *>(context->ev.data);\n            if (status != REDIS_OK)\n            {\n                LOG_ERROR << \"Failed to connect to \"\n                          << thisPtr->serverAddr_.toIpPort() << \"! \"\n                          << context->errstr;\n                thisPtr->handleDisconnect();\n                if (thisPtr->disconnectCallback_)\n                {\n                    thisPtr->disconnectCallback_(thisPtr->shared_from_this());\n                }\n            }\n            else\n            {\n                LOG_TRACE << \"Connected successfully to \"\n                          << thisPtr->serverAddr_.toIpPort();\n                if (thisPtr->password_.empty())\n                {\n                    if (thisPtr->db_ == 0)\n                    {\n                        thisPtr->status_ = ConnectStatus::kConnected;\n                        if (thisPtr->connectCallback_)\n                        {\n                            thisPtr->connectCallback_(\n                                thisPtr->shared_from_this());\n                        }\n                    }\n                }\n                else\n                {\n                    if (thisPtr->username_.empty())\n                    {\n                        std::weak_ptr<RedisConnection> weakThisPtr =\n                            thisPtr->shared_from_this();\n                        thisPtr->sendCommand(\n                            [weakThisPtr](const RedisResult &r) {\n                                auto thisPtr = weakThisPtr.lock();\n                                if (!thisPtr)\n                                    return;\n                                if (r.asString() == \"OK\")\n                                {\n                                    if (thisPtr->db_ == 0)\n                                    {\n                                        thisPtr->status_ =\n                                            ConnectStatus::kConnected;\n                                        if (thisPtr->connectCallback_)\n                                            thisPtr->connectCallback_(\n                                                thisPtr->shared_from_this());\n                                    }\n                                }\n                                else\n                                {\n                                    LOG_ERROR << r.asString();\n                                    thisPtr->disconnect();\n                                    thisPtr->status_ = ConnectStatus::kEnd;\n                                }\n                            },\n                            [weakThisPtr](const std::exception &err) {\n                                LOG_ERROR << err.what();\n                                auto thisPtr = weakThisPtr.lock();\n                                if (!thisPtr)\n                                    return;\n                                thisPtr->disconnect();\n                                thisPtr->status_ = ConnectStatus::kEnd;\n                            },\n                            \"auth %s\",\n                            thisPtr->password_.c_str());\n                    }\n                    else\n                    {\n                        std::weak_ptr<RedisConnection> weakThisPtr =\n                            thisPtr->shared_from_this();\n                        thisPtr->sendCommand(\n                            [weakThisPtr](const RedisResult &r) {\n                                auto thisPtr = weakThisPtr.lock();\n                                if (!thisPtr)\n                                    return;\n                                if (r.asString() == \"OK\")\n                                {\n                                    if (thisPtr->db_ == 0)\n                                    {\n                                        thisPtr->status_ =\n                                            ConnectStatus::kConnected;\n                                        if (thisPtr->connectCallback_)\n                                            thisPtr->connectCallback_(\n                                                thisPtr->shared_from_this());\n                                    }\n                                }\n                                else\n                                {\n                                    LOG_ERROR << r.asString();\n                                    thisPtr->disconnect();\n                                    thisPtr->status_ = ConnectStatus::kEnd;\n                                }\n                            },\n                            [weakThisPtr](const std::exception &err) {\n                                LOG_ERROR << err.what();\n                                auto thisPtr = weakThisPtr.lock();\n                                if (!thisPtr)\n                                    return;\n                                thisPtr->disconnect();\n                                thisPtr->status_ = ConnectStatus::kEnd;\n                            },\n                            \"auth %s %s\",\n                            thisPtr->username_.c_str(),\n                            thisPtr->password_.c_str());\n                    }\n                }\n\n                if (thisPtr->db_ != 0)\n                {\n                    LOG_TRACE << \"redis db:\" << thisPtr->db_;\n                    std::weak_ptr<RedisConnection> weakThisPtr =\n                        thisPtr->shared_from_this();\n                    thisPtr->sendCommand(\n                        [weakThisPtr](const RedisResult &r) {\n                            auto thisPtr = weakThisPtr.lock();\n                            if (!thisPtr)\n                                return;\n                            if (r.asString() == \"OK\")\n                            {\n                                thisPtr->status_ = ConnectStatus::kConnected;\n                                if (thisPtr->connectCallback_)\n                                {\n                                    thisPtr->connectCallback_(\n                                        thisPtr->shared_from_this());\n                                }\n                            }\n                            else\n                            {\n                                LOG_ERROR << r.asString();\n                                thisPtr->disconnect();\n                                thisPtr->status_ = ConnectStatus::kEnd;\n                            }\n                        },\n                        [weakThisPtr](const std::exception &err) {\n                            LOG_ERROR << err.what();\n                            auto thisPtr = weakThisPtr.lock();\n                            if (!thisPtr)\n                                return;\n                            thisPtr->disconnect();\n                            thisPtr->status_ = ConnectStatus::kEnd;\n                        },\n                        \"select %u\",\n                        thisPtr->db_);\n                }\n            }\n        });\n    redisAsyncSetDisconnectCallback(\n        redisContext_, [](const redisAsyncContext *context, int /*status*/) {\n            auto thisPtr = static_cast<RedisConnection *>(context->ev.data);\n\n            thisPtr->handleDisconnect();\n            if (thisPtr->disconnectCallback_)\n            {\n                thisPtr->disconnectCallback_(thisPtr->shared_from_this());\n            }\n\n            LOG_TRACE << \"Disconnected from \"\n                      << thisPtr->serverAddr_.toIpPort();\n        });\n}\n\nvoid RedisConnection::handleDisconnect()\n{\n    LOG_TRACE << \"handleDisconnect\";\n    loop_->assertInLoopThread();\n    while ((!resultCallbacks_.empty()) && (!exceptionCallbacks_.empty()))\n    {\n        if (exceptionCallbacks_.front())\n        {\n            exceptionCallbacks_.front()(\n                RedisException(RedisErrorCode::kConnectionBroken,\n                               \"Connection is broken\"));\n        }\n        resultCallbacks_.pop();\n        exceptionCallbacks_.pop();\n    }\n    status_ = ConnectStatus::kEnd;\n    channel_->disableAll();\n    channel_->remove();\n    redisContext_->ev.addWrite = nullptr;\n    redisContext_->ev.delWrite = nullptr;\n    redisContext_->ev.addRead = nullptr;\n    redisContext_->ev.delRead = nullptr;\n    redisContext_->ev.cleanup = nullptr;\n    redisContext_->ev.data = nullptr;\n}\n\nvoid RedisConnection::addWrite(void *userData)\n{\n    auto thisPtr = static_cast<RedisConnection *>(userData);\n    assert(thisPtr->channel_);\n    thisPtr->channel_->enableWriting();\n}\n\nvoid RedisConnection::delWrite(void *userData)\n{\n    auto thisPtr = static_cast<RedisConnection *>(userData);\n    assert(thisPtr->channel_);\n    thisPtr->channel_->disableWriting();\n}\n\nvoid RedisConnection::addRead(void *userData)\n{\n    auto thisPtr = static_cast<RedisConnection *>(userData);\n    assert(thisPtr->channel_);\n    thisPtr->channel_->enableReading();\n}\n\nvoid RedisConnection::delRead(void *userData)\n{\n    auto thisPtr = static_cast<RedisConnection *>(userData);\n    assert(thisPtr->channel_);\n    thisPtr->channel_->disableReading();\n}\n\nvoid RedisConnection::cleanup(void * /*userData*/)\n{\n    LOG_TRACE << \"cleanup\";\n}\n\nvoid RedisConnection::handleRedisRead()\n{\n    if (status_ != ConnectStatus::kEnd)\n    {\n        redisAsyncHandleRead(redisContext_);\n    }\n}\n\nvoid RedisConnection::handleRedisWrite()\n{\n    if (status_ != ConnectStatus::kEnd)\n    {\n        redisAsyncHandleWrite(redisContext_);\n    }\n}\n\nvoid RedisConnection::sendCommandInLoop(\n    const std::string &command,\n    RedisResultCallback &&resultCallback,\n    RedisExceptionCallback &&exceptionCallback)\n{\n    resultCallbacks_.emplace(std::move(resultCallback));\n    exceptionCallbacks_.emplace(std::move(exceptionCallback));\n\n    redisAsyncFormattedCommand(\n        redisContext_,\n        [](redisAsyncContext *context, void *r, void * /*userData*/) {\n            auto thisPtr = static_cast<RedisConnection *>(context->ev.data);\n            thisPtr->handleResult(static_cast<redisReply *>(r));\n        },\n        nullptr,\n        command.c_str(),\n        command.length());\n}\n\nvoid RedisConnection::handleResult(redisReply *result)\n{\n    auto commandCallback = std::move(resultCallbacks_.front());\n    resultCallbacks_.pop();\n    auto exceptionCallback = std::move(exceptionCallbacks_.front());\n    exceptionCallbacks_.pop();\n    if (result && result->type != REDIS_REPLY_ERROR)\n    {\n        commandCallback(RedisResult(result));\n    }\n    else\n    {\n        if (result)\n        {\n            exceptionCallback(\n                RedisException(RedisErrorCode::kRedisError,\n                               std::string{result->str, result->len}));\n        }\n        else\n        {\n            exceptionCallback(RedisException(RedisErrorCode::kConnectionBroken,\n                                             \"Network failure\"));\n        }\n    }\n    if (resultCallbacks_.empty())\n    {\n        assert(exceptionCallbacks_.empty());\n        if (idleCallback_)\n        {\n            idleCallback_(shared_from_this());\n        }\n    }\n}\n\nvoid RedisConnection::disconnect()\n{\n    auto thisPtr = shared_from_this();\n    loop_->queueInLoop(\n        [thisPtr]() { redisAsyncDisconnect(thisPtr->redisContext_); });\n}\n\nvoid RedisConnection::sendSubscribe(\n    const std::shared_ptr<SubscribeContext> &subCtx)\n{\n    if (loop_->isInLoopThread())\n    {\n        sendSubscribeInLoop(subCtx);\n    }\n    else\n    {\n        loop_->queueInLoop([this, subCtx]() { sendSubscribeInLoop(subCtx); });\n    }\n}\n\nvoid RedisConnection::sendUnsubscribe(\n    const std::shared_ptr<SubscribeContext> &subCtx)\n{\n    if (loop_->isInLoopThread())\n    {\n        sendUnsubscribeInLoop(subCtx);\n    }\n    else\n    {\n        loop_->queueInLoop([this, subCtx]() { sendUnsubscribeInLoop(subCtx); });\n    }\n}\n\nvoid RedisConnection::sendSubscribeInLoop(\n    const std::shared_ptr<SubscribeContext> &subCtx)\n{\n    if (!subCtx->alive())\n    {\n        // Unsub-ed by somewhere else\n        return;\n    }\n    subContexts_.emplace(subCtx->contextId(), subCtx);\n    redisAsyncFormattedCommand(\n        redisContext_,\n        [](redisAsyncContext *context, void *r, void *subCtx) {\n            auto thisPtr = static_cast<RedisConnection *>(context->ev.data);\n            thisPtr->handleSubscribeResult(static_cast<redisReply *>(r),\n                                           static_cast<SubscribeContext *>(\n                                               subCtx));\n        },\n        subCtx.get(),\n        subCtx->subscribeCommand().c_str(),\n        subCtx->subscribeCommand().size());\n}\n\nvoid RedisConnection::sendUnsubscribeInLoop(\n    const std::shared_ptr<SubscribeContext> &subCtx)\n{\n    // There is a Hiredis issue here\n    // The un-sub callback will not be called, sub callback will be called\n    // instead, with first element in result as \"unsubscribe\".\n    // This problem is fixed in 2021-12-02 commit da5a4ff, but\n    // have not been released as a tag.\n    // Here we just register a same function to deal with both situation.\n    redisAsyncFormattedCommand(\n        redisContext_,\n        [](redisAsyncContext *context, void *r, void *subCtx) {\n            auto thisPtr = static_cast<RedisConnection *>(context->ev.data);\n            thisPtr->handleSubscribeResult(static_cast<redisReply *>(r),\n                                           static_cast<SubscribeContext *>(\n                                               subCtx));\n        },\n        subCtx.get(),\n        subCtx->unsubscribeCommand().c_str(),\n        subCtx->unsubscribeCommand().size());\n}\n\nvoid RedisConnection::handleSubscribeResult(redisReply *result,\n                                            SubscribeContext *subCtx)\n{\n    if (result && result->type == REDIS_REPLY_ARRAY && result->elements >= 3 &&\n        result->element[0]->type == REDIS_REPLY_STRING)\n    {\n        const char *type = result->element[0]->str;\n        int isPattern = (type[0] == 'p' || type[0] == 'P') ? 1 : 0;\n        if (isPattern)\n        {\n            type += 1;\n        }\n        if (strcasecmp(type, \"message\") == 0)\n        {\n            std::string channel(result->element[1 + isPattern]->str,\n                                result->element[1 + isPattern]->len);\n            std::string message(result->element[2 + isPattern]->str,\n                                result->element[2 + isPattern]->len);\n            if (!subCtx->alive())\n            {\n                LOG_DEBUG << \"Subscribe callback receive message, but \"\n                             \"context is no \"\n                             \"longer alive\"\n                          << \", channel: \" << channel\n                          << \", message: \" << message;\n            }\n            else\n            {\n                subCtx->onMessage(channel, message);\n            }\n            // Message callback, no need to call idleCallback_\n            return;\n        }\n\n        std::string channel(result->element[1]->str, result->element[1]->len);\n        long long number = result->element[2]->integer;\n\n        // On channel subscribed\n        if (strcasecmp(type, \"subscribe\") == 0)\n        {\n            subCtx->onSubscribe(channel, number);\n        }\n        // On channel unsubscribed\n        else if (strcasecmp(type, \"unsubscribe\") == 0)\n        {\n            subCtx->onUnsubscribe(channel, number);\n            subContexts_.erase(subCtx->contextId());\n        }\n        // Should not happen\n        else\n        {\n            LOG_ERROR << \"Unknown redis response: \" << result->element[0]->str;\n            // Shouldn't let message from another endpoint to abort this\n            // program. So no assert(false) here.\n        }\n    }\n    else if (!result)\n    {\n        // When connection close, if a channel has been subscribed,\n        // this callback will be called with empty result.\n        LOG_DEBUG << \"Empty result (connection lost)\";\n    }\n    else if (result->type == REDIS_REPLY_ERROR)\n    {\n        LOG_ERROR << \"Subscribe callback receive error result: \" << result->str;\n    }\n    else\n    {\n        LOG_ERROR << \"Subscribe callback receive error result type: \"\n                  << result->type;\n    }\n\n    if (idleCallback_)\n    {\n        idleCallback_(shared_from_this());\n    }\n}\n"
  },
  {
    "path": "nosql_lib/redis/src/RedisConnection.h",
    "content": "/**\n *\n *  @file RedisConnection.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <string_view>\n#include <drogon/nosql/RedisException.h>\n#include <drogon/nosql/RedisResult.h>\n#include <drogon/utils/Utilities.h>\n#include <trantor/utils/NonCopyable.h>\n#include <trantor/net/InetAddress.h>\n#include <trantor/net/EventLoop.h>\n#include <trantor/net/Channel.h>\n#include <hiredis/async.h>\n#include <hiredis/hiredis.h>\n#include <memory>\n#include <queue>\n\n#include \"SubscribeContext.h\"\n\nnamespace drogon\n{\nnamespace nosql\n{\nenum class ConnectStatus\n{\n    kNone = 0,\n    kConnecting,\n    kConnected,\n    kEnd\n};\n\nclass RedisConnection : public trantor::NonCopyable,\n                        public std::enable_shared_from_this<RedisConnection>\n{\n  public:\n    RedisConnection(const trantor::InetAddress &serverAddress,\n                    const std::string &username,\n                    const std::string &password,\n                    unsigned int db,\n                    trantor::EventLoop *loop);\n\n    void setConnectCallback(\n        const std::function<void(std::shared_ptr<RedisConnection> &&)>\n            &callback)\n    {\n        connectCallback_ = callback;\n    }\n\n    void setDisconnectCallback(\n        const std::function<void(std::shared_ptr<RedisConnection> &&)>\n            &callback)\n    {\n        disconnectCallback_ = callback;\n    }\n\n    void setIdleCallback(\n        const std::function<void(const std::shared_ptr<RedisConnection> &)>\n            &callback)\n    {\n        idleCallback_ = callback;\n    }\n\n    static std::string getFormattedCommand(const std::string_view &command,\n                                           va_list ap) noexcept(false)\n    {\n        char *cmd{nullptr};\n        auto len = redisvFormatCommand(&cmd, command.data(), ap);\n        if (len == -1)\n        {\n            throw RedisException(RedisErrorCode::kInternalError,\n                                 \"Out of memory\");\n        }\n        else if (len == -2)\n        {\n            throw RedisException(RedisErrorCode::kInternalError,\n                                 \"Invalid format string\");\n        }\n        else if (len <= 0)\n        {\n            throw RedisException(RedisErrorCode::kInternalError,\n                                 \"Unknown format error\");\n        }\n        std::string fullCommand{cmd, static_cast<size_t>(len)};\n        redisFreeCommand(cmd);\n        return fullCommand;\n    }\n\n    void sendFormattedCommand(std::string &&command,\n                              RedisResultCallback &&resultCallback,\n                              RedisExceptionCallback &&exceptionCallback)\n    {\n        if (loop_->isInLoopThread())\n        {\n            sendCommandInLoop(command,\n                              std::move(resultCallback),\n                              std::move(exceptionCallback));\n        }\n        else\n        {\n            loop_->queueInLoop(\n                [this,\n                 callback = std::move(resultCallback),\n                 exceptionCallback = std::move(exceptionCallback),\n                 command = std::move(command)]() mutable {\n                    sendCommandInLoop(command,\n                                      std::move(callback),\n                                      std::move(exceptionCallback));\n                });\n        }\n    }\n\n    void sendvCommand(std::string_view command,\n                      RedisResultCallback &&resultCallback,\n                      RedisExceptionCallback &&exceptionCallback,\n                      va_list ap)\n    {\n        LOG_TRACE << \"redis command: \" << command;\n        try\n        {\n            auto fullCommand = getFormattedCommand(command, ap);\n            if (loop_->isInLoopThread())\n            {\n                sendCommandInLoop(fullCommand,\n                                  std::move(resultCallback),\n                                  std::move(exceptionCallback));\n            }\n            else\n            {\n                loop_->queueInLoop(\n                    [this,\n                     callback = std::move(resultCallback),\n                     exceptionCallback = std::move(exceptionCallback),\n                     fullCommand = std::move(fullCommand)]() mutable {\n                        sendCommandInLoop(fullCommand,\n                                          std::move(callback),\n                                          std::move(exceptionCallback));\n                    });\n            }\n        }\n        catch (const RedisException &err)\n        {\n            exceptionCallback(err);\n        }\n    }\n\n    void sendSubscribe(const std::shared_ptr<SubscribeContext> &subCtx);\n    void sendUnsubscribe(const std::shared_ptr<SubscribeContext> &subCtx);\n\n    ~RedisConnection()\n    {\n        LOG_TRACE << (int)status_;\n        if (redisContext_ && status_ != ConnectStatus::kEnd)\n            redisAsyncDisconnect(redisContext_);\n    }\n\n    void disconnect();\n\n    void sendCommand(RedisResultCallback &&resultCallback,\n                     RedisExceptionCallback &&exceptionCallback,\n                     std::string_view command,\n                     ...)\n    {\n        va_list args;\n        va_start(args, command);\n        sendvCommand(command,\n                     std::move(resultCallback),\n                     std::move(exceptionCallback),\n                     args);\n        va_end(args);\n    }\n\n    trantor::EventLoop *getLoop() const\n    {\n        return loop_;\n    }\n\n  private:\n    redisAsyncContext *redisContext_{nullptr};\n    const trantor::InetAddress serverAddr_;\n    const std::string username_;\n    const std::string password_;\n    const unsigned int db_;\n    trantor::EventLoop *loop_{nullptr};\n    std::unique_ptr<trantor::Channel> channel_{nullptr};\n    std::function<void(std::shared_ptr<RedisConnection> &&)> connectCallback_;\n    std::function<void(std::shared_ptr<RedisConnection> &&)>\n        disconnectCallback_;\n    std::function<void(const std::shared_ptr<RedisConnection> &)> idleCallback_;\n    std::queue<RedisResultCallback> resultCallbacks_;\n    std::queue<RedisExceptionCallback> exceptionCallbacks_;\n    ConnectStatus status_{ConnectStatus::kNone};\n\n    // used to keep the lifetime of context object\n    std::unordered_map<unsigned long long, std::shared_ptr<SubscribeContext>>\n        subContexts_;\n\n    void startConnectionInLoop();\n    static void addWrite(void *userData);\n    static void delWrite(void *userData);\n    static void addRead(void *userData);\n    static void delRead(void *userData);\n    static void cleanup(void *userData);\n    void handleRedisRead();\n    void handleRedisWrite();\n    void handleResult(redisReply *result);\n    void sendCommandInLoop(const std::string &command,\n                           RedisResultCallback &&resultCallback,\n                           RedisExceptionCallback &&exceptionCallback);\n    void sendSubscribeInLoop(const std::shared_ptr<SubscribeContext> &subCtx);\n    void sendUnsubscribeInLoop(const std::shared_ptr<SubscribeContext> &subCtx);\n    void handleSubscribeResult(redisReply *result, SubscribeContext *subCtx);\n\n    void handleDisconnect();\n};\n\nusing RedisConnectionPtr = std::shared_ptr<RedisConnection>;\n}  // namespace nosql\n}  // namespace drogon\n"
  },
  {
    "path": "nosql_lib/redis/src/RedisResult.cc",
    "content": "/**\n *\n *  @file RedisResult.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/nosql/RedisResult.h>\n#include <drogon/nosql/RedisClient.h>\n#include <hiredis/hiredis.h>\n\nusing namespace drogon::nosql;\n\nstd::string RedisResult::getStringForDisplaying() const noexcept\n{\n    return getStringForDisplayingWithIndent(0);\n}\n\nstd::string RedisResult::getStringForDisplayingWithIndent(\n    size_t indent) const noexcept\n{\n    switch (result_->type)\n    {\n        case REDIS_REPLY_STRING:\n            return \"\\\"\" + std::string{result_->str, result_->len} + \"\\\"\";\n        case REDIS_REPLY_STATUS:\n            return std::string{result_->str, result_->len};\n        case REDIS_REPLY_ERROR:\n            return \"'ERROR:\" + std::string{result_->str, result_->len} + \"'\";\n        case REDIS_REPLY_NIL:\n            return \"(nil)\";\n        case REDIS_REPLY_INTEGER:\n            return std::to_string(result_->integer);\n        case REDIS_REPLY_ARRAY:\n        {\n            std::string ret;\n            for (size_t i = 0; i < result_->elements; ++i)\n            {\n                std::string lineNum = std::to_string(i + 1) + \") \";\n                if (i > 0)\n                {\n                    ret += std::string(indent, ' ');\n                }\n                ret += lineNum;\n                ret += RedisResult(result_->element[i])\n                           .getStringForDisplayingWithIndent(lineNum.length());\n                if (i != result_->elements - 1)\n                {\n                    ret += '\\n';\n                }\n            }\n            return ret;\n        }\n        default:\n            return \"*\";\n    }\n}\n\nstd::string RedisResult::asString() const noexcept(false)\n{\n    auto rtype = type();\n    if (rtype == RedisResultType::kString ||\n        rtype == RedisResultType::kStatus || rtype == RedisResultType::kError)\n    {\n        return std::string(result_->str, result_->len);\n    }\n    else if (rtype == RedisResultType::kInteger)\n    {\n        return std::to_string(result_->integer);\n    }\n    else\n    {\n        throw RedisException(RedisErrorCode::kBadType, \"bad type\");\n    }\n}\n\nRedisResultType RedisResult::type() const noexcept\n{\n    switch (result_->type)\n    {\n        case REDIS_REPLY_STRING:\n            return RedisResultType::kString;\n        case REDIS_REPLY_ARRAY:\n            return RedisResultType::kArray;\n        case REDIS_REPLY_INTEGER:\n            return RedisResultType::kInteger;\n        case REDIS_REPLY_NIL:\n            return RedisResultType::kNil;\n        case REDIS_REPLY_STATUS:\n            return RedisResultType::kStatus;\n        case REDIS_REPLY_ERROR:\n        default:\n            return RedisResultType::kError;\n    }\n}\n\nstd::vector<RedisResult> RedisResult::asArray() const noexcept(false)\n{\n    auto rtype = type();\n    if (rtype == RedisResultType::kArray)\n    {\n        std::vector<RedisResult> array;\n        for (size_t i = 0; i < result_->elements; ++i)\n        {\n            array.emplace_back(result_->element[i]);\n        }\n        return array;\n    }\n    throw RedisException(RedisErrorCode::kBadType, \"bad type\");\n}\n\nlong long RedisResult::asInteger() const noexcept(false)\n{\n    if (type() == RedisResultType::kInteger)\n        return result_->integer;\n    throw RedisException(RedisErrorCode::kBadType, \"bad type\");\n}\n\nbool RedisResult::isNil() const noexcept\n{\n    return type() == RedisResultType::kNil;\n}\n"
  },
  {
    "path": "nosql_lib/redis/src/RedisSubscriberImpl.cc",
    "content": "/**\n *\n *  @file RedisSubscriberImpl.cpp\n *  @author Nitromelon\n *\n *  Copyright 2022, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"RedisSubscriberImpl.h\"\n\nusing namespace drogon::nosql;\n\nRedisSubscriberImpl::~RedisSubscriberImpl()\n{\n    RedisConnectionPtr conn;\n    std::lock_guard<std::mutex> lock(mutex_);\n    if (conn_)\n    {\n        conn.swap(conn_);\n        conn->getLoop()->runInLoop([conn]() {\n            // Run in self loop to avoid blocking\n            conn->disconnect();\n        });\n    }\n}\n\nvoid RedisSubscriberImpl::subscribe(\n    const std::string &channel,\n    RedisMessageCallback &&messageCallback) noexcept\n{\n    LOG_TRACE << \"Subscribe \" << channel;\n\n    std::shared_ptr<SubscribeContext> subCtx;\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        if (subContexts_.find(channel) != subContexts_.end())\n        {\n            subCtx = subContexts_.at(channel);\n        }\n        else\n        {\n            subCtx = SubscribeContext::newContext(shared_from_this(), channel);\n            subContexts_.emplace(channel, subCtx);\n        }\n        subCtx->addMessageCallback(std::move(messageCallback));\n    }\n\n    RedisConnectionPtr connPtr;\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        connPtr = conn_;\n    }\n\n    if (connPtr)\n    {\n        connPtr->sendSubscribe(subCtx);\n    }\n    else\n    {\n        LOG_TRACE << \"no subscribe connection available, wait for connection\";\n        // Just wait for connection, all channels will be re-sub\n    }\n}\n\nvoid RedisSubscriberImpl::psubscribe(\n    const std::string &pattern,\n    RedisMessageCallback &&messageCallback) noexcept\n{\n    LOG_TRACE << \"Psubscribe \" << pattern;\n\n    std::shared_ptr<SubscribeContext> subCtx;\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        if (psubContexts_.find(pattern) != psubContexts_.end())\n        {\n            subCtx = psubContexts_.at(pattern);\n        }\n        else\n        {\n            subCtx =\n                SubscribeContext::newContext(shared_from_this(), pattern, true);\n            psubContexts_.emplace(pattern, subCtx);\n        }\n        subCtx->addMessageCallback(std::move(messageCallback));\n    }\n\n    RedisConnectionPtr connPtr;\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        connPtr = conn_;\n    }\n\n    if (connPtr)\n    {\n        connPtr->sendSubscribe(subCtx);\n    }\n    else\n    {\n        LOG_TRACE << \"no subscribe connection available, wait for connection\";\n        // Just wait for connection, all channels will be re-sub\n    }\n}\n\nvoid RedisSubscriberImpl::unsubscribe(const std::string &channel) noexcept\n{\n    LOG_TRACE << \"Unsubscribe \" << channel;\n\n    std::shared_ptr<SubscribeContext> subCtx;\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        auto iter = subContexts_.find(channel);\n        if (iter == subContexts_.end())\n        {\n            LOG_DEBUG << \"Attempt to unsubscribe from unknown channel \"\n                      << channel;\n            return;\n        }\n        subCtx = std::move(iter->second);\n        subContexts_.erase(iter);\n    }\n    subCtx->disable();\n\n    RedisConnectionPtr connPtr;\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        connPtr = conn_;\n    }\n    if (!connPtr)\n    {\n        LOG_TRACE << \"Connection unavailable, no need to send unsub command\";\n        return;\n    }\n\n    connPtr->sendUnsubscribe(subCtx);\n}\n\nvoid RedisSubscriberImpl::punsubscribe(const std::string &pattern) noexcept\n{\n    LOG_TRACE << \"Punsubscribe \" << pattern;\n\n    std::shared_ptr<SubscribeContext> subCtx;\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        auto iter = psubContexts_.find(pattern);\n        if (iter == psubContexts_.end())\n        {\n            LOG_DEBUG << \"Attempt to punsubscribe from unknown pattern \"\n                      << pattern;\n            return;\n        }\n        subCtx = std::move(iter->second);\n        psubContexts_.erase(iter);\n    }\n    subCtx->disable();\n\n    RedisConnectionPtr connPtr;\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        connPtr = conn_;\n    }\n    if (!connPtr)\n    {\n        LOG_TRACE << \"Connection unavailable, no need to send unsub command\";\n        return;\n    }\n\n    connPtr->sendUnsubscribe(subCtx);\n}\n\nvoid RedisSubscriberImpl::setConnection(const RedisConnectionPtr &conn)\n{\n    assert(conn);\n    std::lock_guard<std::mutex> lock(mutex_);\n    assert(!conn_);\n    conn_ = conn;\n}\n\nvoid RedisSubscriberImpl::clearConnection()\n{\n    std::lock_guard<std::mutex> lock(mutex_);\n    if (conn_)\n    {\n        conn_ = nullptr;\n        tasks_.clear();\n    }\n}\n\nvoid RedisSubscriberImpl::subscribeNext()\n{\n    RedisConnectionPtr connPtr;\n    std::shared_ptr<std::function<void(const RedisConnectionPtr &)>> taskPtr;\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        if (!conn_ || tasks_.empty())\n        {\n            return;\n        }\n        connPtr = conn_;\n        taskPtr = std::move(tasks_.front());\n        tasks_.pop_front();\n    }\n    (*taskPtr)(connPtr);\n}\n\nvoid RedisSubscriberImpl::subscribeAll()\n{\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        for (auto &item : subContexts_)\n        {\n            auto subCtx = item.second;\n            tasks_.emplace_back(\n                std::make_shared<\n                    std::function<void(const RedisConnectionPtr &)>>(\n                    [subCtx](const RedisConnectionPtr &connPtr) mutable {\n                        connPtr->sendSubscribe(subCtx);\n                    }));\n        }\n        for (auto &item : psubContexts_)\n        {\n            auto subCtx = item.second;\n            tasks_.emplace_back(\n                std::make_shared<\n                    std::function<void(const RedisConnectionPtr &)>>(\n                    [subCtx](const RedisConnectionPtr &connPtr) mutable {\n                        connPtr->sendSubscribe(subCtx);\n                    }));\n        }\n    }\n    subscribeNext();\n}\n"
  },
  {
    "path": "nosql_lib/redis/src/RedisSubscriberImpl.h",
    "content": "/**\n *\n *  @file RedisSubscriberImpl.h\n *  @author Nitromelon\n *\n *  Copyright 2022, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include <drogon/nosql/RedisSubscriber.h>\n#include \"RedisConnection.h\"\n#include \"SubscribeContext.h\"\n\n#include <mutex>\n#include <unordered_map>\n#include <memory>\n#include <list>\n\nnamespace drogon::nosql\n{\nclass RedisSubscriberImpl\n    : public RedisSubscriber,\n      public std::enable_shared_from_this<RedisSubscriberImpl>\n{\n  public:\n    ~RedisSubscriberImpl() override;\n\n    void subscribe(const std::string &channel,\n                   RedisMessageCallback &&messageCallback) noexcept override;\n    void psubscribe(const std::string &pattern,\n                    RedisMessageCallback &&messageCallback) noexcept override;\n\n    void unsubscribe(const std::string &channel) noexcept override;\n    void punsubscribe(const std::string &pattern) noexcept override;\n\n    // Set a connected connection to subscriber.\n    void setConnection(const RedisConnectionPtr &conn);\n    // Clear connection and task queue.\n    void clearConnection();\n    // Subscribe next channel in task queue.\n    void subscribeNext();\n    // Subscribe all channels.\n    void subscribeAll();\n\n  private:\n    RedisConnectionPtr conn_;\n    std::unordered_map<std::string, std::shared_ptr<SubscribeContext>>\n        subContexts_;\n    std::unordered_map<std::string, std::shared_ptr<SubscribeContext>>\n        psubContexts_;\n    std::list<std::shared_ptr<std::function<void(const RedisConnectionPtr &)>>>\n        tasks_;\n    std::mutex mutex_;\n};\n\n}  // namespace drogon::nosql\n"
  },
  {
    "path": "nosql_lib/redis/src/RedisTransactionImpl.cc",
    "content": "/**\n *\n *  @file RedisConnection.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"RedisTransactionImpl.h\"\n#include \"../../lib/src/TaskTimeoutFlag.h\"\nusing namespace drogon::nosql;\n\nRedisTransactionImpl::RedisTransactionImpl(RedisConnectionPtr connPtr) noexcept\n    : connPtr_(std::move(connPtr))\n{\n}\n\nvoid RedisTransactionImpl::execute(RedisResultCallback &&resultCallback,\n                                   RedisExceptionCallback &&exceptionCallback)\n{\n    execCommandAsync(\n        [thisPtr = shared_from_this(),\n         resultCallback =\n             std::move(resultCallback)](const RedisResult &result) {\n            thisPtr->isExecutedOrCancelled_ = true;\n            resultCallback(result);\n        },\n        std::move(exceptionCallback),\n        \"EXEC\");\n}\n\nvoid RedisTransactionImpl::execCommandAsync(\n    RedisResultCallback &&resultCallback,\n    RedisExceptionCallback &&exceptionCallback,\n    std::string_view command,\n    ...) noexcept\n{\n    if (isExecutedOrCancelled_)\n    {\n        exceptionCallback(RedisException(RedisErrorCode::kTransactionCancelled,\n                                         \"Transaction was cancelled\"));\n        return;\n    }\n    if (timeout_ <= 0.0)\n    {\n        va_list args;\n        va_start(args, command);\n        connPtr_->sendvCommand(\n            command,\n            std::move(resultCallback),\n            [thisPtr = shared_from_this(),\n             exceptionCallback =\n                 std::move(exceptionCallback)](const RedisException &err) {\n                LOG_ERROR << err.what();\n                thisPtr->isExecutedOrCancelled_ = true;\n                exceptionCallback(err);\n            },\n            args);\n        va_end(args);\n    }\n    else\n    {\n        auto expCbPtr = std::make_shared<RedisExceptionCallback>(\n            std::move(exceptionCallback));\n        auto timeoutFlagPtr = std::make_shared<TaskTimeoutFlag>(\n            connPtr_->getLoop(),\n            std::chrono::duration<double>(timeout_),\n            [expCbPtr]() {\n                if (*expCbPtr)\n                {\n                    (*expCbPtr)(RedisException(RedisErrorCode::kTimeout,\n                                               \"Command execution timeout\"));\n                }\n            });\n        va_list args;\n        va_start(args, command);\n        connPtr_->sendvCommand(\n            command,\n            [resultCallback = std::move(resultCallback),\n             timeoutFlagPtr](const RedisResult &result) {\n                if (timeoutFlagPtr->done())\n                {\n                    return;\n                }\n                resultCallback(result);\n            },\n            [thisPtr = shared_from_this(), expCbPtr, timeoutFlagPtr](\n                const RedisException &err) {\n                if (timeoutFlagPtr->done())\n                {\n                    return;\n                }\n                LOG_ERROR << err.what();\n                thisPtr->isExecutedOrCancelled_ = true;\n                if (*expCbPtr)\n                    (*expCbPtr)(err);\n            },\n            args);\n        va_end(args);\n        timeoutFlagPtr->runTimer();\n    }\n}\n\nvoid RedisTransactionImpl::doBegin()\n{\n    assert(!isExecutedOrCancelled_);\n    execCommandAsync([](const RedisResult & /*result*/) {},\n                     [](const RedisException & /*err*/) {},\n                     \"MULTI\");\n}\n\nRedisTransactionImpl::~RedisTransactionImpl()\n{\n    if (!isExecutedOrCancelled_)\n    {\n        LOG_WARN << \"The transaction is not executed before being destroyed\";\n        connPtr_->sendCommand([](const RedisResult & /*result*/) {},\n                              [](const RedisException & /*err*/) {},\n                              \"DISCARD\");\n    }\n    LOG_TRACE << \"transaction is destroyed\";\n}\n"
  },
  {
    "path": "nosql_lib/redis/src/RedisTransactionImpl.h",
    "content": "/**\n *\n *  @file RedisConnection.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include \"RedisConnection.h\"\n#include <drogon/nosql/RedisClient.h>\n#include <memory>\n\nnamespace drogon\n{\nnamespace nosql\n{\nclass RedisTransactionImpl final\n    : public RedisTransaction,\n      public std::enable_shared_from_this<RedisTransactionImpl>\n{\n  public:\n    explicit RedisTransactionImpl(RedisConnectionPtr connection) noexcept;\n    // virtual void cancel() override;\n    void execute(RedisResultCallback &&resultCallback,\n                 RedisExceptionCallback &&exceptionCallback) override;\n    void execCommandAsync(RedisResultCallback &&resultCallback,\n                          RedisExceptionCallback &&exceptionCallback,\n                          std::string_view command,\n                          ...) noexcept override;\n\n    std::shared_ptr<RedisSubscriber> newSubscriber() noexcept override\n    {\n        LOG_ERROR << \"You can't create subscriber from redis transaction\";\n        assert(0);\n        return nullptr;\n    }\n\n    std::shared_ptr<RedisTransaction> newTransaction() override\n    {\n        return shared_from_this();\n    }\n\n    void newTransactionAsync(\n        const std::function<void(const std::shared_ptr<RedisTransaction> &)>\n            &callback) override\n    {\n        callback(shared_from_this());\n    }\n\n    void setTimeout(double timeout) override\n    {\n        timeout_ = timeout;\n    }\n\n    void doBegin();\n    ~RedisTransactionImpl() override;\n\n  private:\n    bool isExecutedOrCancelled_{false};\n    RedisConnectionPtr connPtr_;\n    double timeout_{-1.0};\n};\n}  // namespace nosql\n}  // namespace drogon\n"
  },
  {
    "path": "nosql_lib/redis/src/SubscribeContext.cc",
    "content": "/**\n *\n *  @file SubscribeContext.cc\n *  @author Nitromelon\n *\n *  Copyright 2022, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#include \"SubscribeContext.h\"\n#include <stdio.h>\n#include <string>\n#include <utility>\n\nusing namespace drogon::nosql;\n\nstd::atomic<unsigned long long> SubscribeContext::maxContextId_{0};\n\nenum class SubCommandType\n{\n    Subscribe,\n    Unsubscribe,\n    Psubscribe,\n    Punsubscribe\n};\n\nstatic std::string formatSubscribeCommand(const std::string &channel,\n                                          SubCommandType type)\n{\n    // Avoid using redisvFormatCommand, we don't want to emit unknown error\n    static const char *redisSubFmt =\n        \"*2\\r\\n$9\\r\\nsubscribe\\r\\n$%zu\\r\\n%.*s\\r\\n\";\n    static const char *redisUnsubFmt =\n        \"*2\\r\\n$11\\r\\nunsubscribe\\r\\n$%zu\\r\\n%.*s\\r\\n\";\n    static const char *redisPsubFmt =\n        \"*2\\r\\n$10\\r\\npsubscribe\\r\\n$%zu\\r\\n%.*s\\r\\n\";\n    static const char *redisPunsubFmt =\n        \"*2\\r\\n$12\\r\\npunsubscribe\\r\\n$%zu\\r\\n%.*s\\r\\n\";\n\n    const char *fmt;\n    switch (type)\n    {\n        case SubCommandType::Subscribe:\n            fmt = redisSubFmt;\n            break;\n        case SubCommandType::Unsubscribe:\n            fmt = redisUnsubFmt;\n            break;\n        case SubCommandType::Psubscribe:\n            fmt = redisPsubFmt;\n            break;\n        case SubCommandType::Punsubscribe:\n            fmt = redisPunsubFmt;\n            break;\n    }\n\n    std::string command;\n    if (channel.size() < 32)\n    {\n        char buf[64];\n        size_t bufSize = sizeof(buf);\n        int len = snprintf(buf,\n                           bufSize,\n                           fmt,\n                           channel.size(),\n                           (int)channel.size(),\n                           channel.c_str());\n        command = std::string(buf, len);\n    }\n    else\n    {\n        size_t bufSize = channel.size() + 64;\n        char *buf = static_cast<char *>(malloc(bufSize));\n        int len = snprintf(buf,\n                           bufSize,\n                           fmt,\n                           channel.size(),\n                           (int)channel.size(),\n                           channel.c_str());\n        command = std::string(buf, len);\n        free(buf);\n    }\n    return command;\n}\n\nSubscribeContext::SubscribeContext(std::weak_ptr<RedisSubscriber> &&weakSub,\n                                   const std::string &channel,\n                                   bool isPattern)\n    : contextId_(++maxContextId_),\n      weakSub_(std::move(weakSub)),\n      channel_(channel),\n      isPattern_(isPattern)\n{\n    if (isPattern)\n    {\n        subscribeCommand_ =\n            formatSubscribeCommand(channel, SubCommandType::Psubscribe);\n        unsubscribeCommand_ =\n            formatSubscribeCommand(channel, SubCommandType::Punsubscribe);\n    }\n    else\n    {\n        subscribeCommand_ =\n            formatSubscribeCommand(channel, SubCommandType::Subscribe);\n        unsubscribeCommand_ =\n            formatSubscribeCommand(channel, SubCommandType::Unsubscribe);\n    }\n}\n\nvoid SubscribeContext::onMessage(const std::string &channel,\n                                 const std::string &message)\n{\n    std::lock_guard<std::mutex> lock(mutex_);\n    for (auto &callback : messageCallbacks_)\n    {\n        callback(channel, message);\n    }\n}\n\nvoid SubscribeContext::onSubscribe(const std::string &channel,\n                                   long long numChannels)\n{\n    LOG_DEBUG << \"Subscribe success to [\" << channel << \"], total \"\n              << numChannels;\n}\n\nvoid SubscribeContext::onUnsubscribe(const std::string &channel,\n                                     long long numChannels)\n{\n    LOG_DEBUG << \"Unsubscribe success from [\" << channel << \"], total \"\n              << numChannels;\n}\n"
  },
  {
    "path": "nosql_lib/redis/src/SubscribeContext.h",
    "content": "/**\n *\n *  @file SubscribeContext.h\n *  @author Nitromelon\n *\n *  Copyright 2022, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include <drogon/nosql/RedisClient.h>\n#include <list>\n#include <atomic>\n#include <mutex>\n#include <utility>\n\nnamespace drogon::nosql\n{\nclass SubscribeContext\n{\n  public:\n    static std::shared_ptr<SubscribeContext> newContext(\n        std::weak_ptr<RedisSubscriber> &&weakSub,\n        const std::string &channel,\n        bool isPattern = false)\n    {\n        return std::shared_ptr<SubscribeContext>(\n            new SubscribeContext(std::move(weakSub), channel, isPattern));\n    }\n\n    unsigned long long contextId() const\n    {\n        return contextId_;\n    }\n\n    const std::string &channel() const\n    {\n        return channel_;\n    }\n\n    const std::string &subscribeCommand() const\n    {\n        return subscribeCommand_;\n    }\n\n    const std::string &unsubscribeCommand() const\n    {\n        return unsubscribeCommand_;\n    }\n\n    void addMessageCallback(RedisMessageCallback &&messageCallback)\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        messageCallbacks_.emplace_back(std::move(messageCallback));\n    }\n\n    void disable()\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        disabled_ = true;\n        messageCallbacks_.clear();\n    }\n\n    void clear()\n    {\n        std::lock_guard<std::mutex> lock(mutex_);\n        messageCallbacks_.clear();\n    }\n\n    /**\n     * Message callback called by RedisConnection, whenever a message is\n     * published in target channel\n     * @param channel : target channel name\n     * @param message : message from channel\n     */\n    void onMessage(const std::string &channel, const std::string &message);\n\n    /**\n     * Callback called by RedisConnection, whenever a sub or re-sub is success\n     */\n    void onSubscribe(const std::string &channel, long long numChannels);\n\n    /**\n     * Callback called by RedisConnection, when unsubscription success.\n     */\n    void onUnsubscribe(const std::string &channel, long long numChannels);\n\n    bool alive() const\n    {\n        return !disabled_ && weakSub_.lock() != nullptr;\n    }\n\n  private:\n    SubscribeContext(std::weak_ptr<RedisSubscriber> &&weakSub,\n                     const std::string &channel,\n                     bool isPattern);\n    static std::atomic<unsigned long long> maxContextId_;\n\n    unsigned long long contextId_;\n    std::weak_ptr<RedisSubscriber> weakSub_;\n    std::string channel_;\n    bool isPattern_{false};\n    std::string subscribeCommand_;\n    std::string unsubscribeCommand_;\n    std::mutex mutex_;\n    std::list<RedisMessageCallback> messageCallbacks_;\n    bool disabled_{false};\n};\n\n}  // namespace drogon::nosql\n"
  },
  {
    "path": "nosql_lib/redis/tests/CMakeLists.txt",
    "content": "link_libraries(${PROJECT_NAME})\nif(WIN32)\n  link_libraries(iphlpapi)\nendif(WIN32)\n\nadd_executable(redis_test\n        redis_test.cc\n        )\n\nset_property(TARGET redis_test PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})\nset_property(TARGET redis_test PROPERTY CXX_STANDARD_REQUIRED ON)\nset_property(TARGET redis_test PROPERTY CXX_EXTENSIONS OFF)\n\nadd_executable(redis_subscriber_test\n        redis_subscriber_test.cc\n        )\n\nset_property(TARGET redis_subscriber_test PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})\nset_property(TARGET redis_subscriber_test PROPERTY CXX_STANDARD_REQUIRED ON)\nset_property(TARGET redis_subscriber_test PROPERTY CXX_EXTENSIONS OFF)\n"
  },
  {
    "path": "nosql_lib/redis/tests/redis_subscriber_test.cc",
    "content": "#define DROGON_TEST_MAIN\n#include <drogon/nosql/RedisClient.h>\n#include <drogon/drogon_test.h>\n#include <drogon/drogon.h>\n#include <iostream>\n#include <thread>\n\nusing namespace std::chrono_literals;\nusing namespace drogon::nosql;\nstatic std::atomic_int nMsgRecv{0};\nstatic std::atomic_int nPmsgRecv{0};\nstatic std::atomic_int nMsgSent{0};\n\nRedisClientPtr redisClient;\n\nDROGON_TEST(RedisSubscriberTest)\n{\n    redisClient = drogon::nosql::RedisClient::newRedisClient(\n        trantor::InetAddress(\"127.0.0.1\", 6379), 1);\n    REQUIRE(redisClient != nullptr);\n\n    auto subscriber = redisClient->newSubscriber();\n    subscriber->subscribe(\"test_sub\",\n                          [](const std::string &channel,\n                             const std::string &message) {\n                              ++nMsgRecv;\n                              LOG_INFO << \"Channel test_sub receive \"\n                                       << nMsgRecv << \" messages: \" << message;\n                          });\n    subscriber->psubscribe(\"test_*\",\n                           [](const std::string &channel,\n                              const std::string &message) {\n                               ++nPmsgRecv;\n                               LOG_INFO << \"Channel \" << channel << \" receive \"\n                                        << nPmsgRecv\n                                        << \" pmessages: \" << message;\n                           });\n    std::this_thread::sleep_for(1s);\n\n    auto fnPublish = [TEST_CTX](const char *channel, int i) {\n        redisClient->execCommandAsync(\n            [TEST_CTX](const drogon::nosql::RedisResult &r) {\n                SUCCESS();\n                ++nMsgSent;\n            },\n            [TEST_CTX](const std::exception &err) {\n                MANDATE(err.what());\n                LOG_ERROR << err.what();\n                ++nMsgSent;\n            },\n            \"publish %s %s%d\",\n            channel,\n            \"drogon\",\n            i);\n    };\n\n    for (int i = 0; i < 5; ++i)\n    {\n        fnPublish(\"test_sub\", i);\n    }\n    for (int i = 5; i < 10; ++i)\n    {\n        fnPublish(\"test_test\", i);\n    }\n\n    while (nMsgSent < 10)\n    {\n        std::this_thread::sleep_for(100ms);\n    }\n    std::this_thread::sleep_for(1s);\n\n    MANDATE(nMsgRecv == 5);\n    MANDATE(nPmsgRecv == 10);\n\n    // Unsub from channel\n    subscriber->unsubscribe(\"test_sub\");\n    fnPublish(\"test_sub\", 11);\n    while (nMsgSent < 11)\n    {\n        std::this_thread::sleep_for(100ms);\n    }\n    std::this_thread::sleep_for(1s);\n    MANDATE(nMsgRecv == 5);\n    MANDATE(nPmsgRecv == 11);\n\n    // Unsub from pattern\n    subscriber->punsubscribe(\"test_*\");\n    fnPublish(\"test_sub\", 12);\n    while (nMsgSent < 12)\n    {\n        std::this_thread::sleep_for(100ms);\n    }\n    std::this_thread::sleep_for(1s);\n    MANDATE(nMsgRecv == 5);\n    MANDATE(nPmsgRecv == 11);\n}\n\nint main(int argc, char **argv)\n{\n#ifndef USE_REDIS\n    LOG_DEBUG << \"Drogon is built without \"\n                 \"Redis. No tests executed.\";\n    return 0;\n#endif\n    std::promise<void> p1;\n    std::future<void> f1 = p1.get_future();\n\n    std::thread thr([&]() {\n        p1.set_value();\n        drogon::app().run();\n    });\n\n    f1.get();\n    int testStatus = drogon::test::run(argc, argv);\n    drogon::app().getLoop()->queueInLoop([]() { drogon::app().quit(); });\n    thr.join();\n    return testStatus;\n}\n"
  },
  {
    "path": "nosql_lib/redis/tests/redis_test.cc",
    "content": "#define DROGON_TEST_MAIN\n#include <drogon/nosql/RedisClient.h>\n#include <drogon/drogon_test.h>\n#include <drogon/drogon.h>\n#include <iostream>\n#include <thread>\n\nusing namespace std::chrono_literals;\nusing namespace drogon::nosql;\n\nRedisClientPtr redisClient;\n\nDROGON_TEST(RedisTest)\n{\n    redisClient = drogon::nosql::RedisClient::newRedisClient(\n        trantor::InetAddress(\"127.0.0.1\", 6379), 1);\n    REQUIRE(redisClient != nullptr);\n    // std::this_thread::sleep_for(1s);\n    redisClient->newTransactionAsync(\n        [TEST_CTX](const RedisTransactionPtr &transPtr) {\n            // 1\n            transPtr->execCommandAsync(\n                [TEST_CTX](const drogon::nosql::RedisResult &r) { SUCCESS(); },\n                [TEST_CTX](const std::exception &err) { MANDATE(err.what()); },\n                \"ping\");\n            // 2\n            transPtr->execute(\n                [TEST_CTX](const drogon::nosql::RedisResult &r) { SUCCESS(); },\n                [TEST_CTX](const std::exception &err) { MANDATE(err.what()); });\n        });\n    // 3\n    redisClient->execCommandAsync(\n        [TEST_CTX](const drogon::nosql::RedisResult &r) { SUCCESS(); },\n        [TEST_CTX](const std::exception &err) { MANDATE(err.what()); },\n        \"set %s %s\",\n        \"id_123\",\n        \"drogon\");\n    // 4\n    redisClient->execCommandAsync(\n        [TEST_CTX](const drogon::nosql::RedisResult &r) {\n            MANDATE(r.type() == RedisResultType::kArray);\n            MANDATE(r.asArray().size() == 1UL);\n        },\n        [TEST_CTX](const std::exception &err) { MANDATE(err.what()); },\n        \"keys id_*\");\n    // 5\n    redisClient->execCommandAsync(\n        [TEST_CTX](const drogon::nosql::RedisResult &r) {\n            MANDATE(r.asString() == \"hello\");\n        },\n        [TEST_CTX](const RedisException &err) { MANDATE(err.what()); },\n        \"echo %s\",\n        \"hello\");\n    // 6\n    redisClient->execCommandAsync(\n        [TEST_CTX](const drogon::nosql::RedisResult &r) { SUCCESS(); },\n        [TEST_CTX](const RedisException &err) { MANDATE(err.what()); },\n        \"flushall\");\n    // 7\n    redisClient->execCommandAsync(\n\n        [TEST_CTX](const drogon::nosql::RedisResult &r) {\n            MANDATE(r.type() == RedisResultType::kNil);\n        },\n        [TEST_CTX](const RedisException &err) { MANDATE(err.what()); },\n        \"get %s\",\n        \"xxxxx\");\n\n#ifdef __cpp_impl_coroutine\n    auto coro_test = [TEST_CTX]() -> drogon::Task<> {\n        // 8\n        try\n        {\n            auto r = co_await redisClient->execCommandCoro(\"get %s\", \"haha\");\n            MANDATE(r.type() == RedisResultType::kNil);\n        }\n        catch (const RedisException &err)\n        {\n            FAULT(err.what());\n        }\n    };\n    drogon::sync_wait(coro_test());\n#endif\n\n    // 9. Test sync\n    try\n    {\n        auto res = redisClient->execCommandSync<std::string>(\n            [](const RedisResult &result) { return result.asString(); },\n            \"set %s %s\",\n            \"sync_key\",\n            \"sync_value\");\n        MANDATE(res == \"OK\");\n    }\n    catch (const RedisException &err)\n    {\n        MANDATE(err.what());\n    }\n\n    try\n    {\n        auto [isNull, str] =\n            redisClient->execCommandSync<std::pair<bool, std::string>>(\n                [](const RedisResult &result) -> std::pair<bool, std::string> {\n                    if (result.isNil())\n                    {\n                        return {true, \"\"};\n                    }\n                    return {false, result.asString()};\n                },\n                \"get %s\",\n                \"sync_key\");\n        MANDATE(isNull == false);\n        MANDATE(str == \"sync_value\");\n    }\n    catch (const RedisException &err)\n    {\n        MANDATE(err.what());\n    }\n\n    // 10. Test sync redis exception\n    try\n    {\n        auto [isNull, str] =\n            redisClient->execCommandSync<std::pair<bool, std::string>>(\n                [](const RedisResult &result) -> std::pair<bool, std::string> {\n                    if (result.isNil())\n                    {\n                        return {true, \"\"};\n                    }\n                    return {false, result.asString()};\n                },\n                \"get %s %s\",\n                \"sync_key\",\n                \"sync_key\");\n        MANDATE(false);\n    }\n    catch (const RedisException &err)\n    {\n        LOG_INFO << \"Successfully catch sync error: \" << err.what();\n        MANDATE(err.code() == RedisErrorCode::kRedisError);\n        SUCCESS();\n    }\n\n    // 11. Test sync process function exception\n    try\n    {\n        auto value = redisClient->execCommandSync<std::string>(\n            [](const RedisResult &result) {\n                if (result.isNil())\n                {\n                    throw std::runtime_error(\"Key not exists\");\n                }\n                return result.asString();\n            },\n            \"get %s\",\n            \"not_exists\");\n        MANDATE(false);\n    }\n    catch (const RedisException &err)\n    {\n        (void)err;\n        MANDATE(false);\n    }\n    catch (const std::runtime_error &err)\n    {\n        MANDATE(std::string(\"Key not exists\") == err.what());\n        SUCCESS();\n    }\n\n    // 12. Test omit template parameter\n    try\n    {\n        auto i = redisClient->execCommandSync(\n            [](const RedisResult &r) { return r.asInteger(); },\n            \"del %s\",\n            \"sync_key\");\n        MANDATE(i == 1);\n    }\n    catch (const RedisException &err)\n    {\n        MANDATE(err.what());\n    }\n}\n\nint main(int argc, char **argv)\n{\n#ifndef USE_REDIS\n    LOG_DEBUG << \"Drogon is built without Redis. No tests executed.\";\n    return 0;\n#endif\n    std::promise<void> p1;\n    std::future<void> f1 = p1.get_future();\n\n    std::thread thr([&]() {\n        p1.set_value();\n        drogon::app().run();\n    });\n\n    f1.get();\n    int testStatus = drogon::test::run(argc, argv);\n    drogon::app().getLoop()->queueInLoop([]() { drogon::app().quit(); });\n    thr.join();\n    return testStatus;\n}\n"
  },
  {
    "path": "orm_lib/COPYING",
    "content": "MIT License\n\nCopyright (c) 2018 An Tao\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n\nThe license for libpqxx is as follows. Source code for several classes, including the\nResult class, the Row class, the Field class and their iterator classes are taken from \nlibpqxx and modified.\n\nCopyright (c) 2001-2017 Jeroen T. Vermeulen.\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n* Neither the name of the author, nor the names of other contributors may be\n  used to endorse or promote products derived from this software without\n  specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  },
  {
    "path": "orm_lib/inc/drogon/orm/ArrayParser.h",
    "content": "/** Handling of SQL arrays.\n *\n * DO NOT INCLUDE THIS FILE DIRECTLY; include drogon/orm/Field.h instead.\n *\n * Copyright (c) 2018, Jeroen T. Vermeulen.\n *\n * See COPYING for copyright license.  If you did not receive a file called\n * COPYING with this source code, please notify the distributor of this mistake,\n * or contact the author.\n */\n// Taken from libpqxx and modified\n\n/**\n *\n *  @file ArrayParser.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <stdexcept>\n#include <string>\n#include <utility>\n\nnamespace drogon\n{\nnamespace orm\n{\n/// Low-level array parser.\n/** Use this to read an array field retrieved from the database.\n *\n * Use this only if your client encoding is UTF-8, ASCII, or a single-byte\n * encoding which is a superset of ASCII.\n *\n * The input is a C-style string containing the textual representation of an\n * array, as returned by the database.  The parser reads this representation\n * on the fly.  The string must remain in memory until parsing is done.\n *\n * Parse the array by making calls to @c get_next until it returns a\n * @c juncture of \"done\".  The @c juncture tells you what the parser found in\n * that step: did the array \"nest\" to a deeper level, or \"un-nest\" back up?\n */\nclass DROGON_EXPORT ArrayParser\n{\n  public:\n    /// What's the latest thing found in the array?\n    enum juncture\n    {\n        /// Starting a new row.\n        row_start,\n        /// Ending the current row.\n        row_end,\n        /// Found a NULL value.\n        null_value,\n        /// Found a string value.\n        string_value,\n        /// Parsing has completed.\n        done,\n    };\n\n    /// Constructor.  You don't need this; use @c field::as_array instead.\n    explicit ArrayParser(const char input[]);\n\n    /// Parse the next step in the array.\n    /** Returns what it found.  If the juncture is @c string_value, the string\n     * will contain the value.  Otherwise, it will be empty.\n     *\n     * Call this until the @c juncture it returns is @c done.\n     */\n    std::pair<juncture, std::string> getNext();\n\n  private:\n    /// Current parsing position in the input.\n    const char *pos_;\n};\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/BaseBuilder.h",
    "content": "/**\n *\n *  @file BaseBuilder.h\n *  @author Ken Matsui\n *\n *  Copyright 2022, Ken Matsui.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/orm/Criteria.h>\n#include <drogon/orm/DbClient.h>\n#include <string_view>\n#include <future>\n#include <memory>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n#include <optional>\n\n#define unimplemented() assert(false && \"unimplemented\")\n\nnamespace drogon\n{\nnamespace orm\n{\ninline std::string to_string(CompareOperator op)\n{\n    switch (op)\n    {\n        case CompareOperator::EQ:\n            return \"=\";\n        case CompareOperator::NE:\n            return \"!=\";\n        case CompareOperator::GT:\n            return \">\";\n        case CompareOperator::GE:\n            return \">=\";\n        case CompareOperator::LT:\n            return \"<\";\n        case CompareOperator::LE:\n            return \"<=\";\n        case CompareOperator::Like:\n            return \"like\";\n        case CompareOperator::NotLike:\n        case CompareOperator::In:\n        case CompareOperator::NotIn:\n        case CompareOperator::IsNull:\n        case CompareOperator::IsNotNull:\n        default:\n            unimplemented();\n            return \"\";\n    }\n}\n\nstruct Filter\n{\n    std::string column;\n    CompareOperator op;\n    std::string value;\n};\n\n// Forward declaration to be a friend\ntemplate <typename T, bool SelectAll, bool Single = false>\nclass TransformBuilder;\n\ntemplate <typename T, bool SelectAll, bool Single = false>\nclass BaseBuilder\n{\n    using ResultType =\n        std::conditional_t<SelectAll,\n                           std::conditional_t<Single, T, std::vector<T>>,\n                           std::conditional_t<Single, Row, Result>>;\n\n    // Make the constructor of `TransformBuilder<T, SelectAll, true>` through\n    // `TransformBuilder::single()` be able to read these protected members.\n    friend class TransformBuilder<T, SelectAll, true>;\n\n  protected:\n    std::string from_;\n    std::string columns_;\n    std::vector<Filter> filters_;\n    std::optional<std::uint64_t> limit_;\n    std::optional<std::uint64_t> offset_;\n    // The order is important; use vector<pair> instead of unordered_map and\n    // map.\n    std::vector<std::pair<std::string, bool>> orders_;\n\n    inline void assert_column(const std::string &colName) const\n    {\n        for (const typename T::MetaData &m : T::metaData_)\n        {\n            if (m.colName_ == colName)\n            {\n                return;\n            }\n        }\n        throw UsageError(\"The column `\" + colName +\n                         \"` is not in the specified table.\");\n    }\n\n  private:\n    /**\n     * @brief Generate SQL query in string.\n     *\n     * @return std::string The string generated SQL query.\n     */\n    inline std::string gen_sql(ClientType type) const noexcept\n    {\n        int pCount = 0;\n        const auto placeholder = [type, &pCount]() {\n            ++pCount;\n            return type == ClientType::PostgreSQL ? \"$\" + std::to_string(pCount)\n                                                  : \"?\";\n        };\n\n        std::string sql = \"select \" + columns_ + \" from \" + from_;\n        if (!filters_.empty())\n        {\n            sql += \" where \" + filters_[0].column + \" \" +\n                   to_string(filters_[0].op) + \" \" + placeholder() + \"\";\n            for (int i = 1; i < filters_.size(); ++i)\n            {\n                sql += \" and \" + filters_[i].column + \" \" +\n                       to_string(filters_[i].op) + \" \" + placeholder() + \"\";\n            }\n        }\n        if (!orders_.empty())\n        {\n            sql += \" order by \" + orders_[0].first + \" \" +\n                   std::string(orders_[0].second ? \"asc\" : \"desc\");\n            for (int i = 1; i < orders_.size(); ++i)\n            {\n                sql += \", \" + orders_[i].first + \" \" +\n                       std::string(orders_[i].second ? \"asc\" : \"desc\");\n            }\n        }\n        if (limit_.has_value())\n        {\n            sql += \" limit \" + std::to_string(limit_.value());\n        }\n        if (offset_.has_value())\n        {\n            sql += \" offset \" + std::to_string(offset_.value());\n        }\n        return sql;\n    }\n\n    inline std::vector<std::string> gen_args() const noexcept\n    {\n        std::vector<std::string> args;\n        if (!filters_.empty())\n        {\n            for (const Filter &f : filters_)\n            {\n                args.emplace_back(f.value);\n            }\n        }\n        return args;\n    }\n\n  public:\n    static ResultType convert_result(const Result &r)\n    {\n        if constexpr (SelectAll)\n        {\n            if constexpr (Single)\n            {\n                return T(r[0]);\n            }\n            else\n            {\n                std::vector<T> ret;\n                for (const Row &row : r)\n                {\n                    ret.emplace_back(T(row));\n                }\n                return ret;\n            }\n        }\n        else\n        {\n            if constexpr (Single)\n            {\n                return r[0];\n            }\n            else\n            {\n                return r;\n            }\n        }\n    }\n\n    inline ResultType execSync(const DbClientPtr &client)\n    {\n        Result r(nullptr);\n        {\n            auto binder = *client << gen_sql(client->type());\n            for (const std::string &a : gen_args())\n            {\n                binder << a;\n            }\n            binder << Mode::Blocking;\n            binder >> [&r](const Result &result) { r = result; };\n            binder.exec();  // exec may throw exception\n        }\n        return convert_result(r);\n    }\n\n    template <typename TFn, typename EFn>\n    void execAsync(const DbClientPtr &client,\n                   TFn &&rCallback,\n                   EFn &&exceptCallback) noexcept\n    {\n        auto binder = *client << gen_sql(client->type());\n        for (const std::string &a : gen_args())\n        {\n            binder << a;\n        }\n        binder >> std::forward<TFn>(rCallback);\n        binder >> std::forward<EFn>(exceptCallback);\n    }\n\n    inline std::future<ResultType> execAsyncFuture(\n        const DbClientPtr &client) noexcept\n    {\n        auto binder = *client << gen_sql(client->type());\n        for (const std::string &a : gen_args())\n        {\n            binder << a;\n        }\n        std::shared_ptr<std::promise<ResultType>> prom =\n            std::make_shared<std::promise<ResultType>>();\n        binder >>\n            [prom](const Result &r) { prom->set_value(convert_result(r)); };\n        binder >>\n            [prom](const std::exception_ptr &e) { prom->set_exception(e); };\n        binder.exec();\n        return prom->get_future();\n    }\n};\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/CoroMapper.h",
    "content": "/**\n *\n *  @file CoroMapper.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include <functional>\n#include <tuple>\n\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/Mapper.h>\n#include <drogon/utils/coroutine.h>\n\nnamespace drogon\n{\nnamespace orm\n{\nnamespace internal\n{\ntemplate <typename ReturnType>\nstruct [[nodiscard]] MapperAwaiter : public CallbackAwaiter<ReturnType>\n{\n    using MapperFunction =\n        std::function<void(std::function<void(ReturnType result)> &&,\n                           std::function<void(const std::exception_ptr &)> &&)>;\n\n    explicit MapperAwaiter(MapperFunction &&function)\n        : function_(std::move(function))\n    {\n    }\n\n    void await_suspend(std::coroutine_handle<> handle)\n    {\n        function_(\n            [handle, this](ReturnType result) {\n                this->setValue(std::move(result));\n                handle.resume();\n            },\n            [handle, this](const std::exception_ptr &e) {\n                this->setException(e);\n                handle.resume();\n            });\n    }\n\n  private:\n    MapperFunction function_;\n};\n}  // namespace internal\n\n/**\n * @brief This template implements coroutine interfaces of ORM. All the methods\n * of this template are coroutine versions of the synchronous interfaces of the\n * orm::Mapper template.\n *\n * @tparam T The type of the model.\n */\ntemplate <typename T>\nclass CoroMapper : public Mapper<T>\n{\n  public:\n    using SingleRowCallback = typename Mapper<T>::SingleRowCallback;\n    using MultipleRowsCallback = typename Mapper<T>::MultipleRowsCallback;\n    using CountCallback = typename Mapper<T>::CountCallback;\n    using ExceptPtrCallback = std::function<void(const std::exception_ptr &)>;\n\n    explicit CoroMapper(DbClientPtr client) : Mapper<T>(std::move(client))\n    {\n    }\n\n    using TraitsPKType = typename Mapper<T>::TraitsPKType;\n\n    inline internal::MapperAwaiter<T> findByPrimaryKey(const TraitsPKType &key)\n    {\n        if constexpr (!std::is_same_v<typename T::PrimaryKeyType, void>)\n        {\n            auto lb = [this, key](SingleRowCallback &&callback,\n                                  ExceptPtrCallback &&errCallback) mutable {\n                static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                              \"No primary key in the table!\");\n                static_assert(\n                    internal::has_sqlForFindingByPrimaryKey<T>::value,\n                    \"No function member named sqlForFindingByPrimaryKey, \"\n                    \"please \"\n                    \"make sure that the model class is generated by the latest \"\n                    \"version of drogon_ctl\");\n                // return findFutureOne(Criteria(T::primaryKeyName, key));\n                std::string sql = T::sqlForFindingByPrimaryKey();\n                if (this->forUpdate_)\n                {\n                    sql += \" for update\";\n                }\n                this->clear();\n                auto binder = *(this->client_) << std::move(sql);\n                this->outputPrimaryKeyToBinder(key, binder);\n\n                binder >> [callback = std::move(callback),\n                           errCallback](const Result &r) {\n                    if (r.size() == 0)\n                    {\n                        errCallback(std::make_exception_ptr(\n                            UnexpectedRows(\"0 rows found\")));\n                    }\n                    else if (r.size() > 1)\n                    {\n                        errCallback(std::make_exception_ptr(\n                            UnexpectedRows(\"Found more than one row\")));\n                    }\n                    else\n                    {\n                        callback(T(r[0]));\n                    }\n                };\n                binder >> std::move(errCallback);\n                binder.exec();\n            };\n            return internal::MapperAwaiter<T>(std::move(lb));\n        }\n        else\n        {\n            LOG_FATAL << \"The table must have a primary key\";\n            abort();\n        }\n    }\n\n    // Query condition overrides\n\n    /**\n     * @brief Add a limit to the query.\n     *\n     * @param limit The limit\n     * @return CoroMapper<T>& The CoroMapper itself.\n     */\n    CoroMapper<T> &limit(size_t limit)\n    {\n        Mapper<T>::limit(limit);\n        return *this;\n    }\n\n    /**\n     * @brief Add a offset to the query.\n     *\n     * @param offset The offset.\n     * @return CoroMapper<T>& The CoroMapper itself.\n     */\n    CoroMapper<T> &offset(size_t offset)\n    {\n        Mapper<T>::offset(offset);\n        return *this;\n    }\n\n    /**\n     * @brief Set the order of the results.\n     *\n     * @param colName the column name, the results are sorted by that column\n     * @param order Ascending or descending order\n     * @return CoroMapper<T>& The CoroMapper itself.\n     */\n    CoroMapper<T> &orderBy(const std::string &colName,\n                           const SortOrder &order = SortOrder::ASC)\n    {\n        Mapper<T>::orderBy(colName, order);\n        return *this;\n    }\n\n    /**\n     * @brief Set the order of the results.\n     *\n     * @param colIndex the column index, the results are sorted by that column\n     * @param order Ascending or descending order\n     * @return CoroMapper<T>& The CoroMapper itself.\n     */\n    CoroMapper<T> &orderBy(size_t colIndex,\n                           const SortOrder &order = SortOrder::ASC)\n    {\n        Mapper<T>::orderBy(colIndex, order);\n        return *this;\n    }\n\n    /**\n     * @brief Set limit and offset to achieve pagination.\n     * This method will override limit() and offset(), and will be override by\n     * them.\n     *\n     * @param page The page number\n     * @param perPage The number of columns per page\n     * @return CoroMapper<T>& The CoroMapper itself.\n     */\n    CoroMapper<T> &paginate(size_t page, size_t perPage)\n    {\n        Mapper<T>::paginate(page, perPage);\n        return *this;\n    }\n\n    /**\n     * @brief Lock the result for updating.\n     *\n     * @return CoroMapper<T>& The CoroMapper itself.\n     */\n    CoroMapper<T> &forUpdate()\n    {\n        Mapper<T>::forUpdate();\n        return *this;\n    }\n\n    // Read api for coroutines\n\n    inline internal::MapperAwaiter<std::vector<T>> findAll()\n    {\n        return findBy(Criteria());\n    }\n\n    inline internal::MapperAwaiter<size_t> count(\n        const Criteria &criteria = Criteria())\n    {\n        auto lb = [this, criteria](CountCallback &&callback,\n                                   ExceptPtrCallback &&errCallback) {\n            std::string sql = \"select count(*) from \";\n            sql += T::tableName;\n            if (criteria)\n            {\n                sql += \" where \";\n                sql += criteria.criteriaString();\n                sql = this->replaceSqlPlaceHolder(sql, \"$?\");\n            }\n            this->clear();\n            auto binder = *(this->client_) << std::move(sql);\n            if (criteria)\n                criteria.outputArgs(binder);\n            binder >> [callback = std::move(callback)](const Result &r) {\n                assert(r.size() == 1);\n                callback(r[0][(Row::SizeType)0].as<size_t>());\n            };\n            binder >> std::move(errCallback);\n        };\n        return internal::MapperAwaiter<size_t>(std::move(lb));\n    }\n\n    inline internal::MapperAwaiter<T> findOne(const Criteria &criteria)\n    {\n        auto lb = [this, criteria](SingleRowCallback &&callback,\n                                   ExceptPtrCallback &&errCallback) {\n            std::string sql = \"select * from \";\n            sql += T::tableName;\n            bool hasParameters = false;\n            if (criteria)\n            {\n                sql += \" where \";\n                sql += criteria.criteriaString();\n                hasParameters = true;\n            }\n            sql.append(this->orderByString_);\n            if (this->limit_ > 0)\n            {\n                hasParameters = true;\n                sql.append(\" limit $?\");\n            }\n            if (this->offset_ > 0)\n            {\n                hasParameters = true;\n                sql.append(\" offset $?\");\n            }\n            if (hasParameters)\n                sql = this->replaceSqlPlaceHolder(sql, \"$?\");\n            if (this->forUpdate_)\n            {\n                sql += \" for update\";\n            }\n            auto binder = *(this->client_) << std::move(sql);\n            if (criteria)\n                criteria.outputArgs(binder);\n            if (this->limit_ > 0)\n                binder << this->limit_;\n            if (this->offset_)\n                binder << this->offset_;\n            this->clear();\n            binder >>\n                [errCallback, callback = std::move(callback)](const Result &r) {\n                    if (r.size() == 0)\n                    {\n                        errCallback(std::make_exception_ptr(\n                            UnexpectedRows(\"0 rows found\")));\n                    }\n                    else if (r.size() > 1)\n                    {\n                        errCallback(std::make_exception_ptr(\n                            UnexpectedRows(\"Found more than one row\")));\n                    }\n                    else\n                    {\n                        callback(T(r[0]));\n                    }\n                };\n            binder >> std::move(errCallback);\n        };\n        return internal::MapperAwaiter<T>(std::move(lb));\n    }\n\n    inline internal::MapperAwaiter<std::vector<T>> findBy(\n        const Criteria &criteria)\n    {\n        auto lb = [this, criteria](MultipleRowsCallback &&callback,\n                                   ExceptPtrCallback &&errCallback) {\n            std::string sql = \"select * from \";\n            sql += T::tableName;\n            bool hasParameters = false;\n            if (criteria)\n            {\n                hasParameters = true;\n                sql += \" where \";\n                sql += criteria.criteriaString();\n            }\n            sql.append(this->orderByString_);\n            if (this->limit_ > 0)\n            {\n                hasParameters = true;\n                sql.append(\" limit $?\");\n            }\n            if (this->offset_ > 0)\n            {\n                hasParameters = true;\n                sql.append(\" offset $?\");\n            }\n            if (hasParameters)\n                sql = this->replaceSqlPlaceHolder(sql, \"$?\");\n            if (this->forUpdate_)\n            {\n                sql += \" for update\";\n            }\n            auto binder = *(this->client_) << std::move(sql);\n            if (criteria)\n                criteria.outputArgs(binder);\n            if (this->limit_ > 0)\n                binder << this->limit_;\n            if (this->offset_)\n                binder << this->offset_;\n            this->clear();\n            binder >> [callback = std::move(callback)](const Result &r) {\n                std::vector<T> ret;\n                for (auto const &row : r)\n                {\n                    ret.push_back(T(row));\n                }\n                callback(ret);\n            };\n            binder >> std::move(errCallback);\n        };\n        return internal::MapperAwaiter<std::vector<T>>(std::move(lb));\n    }\n\n    inline internal::MapperAwaiter<T> insert(const T &obj)\n    {\n        auto lb = [this, obj](SingleRowCallback &&callback,\n                              ExceptPtrCallback &&errCallback) {\n            this->clear();\n            bool needSelection = false;\n            auto binder = *(this->client_)\n                          << obj.sqlForInserting(needSelection);\n            obj.outputArgs(binder);\n            auto client = this->client_;\n            binder >> [client,\n                       callback = std::move(callback),\n                       obj,\n                       needSelection,\n                       errCallback](const Result &r) {\n                assert(r.affectedRows() == 1);\n                if (client->type() == ClientType::PostgreSQL)\n                {\n                    if (needSelection)\n                    {\n                        assert(r.size() == 1);\n                        callback(T(r[0]));\n                    }\n                    else\n                    {\n                        callback(obj);\n                    }\n                }\n                else  // Mysql or Sqlite3\n                {\n                    auto id = r.insertId();\n                    auto newObj = obj;\n                    newObj.updateId(id);\n                    if (needSelection)\n                    {\n                        auto tmp = Mapper<T>(client);\n                        tmp.findByPrimaryKey(\n                            newObj.getPrimaryKey(),\n                            callback,\n                            [errCallback](const DrogonDbException &err) {\n                                errCallback(std::make_exception_ptr(\n                                    Failure(err.base().what())));\n                            });\n                    }\n                    else\n                    {\n                        callback(newObj);\n                    }\n                }\n            };\n            binder >> std::move(errCallback);\n        };\n        return internal::MapperAwaiter<T>(std::move(lb));\n    }\n\n    inline internal::MapperAwaiter<size_t> update(const T &obj)\n    {\n        auto lb = [this, obj](CountCallback &&callback,\n                              ExceptPtrCallback &&errCallback) {\n            this->clear();\n            static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                          \"No primary key in the table!\");\n            std::vector<std::string> colNames = obj.updateColumns();\n            if (colNames.empty())\n            {\n                callback(0);\n                return;\n            }\n            std::string sql = \"update \";\n            sql += T::tableName;\n            sql += \" set \";\n            for (auto const &colName : colNames)\n            {\n                sql += colName;\n                sql += \" = $?,\";\n            }\n            sql[sql.length() - 1] = ' ';  // Replace the last ','\n\n            this->makePrimaryKeyCriteria(sql);\n\n            sql = this->replaceSqlPlaceHolder(sql, \"$?\");\n            auto binder = *(this->client_) << std::move(sql);\n            obj.updateArgs(binder);\n            this->outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);\n            binder >> [callback = std::move(callback)](const Result &r) {\n                callback(r.affectedRows());\n            };\n            binder >> std::move(errCallback);\n        };\n        return internal::MapperAwaiter<size_t>(std::move(lb));\n    }\n\n    template <typename... TupleArgs, typename... Arguments>\n    inline internal::MapperAwaiter<size_t> updateBy(\n        const std::tuple<TupleArgs...> &colNames,\n        const Criteria &criteria,\n        Arguments &&...args)\n    {\n        static_assert(sizeof...(args) > 0);\n        static_assert(sizeof...(args) ==\n                      std::tuple_size_v<std::tuple<TupleArgs...>>);\n        std::string sql = \"update \";\n        sql += T::tableName;\n        sql += \" set \";\n        std::apply(\n            [&sql](auto &&...name) {\n                ((sql += std::string(name) + \" = $?,\"), ...);\n            },\n            colNames);\n        sql[sql.length() - 1] = ' ';  // Replace the last ','\n\n        return updateByHelper(std::move(sql),\n                              criteria,\n                              std::forward<Arguments>(args)...);\n    }\n\n    template <typename... Arguments>\n    internal::MapperAwaiter<size_t> updateBy(\n        const std::vector<std::string> &colNames,\n        const Criteria &criteria,\n        Arguments &&...args)\n    {\n        static_assert(sizeof...(args) > 0);\n        assert(colNames.size() == sizeof...(args));\n        std::string sql = \"update \";\n        sql += T::tableName;\n        sql += \" set \";\n        for (auto const &colName : colNames)\n        {\n            sql += colName;\n            sql += \" = $?,\";\n        }\n        sql[sql.length() - 1] = ' ';  // Replace the last ','\n\n        return updateByHelper(std::move(sql),\n                              criteria,\n                              std::forward<Arguments>(args)...);\n    }\n\n    template <typename... Arguments>\n    inline internal::MapperAwaiter<size_t> increment(\n        const std::vector<std::string> &colNames,\n        const Criteria &criteria,\n        Arguments... args)\n    {\n        static_assert(sizeof...(args) > 0);\n        assert(colNames.size() == sizeof...(args));\n        std::string sql = \"update \";\n        sql += T::tableName;\n        sql += \" set \";\n\n        std::vector<const char *> temps;\n        (void)std::initializer_list<int>{(\n            [&args, &temps] {\n                args = (args < 0) ? (temps.push_back(\" - $?,\"), -args)\n                                  : (temps.push_back(\" + $?,\"), args);\n            }(),\n            0)...};\n\n        for (int i = 0; i < sizeof...(args); ++i)\n        {\n            const auto &colName = colNames[i];\n            sql += colName;\n            sql += \" = \";\n            sql += colName;\n            sql += temps[i];\n        }\n        sql[sql.length() - 1] = ' ';  // Replace the last ','\n\n        return updateByHelper(std::move(sql),\n                              criteria,\n                              std::forward<Arguments>(args)...);\n    }\n\n  private:\n    template <typename... Arguments>\n    internal::MapperAwaiter<size_t> updateByHelper(std::string &&sql,\n                                                   const Criteria &criteria,\n                                                   Arguments &&...args)\n    {\n        auto lb = [this,\n                   sql = std::move(sql),\n                   criteria,\n                   ... args = std::forward<Arguments>(\n                       args)](CountCallback &&callback,\n                              ExceptPtrCallback &&errCallback) mutable {\n            this->clear();\n\n            if (criteria)\n            {\n                sql += \" where \";\n                sql += criteria.criteriaString();\n            }\n\n            sql = this->replaceSqlPlaceHolder(sql, \"$?\");\n            auto binder = *(this->client_) << std::move(sql);\n            (void)std::initializer_list<int>{(binder << args, 0)...};\n            if (criteria)\n                criteria.outputArgs(binder);\n            binder >> [callback = std::move(callback)](const Result &r) {\n                callback(r.affectedRows());\n            };\n            binder >> std::move(errCallback);\n        };\n        return internal::MapperAwaiter<size_t>(std::move(lb));\n    }\n\n  public:\n    inline internal::MapperAwaiter<size_t> deleteOne(const T &obj)\n    {\n        auto lb = [this, obj](CountCallback &&callback,\n                              ExceptPtrCallback &&errCallback) {\n            this->clear();\n            static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                          \"No primary key in the table!\");\n            std::string sql = \"delete from \";\n            sql += T::tableName;\n            sql += \" \";\n\n            this->makePrimaryKeyCriteria(sql);\n\n            sql = this->replaceSqlPlaceHolder(sql, \"$?\");\n            auto binder = *(this->client_) << std::move(sql);\n            this->outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);\n            binder >> [callback = std::move(callback)](const Result &r) {\n                callback(r.affectedRows());\n            };\n            binder >> std::move(errCallback);\n        };\n        return internal::MapperAwaiter<size_t>(std::move(lb));\n    }\n\n    inline internal::MapperAwaiter<size_t> deleteBy(const Criteria &criteria)\n    {\n        auto lb = [this, criteria](CountCallback &&callback,\n                                   ExceptPtrCallback &&errCallback) {\n            this->clear();\n            static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                          \"No primary key in the table!\");\n            std::string sql = \"delete from \";\n            sql += T::tableName;\n\n            if (criteria)\n            {\n                sql += \" where \";\n                sql += criteria.criteriaString();\n                sql = this->replaceSqlPlaceHolder(sql, \"$?\");\n            }\n\n            auto binder = *(this->client_) << std::move(sql);\n            if (criteria)\n            {\n                criteria.outputArgs(binder);\n            }\n            binder >> [callback = std::move(callback)](const Result &r) {\n                callback(r.affectedRows());\n            };\n            binder >> std::move(errCallback);\n        };\n        return internal::MapperAwaiter<size_t>(std::move(lb));\n    }\n\n    inline internal::MapperAwaiter<size_t> deleteByPrimaryKey(\n        const TraitsPKType &key)\n    {\n        static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                      \"No primary key in the table!\");\n        static_assert(\n            internal::has_sqlForDeletingByPrimaryKey<T>::value,\n            \"No function member named sqlForDeletingByPrimaryKey, please \"\n            \"make sure that the model class is generated by the latest \"\n            \"version of drogon_ctl\");\n        auto lb = [this, key](CountCallback &&callback,\n                              ExceptPtrCallback &&errCallback) {\n            this->clear();\n            auto binder = *(this->client_) << T::sqlForDeletingByPrimaryKey();\n            this->outputPrimaryKeyToBinder(key, binder);\n            binder >> [callback = std::move(callback)](const Result &r) {\n                callback(r.affectedRows());\n            };\n            binder >> std::move(errCallback);\n        };\n        return internal::MapperAwaiter<size_t>(std::move(lb));\n    }\n};\n}  // namespace orm\n}  // namespace drogon\n#endif\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/Criteria.h",
    "content": "/**\n *\n *  @file Criteria.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <drogon/orm/SqlBinder.h>\n#include <assert.h>\n#include <memory>\n#include <string>\n#include <tuple>\n#include <type_traits>\n\nnamespace Json\n{\nclass Value;\n}\n\nnamespace drogon\n{\nnamespace orm\n{\nenum class CompareOperator\n{\n    EQ,\n    NE,\n    GT,\n    GE,\n    LT,\n    LE,\n    Like,\n    NotLike,\n    In,\n    NotIn,\n    IsNull,\n    IsNotNull\n};\n\n/**\n * @brief Wrapper for custom SQL string\n */\nstruct CustomSql\n{\n    explicit CustomSql(std::string content) : content_(std::move(content))\n    {\n    }\n\n    std::string content_;\n};\n\n/**\n * @brief User-defined literal to convert a string to CustomSql\n */\ninline CustomSql operator\"\"_sql(const char *str, size_t)\n{\n    return CustomSql(str);\n}\n\n/**\n * @brief this class represents a comparison condition.\n */\nclass DROGON_EXPORT Criteria\n{\n  public:\n    /**\n     * @brief Check if the condition is empty.\n     *\n     * @return true means the condition is not empty.\n     * @return false means the condition is empty.\n     */\n    explicit operator bool() const\n    {\n        return !conditionString_.empty();\n    }\n\n    /**\n     * @brief return the condition string in SQL.\n     *\n     * @return std::string\n     */\n    std::string criteriaString() const\n    {\n        return conditionString_;\n    }\n\n    /**\n     * @brief Construct a new custom Criteria object\n     *\n     * @param sql The SQL statement to be executed.\n     * @param args The parameters to be passed to the placeholders.\n     *\n     * @note\n     *\n     * Use $? as placeholders in the SQL statement.\n     *\n     * Be careful that the placeholders are not the same as the ones in\n     * functions such as drogon::orm::DbClient::execSqlAsync. Which means you\n     * couldn't use numeric placeholders such as $1, $2 to represent the order\n     * of the arguments.\n     *\n     * The arguments should be in the same order as the placeholders.\n     *\n     */\n    template <typename... Arguments>\n    explicit Criteria(const CustomSql &sql, Arguments &&...args)\n    {\n        conditionString_ = sql.content_;\n        outputArgumentsFunc_ =\n            [args = std::make_tuple(std::forward<Arguments>(args)...)](\n                internal::SqlBinder &binder) mutable {\n                return std::apply(\n                    [&binder](auto &&...args) {\n                        (void)std::initializer_list<int>{\n                            (binder << std::forward<Arguments>(args), 0)...};\n                    },\n                    std::move(args));\n            };\n    }\n\n    template <typename... Arguments>\n    explicit Criteria(CustomSql &&sql, Arguments &&...args)\n    {\n        conditionString_ = std::move(sql.content_);\n        outputArgumentsFunc_ =\n            [args = std::make_tuple(std::forward<Arguments>(args)...)](\n                internal::SqlBinder &binder) mutable {\n                return std::apply(\n                    [&binder](auto &&...args) {\n                        (void)std::initializer_list<int>{\n                            (binder << std::forward<Arguments>(args), 0)...};\n                    },\n                    std::move(args));\n            };\n    }\n\n    /**\n     * @brief Construct a new Criteria object\n     *\n     * @tparam T the type of the object to be compared with.\n     * @param colName The name of the column.\n     * @param opera The type of the comparison.\n     * @param arg The object to be compared with.\n     */\n    template <typename T>\n    Criteria(const std::string &colName, const CompareOperator &opera, T &&arg)\n    {\n        assert(opera != CompareOperator::IsNotNull &&\n               opera != CompareOperator::IsNull);\n        conditionString_ = colName;\n        switch (opera)\n        {\n            case CompareOperator::EQ:\n                conditionString_ += \" = $?\";\n                break;\n            case CompareOperator::NE:\n                conditionString_ += \" != $?\";\n                break;\n            case CompareOperator::GT:\n                conditionString_ += \" > $?\";\n                break;\n            case CompareOperator::GE:\n                conditionString_ += \" >= $?\";\n                break;\n            case CompareOperator::LT:\n                conditionString_ += \" < $?\";\n                break;\n            case CompareOperator::LE:\n                conditionString_ += \" <= $?\";\n                break;\n            case CompareOperator::Like:\n                conditionString_ += \" like $?\";\n                break;\n            case CompareOperator::NotLike:\n                conditionString_ += \" not like $?\";\n                break;\n            default:\n                break;\n        }\n        outputArgumentsFunc_ =\n            [arg = std::forward<T>(arg)](internal::SqlBinder &binder) {\n                binder << arg;\n            };\n    }\n\n    template <typename T>\n    Criteria(const std::string &colName,\n             const CompareOperator &opera,\n             const std::vector<T> &args)\n    {\n        const auto argsSize = args.size();\n        assert(((opera == CompareOperator::In) ||\n                (opera == CompareOperator::NotIn)) &&\n               (argsSize > 0));\n        if (opera == CompareOperator::In)\n        {\n            conditionString_ = colName + \" in (\";\n        }\n        else if (opera == CompareOperator::NotIn)\n        {\n            conditionString_ = colName + \" not in (\";\n        }\n        for (size_t i = 0; i < argsSize; ++i)\n        {\n            if (i < (argsSize - 1))\n                conditionString_.append(\"$?,\");\n            else\n                conditionString_.append(\"$?\");\n        }\n        conditionString_.append(\")\");\n        outputArgumentsFunc_ = [args](internal::SqlBinder &binder) {\n            for (auto &arg : args)\n            {\n                binder << arg;\n            }\n        };\n    }\n\n    template <typename T>\n    Criteria(const std::string &colName,\n             const CompareOperator &opera,\n             std::vector<T> &&args)\n    {\n        const auto argsSize = args.size();\n        assert(((opera == CompareOperator::In) ||\n                (opera == CompareOperator::NotIn)) &&\n               (argsSize > 0));\n        if (opera == CompareOperator::In)\n        {\n            conditionString_ = colName + \" in (\";\n        }\n        else if (opera == CompareOperator::NotIn)\n        {\n            conditionString_ = colName + \" not in (\";\n        }\n        for (size_t i = 0; i < argsSize; ++i)\n        {\n            if (i < (argsSize - 1))\n                conditionString_.append(\"$?,\");\n            else\n                conditionString_.append(\"$?\");\n        }\n        conditionString_.append(\")\");\n        outputArgumentsFunc_ =\n            [args = std::move(args)](internal::SqlBinder &binder) {\n                for (auto &arg : args)\n                {\n                    binder << arg;\n                }\n            };\n    }\n\n    template <typename T>\n    Criteria(const std::string &colName,\n             const CompareOperator &opera,\n             std::vector<T> &args)\n        : Criteria(colName, opera, (const std::vector<T> &)args)\n    {\n    }\n\n    /**\n     * @brief Construct a new Criteria object presenting a equal expression\n     *\n     * @tparam T The type of the object to be equal to.\n     * @param colName The name of the column.\n     * @param arg The object to be equal to.\n     */\n    template <typename T>\n    Criteria(const std::string &colName, T &&arg)\n        : Criteria(colName, CompareOperator::EQ, std::forward<T>(arg))\n    {\n    }\n\n    /**\n     * @brief Construct a new Criteria object presenting a expression without\n     * parameters.\n     *\n     * @param colName The name of the column.\n     * @param opera The type of the comparison. it must be one of the following:\n     * @code\n       CompareOperator::IsNotNull\n       CompareOperator::IsNull\n       @endcode\n     */\n    Criteria(const std::string &colName, const CompareOperator &opera)\n    {\n        assert(opera == CompareOperator::IsNotNull ||\n               opera == CompareOperator::IsNull);\n        conditionString_ = colName;\n        switch (opera)\n        {\n            case CompareOperator::IsNull:\n                conditionString_ += \" is null\";\n                break;\n            case CompareOperator::IsNotNull:\n                conditionString_ += \" is not null\";\n                break;\n            default:\n                break;\n        }\n    }\n\n    Criteria(const std::string &colName, CompareOperator &opera)\n        : Criteria(colName, (const CompareOperator &)opera)\n    {\n    }\n\n    Criteria(const std::string &colName, CompareOperator &&opera)\n        : Criteria(colName, (const CompareOperator &)opera)\n    {\n    }\n\n    /**\n     * @brief Construct a new Criteria object\n     *\n     * @param json A json object representing the criteria\n     * @note the json object must be a array of three items, the first is the\n     * name of the field, the second is the comparison operator and the third is\n     * the value to be compared.\n     * The valid operators are \"=\",\"<\",\">\",\"<=\",\">=\",\"!=\",\"in\"\n     * for example:\n     * [\"id\",\"=\",1] means 'id = 1'\n     * [\"id\",\"!=\",null] means 'id is not null'\n     * [\"user_name\",\"in\",[\"Tom\",\"Bob\"]] means 'user_name in ('Tom', 'Bob')'\n     * [\"price\",\"<\",1000] means 'price < 1000'\n     */\n    explicit Criteria(const Json::Value &json) noexcept(false);\n    Criteria() = default;\n\n    /**\n     * @brief Output arguments to the SQL binder object.\n     *\n     * @param binder The SQL binder object.\n     * @note This method must be called by drogon.\n     */\n    void outputArgs(internal::SqlBinder &binder) const\n    {\n        if (outputArgumentsFunc_)\n            outputArgumentsFunc_(binder);\n    }\n\n  private:\n    friend DROGON_EXPORT const Criteria operator&&(Criteria cond1,\n                                                   Criteria cond2);\n\n    friend DROGON_EXPORT const Criteria operator||(Criteria cond1,\n                                                   Criteria cond2);\n    std::string conditionString_;\n    std::function<void(internal::SqlBinder &)> outputArgumentsFunc_;\n};  // namespace orm\n\nDROGON_EXPORT const Criteria operator&&(Criteria cond1, Criteria cond2);\nDROGON_EXPORT const Criteria operator||(Criteria cond1, Criteria cond2);\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/DbClient.h",
    "content": "/**\n *\n *  @file DbClient.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <drogon/orm/Exception.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/Result.h>\n#include <drogon/orm/ResultIterator.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/RowIterator.h>\n#include <drogon/orm/SqlBinder.h>\n#include <exception>\n#include <functional>\n#include <future>\n#include <string>\n#include <trantor/utils/Logger.h>\n#include <trantor/utils/NonCopyable.h>\n\n#ifdef __cpp_impl_coroutine\n#include <drogon/utils/coroutine.h>\n#endif\n\nnamespace drogon\n{\nnamespace orm\n{\nusing ResultCallback = std::function<void(const Result &)>;\nusing ExceptionCallback = std::function<void(const DrogonDbException &)>;\n\nclass Transaction;\nclass DbClient;\n\nnamespace internal\n{\n#ifdef __cpp_impl_coroutine\nstruct [[nodiscard]] SqlAwaiter : public CallbackAwaiter<Result>\n{\n    explicit SqlAwaiter(internal::SqlBinder &&binder)\n        : binder_(std::move(binder))\n    {\n    }\n\n    void await_suspend(std::coroutine_handle<> handle)\n    {\n        binder_ >> [handle, this](const drogon::orm::Result &result) {\n            setValue(result);\n            handle.resume();\n        };\n        binder_ >> [handle, this](const std::exception_ptr &e) {\n            setException(e);\n            handle.resume();\n        };\n        binder_.exec();\n    }\n\n  private:\n    internal::SqlBinder binder_;\n};\n\nstruct [[nodiscard]] TransactionAwaiter\n    : public CallbackAwaiter<std::shared_ptr<Transaction> >\n{\n    explicit TransactionAwaiter(DbClient *client) : client_(client)\n    {\n    }\n\n    void await_suspend(std::coroutine_handle<> handle);\n\n  private:\n    DbClient *client_;\n};\n\n#endif\n\n}  // namespace internal\n\n/// Database client abstract class\nclass DROGON_EXPORT DbClient : public trantor::NonCopyable\n{\n  public:\n    virtual ~DbClient();\n    /// Create a new database client with multiple connections;\n    /**\n     * @param connInfo: Connection string with some parameters,\n     * each parameter setting is in the form keyword = value. Spaces around the\n     * equal sign are optional.\n     * To write an empty value, or a value containing spaces, surround it with\n     * single quotes, e.g.,\n     * keyword = 'a value'. Single quotes and backslashes within the value must\n     * be escaped with a backslash, i.e., \\' and \\\\. Example: host=localhost\n     * port=5432 dbname=mydb connect_timeout=10 password='' The currently\n     * recognized parameter key words are:\n     * - host: can be either a host name or an IP address.\n     * - port: Port number to connect to at the server host.\n     * - dbname: The database name. Defaults to be the same as the user name.\n     * - user:  user name to connect as. With PostgreSQL defaults to be the same\n     * as the operating system name of the user running the application.\n     * - password: Password to be used if the server demands password\n     * authentication.\n     * - client_encoding: The character set to be used on database connections.\n     *\n     * For other key words on PostgreSQL, see the PostgreSQL documentation.\n     * Only a pair of key values is valid for Sqlite3, and its keyword is\n     * 'filename'.\n     *\n     * @param connNum: The number of connections to database server;\n     */\n    static std::shared_ptr<DbClient> newPgClient(const std::string &connInfo,\n                                                 size_t connNum,\n                                                 bool autoBatch = false);\n    static std::shared_ptr<DbClient> newMysqlClient(const std::string &connInfo,\n                                                    size_t connNum);\n    static std::shared_ptr<DbClient> newSqlite3Client(\n        const std::string &connInfo,\n        size_t connNum);\n\n    /// Async and nonblocking method\n    /**\n     * @param sql is the SQL statement to be executed;\n     * @tparam FUNCTION1 is usually the ResultCallback type;\n     * @tparam FUNCTION2 is usually the ExceptionCallback type;\n     * @param rCallback callback function;\n     * @param exceptCallback callback function on exception;\n     * @param args are parameters that are bound to placeholders in the sql\n     * parameter;\n     *\n     * @note\n     *\n     * If the number of args parameters is not zero, make sure that all criteria\n     * in the sql parameter set by bind parameters, for example:\n     *\n     *   1. select * from users where user_id > 10 limit 10 offset 10; //Not\n     * bad, no bind parameters are used.\n     *   2. select * from users where user_id > ? limit ? offset ?; //Good,\n     * fully use bind parameters.\n     *   3. select * from users where user_id > ? limit ? offset 10; //Bad,\n     * partially use bind parameters.\n     *\n     * Strictly speaking, try not to splice SQL statements dynamically, Instead,\n     * use the constant sql string with placeholders and the bind parameters to\n     * execute sql. This rule makes the sql execute faster and more securely,\n     * and users should follow this rule when calling all methods of DbClient.\n     *\n     */\n    template <typename FUNCTION1, typename FUNCTION2, typename... Arguments>\n    void execSqlAsync(const std::string &sql,\n                      FUNCTION1 &&rCallback,\n                      FUNCTION2 &&exceptCallback,\n                      Arguments &&...args) noexcept\n    {\n        auto binder = *this << sql;\n        (void)std::initializer_list<int>{\n            (binder << std::forward<Arguments>(args), 0)...};\n        binder >> std::forward<FUNCTION1>(rCallback);\n        binder >> std::forward<FUNCTION2>(exceptCallback);\n    }\n\n    /// Async and nonblocking method\n    template <typename... Arguments>\n    std::future<Result> execSqlAsyncFuture(const std::string &sql,\n                                           Arguments &&...args) noexcept\n    {\n        auto binder = *this << sql;\n        (void)std::initializer_list<int>{\n            (binder << std::forward<Arguments>(args), 0)...};\n        std::shared_ptr<std::promise<Result> > prom =\n            std::make_shared<std::promise<Result> >();\n        binder >> [prom](const Result &r) { prom->set_value(r); };\n        binder >>\n            [prom](const std::exception_ptr &e) { prom->set_exception(e); };\n        binder.exec();\n        return prom->get_future();\n    }\n\n    // Sync and blocking method\n    template <typename... Arguments>\n    Result execSqlSync(const std::string &sql,\n                       Arguments &&...args) noexcept(false)\n    {\n        Result r(nullptr);\n        {\n            auto binder = *this << sql;\n            (void)std::initializer_list<int>{\n                (binder << std::forward<Arguments>(args), 0)...};\n            // Use blocking mode\n            binder << Mode::Blocking;\n\n            binder >> [&r](const Result &result) { r = result; };\n            binder.exec();  // exec may be throw exception;\n        }\n        return r;\n    }\n\n#ifdef __cpp_impl_coroutine\n    template <typename... Arguments>\n    internal::SqlAwaiter execSqlCoro(const std::string &sql,\n                                     Arguments &&...args) noexcept\n    {\n        auto binder = *this << sql;\n        (void)std::initializer_list<int>{\n            (binder << std::forward<Arguments>(args), 0)...};\n        return internal::SqlAwaiter(std::move(binder));\n    }\n\n    /**\n     * @brief Execute a SQL query asynchronously using coroutine support.\n     *        This overload accepts a vector of arguments to bind to the query.\n     * @tparam T The type of the elements in the vector.\n     * @param sql The SQL query string to execute.\n     * @param args A vector of arguments to bind to the query.\n     * @return A SqlAwaiter object that can be co_awaited to retrieve the query\n     * result.\n     * @note This method is only available when coroutine support is enabled.\n     */\n    template <typename T>\n    internal::SqlAwaiter execSqlCoro(const std::string &sql,\n                                     const std::vector<T> &args) noexcept\n    {\n        auto binder = *this << sql;\n        for (const auto &arg : args)\n        {\n            binder << arg;\n        }\n        return internal::SqlAwaiter(std::move(binder));\n    }\n#endif\n\n    /// Streaming-like method for sql execution. For more information, see the\n    /// wiki page.\n    internal::SqlBinder operator<<(const std::string &sql);\n    internal::SqlBinder operator<<(std::string &&sql);\n\n    template <int N>\n    internal::SqlBinder operator<<(const char (&sql)[N])\n    {\n        return internal::SqlBinder(sql, N - 1, *this, type_);\n    }\n\n    internal::SqlBinder operator<<(const std::string_view &sql)\n    {\n        return internal::SqlBinder(sql.data(), sql.length(), *this, type_);\n    }\n\n    /// Create a transaction object.\n    /**\n     * @param commitCallback: the callback with which user can get the\n     * submitting result, The Boolean type parameter in the callback function\n     * indicates whether the transaction was submitted successfully.\n     *\n     * @note The callback only indicates the result of the 'commit' command,\n     * which is the last step of the transaction. If the transaction has been\n     * automatically or manually rolled back, the callback will never be\n     * executed. You can also use the setCommitCallback() method of a\n     * transaction object to set the callback.\n     * @note A TimeoutError exception is thrown if the operation is timed out.\n     */\n    virtual std::shared_ptr<Transaction> newTransaction(\n        const std::function<void(bool)> &commitCallback =\n            std::function<void(bool)>()) noexcept(false) = 0;\n\n    /// Create a transaction object in asynchronous mode.\n    /**\n     * @note An empty shared_ptr object is returned via the callback if the\n     * operation is timed out.\n     */\n    virtual void newTransactionAsync(\n        const std::function<void(const std::shared_ptr<Transaction> &)>\n            &callback) = 0;\n\n#ifdef __cpp_impl_coroutine\n    orm::internal::TransactionAwaiter newTransactionCoro()\n    {\n        return orm::internal::TransactionAwaiter(this);\n    }\n#endif\n\n    /**\n     * @brief Check if there is a connection successfully established.\n     *\n     * @return true\n     * @return false\n     */\n    virtual bool hasAvailableConnections() const noexcept = 0;\n\n    ClientType type() const\n    {\n        return type_;\n    }\n\n    const std::string &connectionInfo() const\n    {\n        return connectionInfo_;\n    }\n\n    /**\n     * @brief Set the Timeout value of execution of a SQL.\n     *\n     * @param timeout in seconds, if the SQL result is not returned from the\n     * server within the timeout, a TimeoutError exception with \"SQL execution\n     * timeout\" string is generated and returned to the caller.\n     * @note set the timeout value to zero or negative for no limit on time. The\n     * default value is -1.0, this means there is no time limit if this method\n     * is not called.\n     */\n    virtual void setTimeout(double timeout) = 0;\n    /**\n     * @brief Close all connections in the client. usually used by Drogon in the\n     * quit() method.\n     * */\n    virtual void closeAll() = 0;\n\n    /**\n     * @brief Enable auto-batch mode.\n     * This feature is available only for PostgreSQL 14+ version and the\n     * LIBPQ_BATCH_MODE option is CMakeLists.txt is ON.\n     * When auto-batch mode is disabled (by default), every SQL query\n     * pipelined in a connection is automatically executed in an individual\n     * implicit transaction, this behavior ensures that SQL queries cannot make\n     * side-effects to each other. Most databases client drivers execute SQL\n     * queries in this way.\n     * When auto-batch mode is enabled, SQL queries are batched automatically to\n     * an implicit transaction, the synchronization point as the end of the\n     * transaction is inserted when:\n     * 1. The number of queries in the transaction has reached the upper limit;\n     * 2. The last SQL query in the transaction contains some key words shows\n     * that the query make some changes on the database server;\n     * 3. All SQL queries that are in the same call stack of the current\n     * event-loop are sent to the server;\n     * @note the auto-batch mode is unsafe for general purpose scenarios.\n     * While the framework is doing its best to reduce the side effects of this\n     * implicit transaction, there are some risks that cannot be avoided, for\n     * example:\n     * if a command is executed which happens to SELECT from a function (it\n     * makes some changes but don't cause a synchronization point), and around\n     * the same time another unrelated command is executed which produces an\n     * error (e.g. unique constraint violation), then any side-effects from the\n     * function may get rolled back, without any indication to the program that\n     * this happened. That is, the user code executing the function will\n     * continue as if the function completed successfully and its side effects\n     * were committed, when in fact they were silently rolled back.\n     *\n     * So, users should ensure that all SQL requests sent by database clients\n     * with auto-batch mode are mutually safe, a viable strategy is to ensure\n     * that all SQL is read-only and does not interrupt the current transaction.\n     * Benefiting from the reduction in the number of transactions, the\n     * automatic batch mode has a certain performance improvement for some\n     * high-concurrency scenarios. The auto-batch mode can only be enabled\n     * before the client is used, and enabling it during use will have uncertain\n     * side effects. This feature can be enabled in the configuration file.\n     * */\n    // virtual void enableAutoBatch() = 0;\n\n  private:\n    friend internal::SqlBinder;\n    virtual void execSql(\n        const char *sql,\n        size_t sqlLength,\n        size_t paraNum,\n        std::vector<const char *> &&parameters,\n        std::vector<int> &&length,\n        std::vector<int> &&format,\n        ResultCallback &&rcb,\n        std::function<void(const std::exception_ptr &)> &&exceptCallback) = 0;\n\n  protected:\n    ClientType type_;\n    std::string connectionInfo_;\n};\n\nusing DbClientPtr = std::shared_ptr<DbClient>;\n\nclass Transaction : public DbClient\n{\n  public:\n    virtual void rollback() = 0;\n    // virtual void commit() = 0;\n    virtual void setCommitCallback(\n        const std::function<void(bool)> &commitCallback) = 0;\n\n    void closeAll() override\n    {\n    }\n};\n\n#ifdef __cpp_impl_coroutine\ninline void internal::TransactionAwaiter::await_suspend(\n    std::coroutine_handle<> handle)\n{\n    assert(client_ != nullptr);\n    client_->newTransactionAsync(\n        [this, handle](const std::shared_ptr<Transaction> &transaction) {\n            if (transaction == nullptr)\n                setException(std::make_exception_ptr(TimeoutError(\n                    \"Timeout, no connection available for transaction\")));\n            else\n                setValue(transaction);\n            handle.resume();\n        });\n}\n#endif\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/DbConfig.h",
    "content": "/**\n *\n *  @file DbConfig.h\n *  @author Nitromelon\n *\n *  Copyright 2024, Nitromelon.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <string>\n#include <unordered_map>\n#include <variant>\n\nnamespace drogon::orm\n{\nstruct PostgresConfig\n{\n    std::string host;\n    unsigned short port;\n    std::string databaseName;\n    std::string username;\n    std::string password;\n    size_t connectionNumber;\n    std::string name;\n    bool isFast;\n    std::string characterSet;\n    double timeout;\n    bool autoBatch;\n    std::unordered_map<std::string, std::string> connectOptions;\n};\n\nstruct MysqlConfig\n{\n    std::string host;\n    unsigned short port;\n    std::string databaseName;\n    std::string username;\n    std::string password;\n    size_t connectionNumber;\n    std::string name;\n    bool isFast;\n    std::string characterSet;\n    double timeout;\n};\n\nstruct Sqlite3Config\n{\n    size_t connectionNumber;\n    std::string filename;\n    std::string name;\n    double timeout;\n};\n\nusing DbConfig = std::variant<PostgresConfig, MysqlConfig, Sqlite3Config>;\n\n}  // namespace drogon::orm\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/DbListener.h",
    "content": "/**\n *\n *  @file DbListener.h\n *  @author Nitromelon\n *\n *  Copyright 2022, An Tao.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <functional>\n#include <string>\n#include <memory>\n\nnamespace trantor\n{\nclass EventLoop;\n}\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbListener;\nusing DbListenerPtr = std::shared_ptr<DbListener>;\n\n/// Database asynchronous notification listener abstract class\nclass DROGON_EXPORT DbListener\n{\n  public:\n    using MessageCallback = std::function<void(std::string, std::string)>;\n\n    virtual ~DbListener();\n\n    /// Create a new postgresql notification listener\n    /**\n     * @param connInfo: Connection string, the same as DbClient::newPgClient()\n     * @param loop: The eventloop this DbListener runs in. If empty, a new\n     * thread will be created.\n     * @return DbListenerPtr\n     * @return nullptr if postgresql is not supported.\n     */\n    static DbListenerPtr newPgListener(const std::string &connInfo,\n                                       trantor::EventLoop *loop = nullptr);\n\n    /// Listen to a channel\n    /**\n     * @param channel channel name to listen\n     * @param messageCallback callback when notification arrives on channel\n     *\n     * @note `listen()` can be called on the same channel multiple times.\n     * In this case, each `messageCallback` will be called when message arrives.\n     * However, a single `unlisten()` call will cancel all the callbacks.\n     *\n     * @note If has connection issues, the listener will keep retrying until\n     * listen success. The listener will also re-listen to all channels after\n     * re-connection.\n     * However, if user passes an invalid channel string, the operation will\n     * fail with an error log without any other actions. (This behavior may\n     * change in future. A errorCallback may be added as a parameters.)\n     */\n    virtual void listen(const std::string &channel,\n                        MessageCallback messageCallback) noexcept = 0;\n\n    /// Stop listening to channel\n    /**\n     * @param channel channel to stop listening\n     */\n    virtual void unlisten(const std::string &channel) noexcept = 0;\n};\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/DbTypes.h",
    "content": "/**\n *\n *  @file DbTypes.h\n *  @author interfector18\n *\n *  Copyright 2020, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DefaultValue\n{\n};\n\nnamespace internal\n{\nenum FieldType\n{\n    MySqlTiny,\n    MySqlShort,\n    MySqlLong,\n    MySqlLongLong,\n    MySqlNull,\n    MySqlString,\n    DrogonDefaultValue,\n    MySqlUTiny,\n    MySqlUShort,\n    MySqlULong,\n    MySqlULongLong,\n};\n\n}  // namespace internal\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/Exception.h",
    "content": "/** Definition of Drogon Db exception classes.\n *\n * Copyright (c) 2003-2018, Jeroen T. Vermeulen.\n *\n * See COPYING for copyright license.  If you did not receive a file called\n * COPYING with this source code, please notify the distributor of this mistake,\n * or contact the author.\n */\n\n// taken from libpqxx and modified\n\n/**\n *\n *  @file Exception.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <functional>\n#include <stdexcept>\n#include <string>\n\nnamespace drogon\n{\nnamespace orm\n{\n/// Mixin base class to identify drogon-db-specific exception types\n/**\n * If you wish to catch all exception types specific to drogon db for some\n * reason,\n * catch this type.  All of drogon db's exception classes are derived from it\n * through multiple-inheritance (they also fit into the standard library's\n * exception hierarchy in more fitting places).\n *\n * This class is not derived from std::exception, since that could easily lead\n * to exception classes with multiple std::exception base-class objects.  As\n * Bart Samwel points out, \"catch\" is subject to some nasty fineprint in such\n * cases.\n */\nclass DROGON_EXPORT DrogonDbException\n{\n  public:\n    /// Support run-time polymorphism, and keep this class abstract\n    virtual ~DrogonDbException() noexcept\n    {\n    }\n\n    /// Return std::exception base-class object\n    /**\n     * Use this to get at the exception's what() function, or to downcast to a\n     * more specific type using dynamic_cast.\n     *\n     * Casting directly from DrogonDbException to a specific exception type is\n     * not likely to work since DrogonDbException is not (and could not safely\n     * be) derived from std::exception.\n     *\n     * For example, to test dynamically whether an exception is an SqlError:\n     *\n     * @code\n     * try\n     * {\n     *   // ...\n     * }\n     * catch (const drogon::orm::DrogonDbException &e)\n     * {\n     *   std::cerr << e.base().what() << std::endl;\n     *   const drogon::orm::SqlError *s=dynamic_cast<const\n     * drogon::orm::SqlError*>(&e.base());\n     *   if (s) std::cerr << \"Query was: \" << s->query() << std::endl;\n     * }\n     * @endcode\n     */\n    virtual const std::exception &base() const noexcept\n    {\n        static std::exception except;\n        return except;\n    }  //[t00]\n};\n\n/// Run-time Failure encountered by drogon orm lib, similar to\n/// std::runtime_error\nclass Failure : public DrogonDbException, public std::runtime_error\n{\n    const std::exception &base() const noexcept override\n    {\n        return *this;\n    }\n\n  public:\n    DROGON_EXPORT explicit Failure(const std::string &);\n};\n\n/// Exception class for lost or failed backend connection.\n/**\n * @warning When this happens on Unix-like systems, you may also get a SIGPIPE\n * signal.  That signal aborts the program by default, so if you wish to be able\n * to continue after a connection breaks, be sure to disarm this signal.\n *\n * If you're working on a Unix-like system, see the manual page for\n * @c signal (2) on how to deal with SIGPIPE.  The easiest way to make this\n * signal harmless is to make your program ignore it:\n *\n * @code\n * #include <signal.h>\n *\n * int main()\n * {\n *   signal(SIGPIPE, SIG_IGN);\n *   // ...\n * @endcode\n */\nclass BrokenConnection : public Failure\n{\n  public:\n    DROGON_EXPORT BrokenConnection();\n    DROGON_EXPORT explicit BrokenConnection(const std::string &);\n};\n\n/// Exception class for failed queries.\n/** Carries, in addition to a regular error message, a copy of the failed query\n * and (if available) the SQLSTATE value accompanying the error.\n */\nclass SqlError : public Failure\n{\n    /// Query string.  Empty if unknown.\n    const std::string query_;\n    /// SQLSTATE string describing the error type, if known; or empty string.\n    const std::string sqlState_;\n\n    const int errcode_{0};\n    const int extendedErrcode_{0};\n\n  public:\n    DROGON_EXPORT explicit SqlError(const std::string &msg = \"\",\n                                    const std::string &Q = \"\",\n                                    const char sqlstate[] = nullptr);\n    DROGON_EXPORT explicit SqlError(const std::string &msg,\n                                    const std::string &Q,\n                                    const int errcode,\n                                    const int extendedErrcode);\n    DROGON_EXPORT virtual ~SqlError() noexcept;\n\n    /// The query whose execution triggered the exception\n    DROGON_EXPORT const std::string &query() const noexcept;\n    DROGON_EXPORT const std::string &sqlState() const noexcept;\n    DROGON_EXPORT int errcode() const noexcept;\n    DROGON_EXPORT int extendedErrcode() const noexcept;\n};\n\n/// \"Help, I don't know whether transaction was committed successfully!\"\n/** Exception that might be thrown in rare cases where the connection to the\n * database is lost while finishing a database transaction, and there's no way\n * of telling whether it was actually executed by the backend.  In this case\n * the database is left in an indeterminate (but consistent) state, and only\n * manual inspection will tell which is the case.\n */\nclass InDoubtError : public Failure\n{\n  public:\n    DROGON_EXPORT explicit InDoubtError(const std::string &);\n};\n\n/// The backend saw itself forced to roll back the ongoing transaction.\nclass TransactionRollback : public Failure\n{\n  public:\n    DROGON_EXPORT explicit TransactionRollback(const std::string &);\n};\n\n/// Transaction failed to serialize.  Please retry it.\n/** Can only happen at transaction isolation levels REPEATABLE READ and\n * SERIALIZABLE.\n *\n * The current transaction cannot be committed without violating the guarantees\n * made by its isolation level.  This is the effect of a conflict with another\n * ongoing transaction.  The transaction may still succeed if you try to\n * perform it again.\n */\nclass SerializationFailure : public TransactionRollback\n{\n  public:\n    DROGON_EXPORT explicit SerializationFailure(const std::string &);\n};\n\n/// We can't tell whether our last statement succeeded.\nclass StatementCompletionUnknown : public TransactionRollback\n{\n  public:\n    DROGON_EXPORT explicit StatementCompletionUnknown(const std::string &);\n};\n\n/// The ongoing transaction has deadlocked.  Retrying it may help.\nclass DeadlockDetected : public TransactionRollback\n{\n  public:\n    DROGON_EXPORT explicit DeadlockDetected(const std::string &);\n};\n\n/// Internal error in internal library\nclass InternalError : public DrogonDbException, public std::logic_error\n{\n    const std::exception &base() const noexcept override\n    {\n        return *this;\n    }\n\n  public:\n    DROGON_EXPORT explicit InternalError(const std::string &);\n};\n\n/// Timeout error in when executing the SQL statement.\nclass TimeoutError : public DrogonDbException, public std::logic_error\n{\n    const std::exception &base() const noexcept override\n    {\n        return *this;\n    }\n\n  public:\n    DROGON_EXPORT explicit TimeoutError(const std::string &);\n};\n\n/// Error in usage of drogon orm library, similar to std::logic_error\nclass UsageError : public DrogonDbException, public std::logic_error\n{\n    const std::exception &base() const noexcept override\n    {\n        return *this;\n    }\n\n  public:\n    DROGON_EXPORT explicit UsageError(const std::string &);\n};\n\n/// Invalid argument passed to drogon orm lib, similar to std::invalid_argument\nclass ArgumentError : public DrogonDbException, public std::invalid_argument\n{\n    const std::exception &base() const noexcept override\n    {\n        return *this;\n    }\n\n  public:\n    DROGON_EXPORT explicit ArgumentError(const std::string &);\n};\n\n/// Value conversion failed, e.g. when converting \"Hello\" to int.\nclass ConversionError : public DrogonDbException, public std::domain_error\n{\n    const std::exception &base() const noexcept override\n    {\n        return *this;\n    }\n\n  public:\n    DROGON_EXPORT explicit ConversionError(const std::string &);\n};\n\n/// Something is out of range, similar to std::out_of_range\nclass RangeError : public DrogonDbException, public std::out_of_range\n{\n    const std::exception &base() const noexcept override\n    {\n        return *this;\n    }\n\n  public:\n    DROGON_EXPORT explicit RangeError(const std::string &);\n};\n\n/// Query returned an unexpected number of rows.\nclass UnexpectedRows : public RangeError\n{\n    const std::exception &base() const noexcept override\n    {\n        return *this;\n    }\n\n  public:\n    explicit UnexpectedRows(const std::string &msg) : RangeError(msg)\n    {\n    }\n};\n\n/// Database feature not supported in current setup\nclass FeatureNotSupported : public SqlError\n{\n  public:\n    explicit FeatureNotSupported(const std::string &err,\n                                 const std::string &Q = \"\",\n                                 const char sqlstate[] = nullptr)\n        : SqlError(err, Q, sqlstate)\n    {\n    }\n\n    explicit FeatureNotSupported(const std::string &msg,\n                                 const std::string &Q,\n                                 const int errcode,\n                                 const int extendedErrcode)\n        : SqlError(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\n/// Error in data provided to SQL statement\nclass DataException : public SqlError\n{\n  public:\n    explicit DataException(const std::string &err,\n                           const std::string &Q = \"\",\n                           const char sqlstate[] = nullptr)\n        : SqlError(err, Q, sqlstate)\n    {\n    }\n\n    explicit DataException(const std::string &msg,\n                           const std::string &Q,\n                           const int errcode,\n                           const int extendedErrcode)\n        : SqlError(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass IntegrityConstraintViolation : public SqlError\n{\n  public:\n    explicit IntegrityConstraintViolation(const std::string &err,\n                                          const std::string &Q = \"\",\n                                          const char sqlstate[] = nullptr)\n        : SqlError(err, Q, sqlstate)\n    {\n    }\n\n    explicit IntegrityConstraintViolation(const std::string &msg,\n                                          const std::string &Q,\n                                          const int errcode,\n                                          const int extendedErrcode)\n        : SqlError(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass RestrictViolation : public IntegrityConstraintViolation\n{\n  public:\n    explicit RestrictViolation(const std::string &err,\n                               const std::string &Q = \"\",\n                               const char sqlstate[] = nullptr)\n        : IntegrityConstraintViolation(err, Q, sqlstate)\n    {\n    }\n\n    explicit RestrictViolation(const std::string &msg,\n                               const std::string &Q,\n                               const int errcode,\n                               const int extendedErrcode)\n        : IntegrityConstraintViolation(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass NotNullViolation : public IntegrityConstraintViolation\n{\n  public:\n    explicit NotNullViolation(const std::string &err,\n                              const std::string &Q = \"\",\n                              const char sqlstate[] = nullptr)\n        : IntegrityConstraintViolation(err, Q, sqlstate)\n    {\n    }\n\n    explicit NotNullViolation(const std::string &msg,\n                              const std::string &Q,\n                              const int errcode,\n                              const int extendedErrcode)\n        : IntegrityConstraintViolation(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass ForeignKeyViolation : public IntegrityConstraintViolation\n{\n  public:\n    explicit ForeignKeyViolation(const std::string &err,\n                                 const std::string &Q = \"\",\n                                 const char sqlstate[] = nullptr)\n        : IntegrityConstraintViolation(err, Q, sqlstate)\n    {\n    }\n\n    explicit ForeignKeyViolation(const std::string &msg,\n                                 const std::string &Q,\n                                 const int errcode,\n                                 const int extendedErrcode)\n        : IntegrityConstraintViolation(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass UniqueViolation : public IntegrityConstraintViolation\n{\n  public:\n    explicit UniqueViolation(const std::string &err,\n                             const std::string &Q = \"\",\n                             const char sqlstate[] = nullptr)\n        : IntegrityConstraintViolation(err, Q, sqlstate)\n    {\n    }\n\n    explicit UniqueViolation(const std::string &msg,\n                             const std::string &Q,\n                             const int errcode,\n                             const int extendedErrcode)\n        : IntegrityConstraintViolation(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass CheckViolation : public IntegrityConstraintViolation\n{\n  public:\n    explicit CheckViolation(const std::string &err,\n                            const std::string &Q = \"\",\n                            const char sqlstate[] = nullptr)\n        : IntegrityConstraintViolation(err, Q, sqlstate)\n    {\n    }\n\n    explicit CheckViolation(const std::string &msg,\n                            const std::string &Q,\n                            const int errcode,\n                            const int extendedErrcode)\n        : IntegrityConstraintViolation(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass InvalidCursorState : public SqlError\n{\n  public:\n    explicit InvalidCursorState(const std::string &err,\n                                const std::string &Q = \"\",\n                                const char sqlstate[] = nullptr)\n        : SqlError(err, Q, sqlstate)\n    {\n    }\n\n    explicit InvalidCursorState(const std::string &msg,\n                                const std::string &Q,\n                                const int errcode,\n                                const int extendedErrcode)\n        : SqlError(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass InvalidSqlStatementName : public SqlError\n{\n  public:\n    explicit InvalidSqlStatementName(const std::string &err,\n                                     const std::string &Q = \"\",\n                                     const char sqlstate[] = nullptr)\n        : SqlError(err, Q, sqlstate)\n    {\n    }\n\n    explicit InvalidSqlStatementName(const std::string &msg,\n                                     const std::string &Q,\n                                     const int errcode,\n                                     const int extendedErrcode)\n        : SqlError(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass InvalidCursorName : public SqlError\n{\n  public:\n    explicit InvalidCursorName(const std::string &err,\n                               const std::string &Q = \"\",\n                               const char sqlstate[] = nullptr)\n        : SqlError(err, Q, sqlstate)\n    {\n    }\n\n    explicit InvalidCursorName(const std::string &msg,\n                               const std::string &Q,\n                               const int errcode,\n                               const int extendedErrcode)\n        : SqlError(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass SyntaxError : public SqlError\n{\n  public:\n    /// Approximate position in string where error occurred, or -1 if unknown.\n    const int errorPosition_;\n\n    explicit SyntaxError(const std::string &err,\n                         const std::string &Q = \"\",\n                         const char sqlstate[] = nullptr,\n                         int pos = -1)\n        : SqlError(err, Q, sqlstate), errorPosition_(pos)\n    {\n    }\n\n    explicit SyntaxError(const std::string &msg,\n                         const std::string &Q,\n                         const int errcode,\n                         const int extendedErrcode,\n                         int pos = -1)\n        : SqlError(msg, Q, errcode, extendedErrcode), errorPosition_(pos)\n    {\n    }\n};\n\nclass UndefinedColumn : public SyntaxError\n{\n  public:\n    explicit UndefinedColumn(const std::string &err,\n                             const std::string &Q = \"\",\n                             const char sqlstate[] = nullptr)\n        : SyntaxError(err, Q, sqlstate)\n    {\n    }\n\n    explicit UndefinedColumn(const std::string &msg,\n                             const std::string &Q,\n                             const int errcode,\n                             const int extendedErrcode)\n        : SyntaxError(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass UndefinedFunction : public SyntaxError\n{\n  public:\n    explicit UndefinedFunction(const std::string &err,\n                               const std::string &Q = \"\",\n                               const char sqlstate[] = nullptr)\n        : SyntaxError(err, Q, sqlstate)\n    {\n    }\n\n    explicit UndefinedFunction(const std::string &msg,\n                               const std::string &Q,\n                               const int errcode,\n                               const int extendedErrcode)\n        : SyntaxError(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass UndefinedTable : public SyntaxError\n{\n  public:\n    explicit UndefinedTable(const std::string &err,\n                            const std::string &Q = \"\",\n                            const char sqlstate[] = nullptr)\n        : SyntaxError(err, Q, sqlstate)\n    {\n    }\n\n    explicit UndefinedTable(const std::string &msg,\n                            const std::string &Q,\n                            const int errcode,\n                            const int extendedErrcode)\n        : SyntaxError(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass InsufficientPrivilege : public SqlError\n{\n  public:\n    explicit InsufficientPrivilege(const std::string &err,\n                                   const std::string &Q = \"\",\n                                   const char sqlstate[] = nullptr)\n        : SqlError(err, Q, sqlstate)\n    {\n    }\n\n    explicit InsufficientPrivilege(const std::string &msg,\n                                   const std::string &Q,\n                                   const int errcode,\n                                   const int extendedErrcode)\n        : SqlError(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\n/// Resource shortage on the server\nclass InsufficientResources : public SqlError\n{\n  public:\n    explicit InsufficientResources(const std::string &err,\n                                   const std::string &Q = \"\",\n                                   const char sqlstate[] = nullptr)\n        : SqlError(err, Q, sqlstate)\n    {\n    }\n\n    explicit InsufficientResources(const std::string &msg,\n                                   const std::string &Q,\n                                   const int errcode,\n                                   const int extendedErrcode)\n        : SqlError(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass DiskFull : public InsufficientResources\n{\n  public:\n    explicit DiskFull(const std::string &err,\n                      const std::string &Q = \"\",\n                      const char sqlstate[] = nullptr)\n        : InsufficientResources(err, Q, sqlstate)\n    {\n    }\n\n    explicit DiskFull(const std::string &msg,\n                      const std::string &Q,\n                      const int errcode,\n                      const int extendedErrcode)\n        : InsufficientResources(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass OutOfMemory : public InsufficientResources\n{\n  public:\n    explicit OutOfMemory(const std::string &err,\n                         const std::string &Q = \"\",\n                         const char sqlstate[] = nullptr)\n        : InsufficientResources(err, Q, sqlstate)\n    {\n    }\n\n    explicit OutOfMemory(const std::string &msg,\n                         const std::string &Q,\n                         const int errcode,\n                         const int extendedErrcode)\n        : InsufficientResources(msg, Q, errcode, extendedErrcode)\n    {\n    }\n};\n\nclass TooManyConnections : public BrokenConnection\n{\n  public:\n    explicit TooManyConnections(const std::string &err) : BrokenConnection(err)\n    {\n    }\n};\n\n// /// PL/pgSQL error\n// /** Exceptions derived from this class are errors from PL/pgSQL procedures.\n//  */\n// class PQXX_LIBEXPORT plpgsql_error : public sql_error\n// {\n// public:\n//   explicit plpgsql_error(\n//       const std::string &err,\n//       const std::string &Q = \"\",\n//       const char sqlstate[] = nullptr) : sql_error(err, Q, sqlstate) {}\n// };\n\n// /// Exception raised in PL/pgSQL procedure\n// class PQXX_LIBEXPORT plpgsql_raise : public plpgsql_error\n// {\n// public:\n//   explicit plpgsql_raise(\n//       const std::string &err,\n//       const std::string &Q = \"\",\n//       const char sqlstate[] = nullptr) : plpgsql_error(err, Q, sqlstate) {}\n// };\n\n// class PQXX_LIBEXPORT plpgsql_no_data_found : public plpgsql_error\n// {\n// public:\n//   explicit plpgsql_no_data_found(\n//       const std::string &err,\n//       const std::string &Q = \"\",\n//       const char sqlstate[] = nullptr) : plpgsql_error(err, Q, sqlstate) {}\n// };\n\n// class PQXX_LIBEXPORT plpgsql_too_many_rows : public plpgsql_error\n// {\n// public:\n//   explicit plpgsql_too_many_rows(\n//       const std::string &err,\n//       const std::string &Q = \"\",\n//       const char sqlstate[] = nullptr) : plpgsql_error(err, Q, sqlstate) {}\n// };\nusing DrogonDbExceptionCallback =\n    std::function<void(const DrogonDbException &)>;\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/Field.h",
    "content": "/**\n *\n *  @file Field.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n// Taken from libpqxx and modified.\n// The license for libpqxx can be found in the COPYING file.\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <string_view>\n#include <drogon/orm/ArrayParser.h>\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <trantor/utils/Logger.h>\n#include <memory>\n#include <sstream>\n#include <string>\n#include <vector>\n#ifdef __linux__\n#include <arpa/inet.h>\n#endif\n\nnamespace drogon\n{\nnamespace orm\n{\n/// Reference to a field in a result set.\n/**\n * A field represents one entry in a row.  It represents an actual value\n * in the result set, and can be converted to various types.\n */\nclass DROGON_EXPORT Field\n{\n  public:\n    using SizeType = unsigned long;\n\n    /// Column name\n    const char *name() const;\n\n    /// Is this field's value null?\n    bool isNull() const;\n\n    /// Read as plain C string\n    /**\n     * Since the field's data is stored internally in the form of a\n     * zero-terminated C string, this is the fastest way to read it.  Use the\n     * to() or as() functions to convert the string to other types such as\n     * @c int, or to C++ strings.\n     */\n    const char *c_str() const;\n\n    /// Get the length of the plain C string\n    size_t length() const\n    {\n        return result_.getLength(row_, column_);\n    }\n\n    /// Convert to a type T value\n    template <typename T>\n    T as() const\n    {\n        if (isNull())\n            return T();\n        auto data_ = result_.getValue(row_, column_);\n        // auto dataLength_ = result_.getLength(row_, column_);\n        // For binary format!\n        // if (dataLength_ == 1)\n        // {\n        //     return *data_;\n        // }\n        // else if (dataLength_ == 4)\n        // {\n        //     const int32_t *n = (int32_t *)data_;\n        //     return ntohl(*n);\n        // }\n        // else if (dataLength_ == 8)\n        // {\n        //     const int64_t *n = (int64_t *)data_;\n        //     return ntohll(*n);\n        // }\n        // return 0;\n        T value = T();\n        if (data_)\n        {\n            try\n            {\n                std::stringstream ss(data_);\n                ss >> value;\n            }\n            catch (...)\n            {\n                LOG_DEBUG << \"Type error\";\n            }\n        }\n        return value;\n    }\n\n    /// Parse the field as an SQL array.\n    /**\n     * Call the parser to retrieve values (and structure) from the array.\n     *\n     * Make sure the @c result object stays alive until parsing is finished.  If\n     * you keep the @c row of @c field object alive, it will keep the @c result\n     * object alive as well.\n     */\n    ArrayParser getArrayParser() const\n    {\n        return ArrayParser(result_.getValue(row_, column_));\n    }\n\n    template <typename T>\n    std::vector<std::shared_ptr<T>> asArray() const\n    {\n        std::vector<std::shared_ptr<T>> ret;\n        auto arrParser = getArrayParser();\n        while (1)\n        {\n            auto arrVal = arrParser.getNext();\n            if (arrVal.first == ArrayParser::juncture::done)\n            {\n                break;\n            }\n            if (arrVal.first == ArrayParser::juncture::string_value)\n            {\n                T val;\n                std::stringstream ss(std::move(arrVal.second));\n                ss >> val;\n                ret.push_back(std::shared_ptr<T>(new T(val)));\n            }\n            else if (arrVal.first == ArrayParser::juncture::null_value)\n            {\n                ret.push_back(std::shared_ptr<T>());\n            }\n        }\n        return ret;\n    }\n\n  protected:\n    Result::SizeType row_;\n    /**\n     * Column number\n     * You'd expect this to be a size_t, but due to the way reverse iterators\n     * are related to regular iterators, it must be allowed to underflow to -1.\n     */\n    long column_;\n    friend class Row;\n    Field(const Row &row, Row::SizeType columnNum) noexcept;\n\n  private:\n    const Result result_;\n};\n\ntemplate <>\nDROGON_EXPORT std::string Field::as<std::string>() const;\ntemplate <>\nDROGON_EXPORT const char *Field::as<const char *>() const;\ntemplate <>\nDROGON_EXPORT char *Field::as<char *>() const;\ntemplate <>\nDROGON_EXPORT std::vector<char> Field::as<std::vector<char>>() const;\n\ntemplate <>\ninline std::string_view Field::as<std::string_view>() const\n{\n    auto first = result_.getValue(row_, column_);\n    auto length = result_.getLength(row_, column_);\n    return {first, length};\n}\n\ntemplate <>\ninline float Field::as<float>() const\n{\n    if (isNull())\n        return 0.0;\n    return std::stof(result_.getValue(row_, column_));\n}\n\ntemplate <>\ninline double Field::as<double>() const\n{\n    if (isNull())\n        return 0.0;\n    return std::stod(result_.getValue(row_, column_));\n}\n\ntemplate <>\ninline bool Field::as<bool>() const\n{\n    if (result_.getLength(row_, column_) != 1)\n    {\n        return false;\n    }\n    auto value = result_.getValue(row_, column_);\n    if (*value == 't' || *value == '1')\n        return true;\n    return false;\n}\n\ntemplate <>\ninline int Field::as<int>() const\n{\n    if (isNull())\n        return 0;\n    return std::stoi(result_.getValue(row_, column_));\n}\n\ntemplate <>\ninline long Field::as<long>() const\n{\n    if (isNull())\n        return 0;\n    return std::stol(result_.getValue(row_, column_));\n}\n\ntemplate <>\ninline int8_t Field::as<int8_t>() const\n{\n    if (isNull())\n        return 0;\n    return static_cast<int8_t>(atoi(result_.getValue(row_, column_)));\n}\n\ntemplate <>\ninline long long Field::as<long long>() const\n{\n    if (isNull())\n        return 0;\n    return atoll(result_.getValue(row_, column_));\n}\n\ntemplate <>\ninline unsigned int Field::as<unsigned int>() const\n{\n    if (isNull())\n        return 0;\n    return static_cast<unsigned int>(\n        std::stoul(result_.getValue(row_, column_)));\n}\n\ntemplate <>\ninline unsigned long Field::as<unsigned long>() const\n{\n    if (isNull())\n        return 0;\n    return std::stoul(result_.getValue(row_, column_));\n}\n\ntemplate <>\ninline uint8_t Field::as<uint8_t>() const\n{\n    if (isNull())\n        return 0;\n    return static_cast<uint8_t>(atoi(result_.getValue(row_, column_)));\n}\n\ntemplate <>\ninline unsigned long long Field::as<unsigned long long>() const\n{\n    if (isNull())\n        return 0;\n    return std::stoull(result_.getValue(row_, column_));\n}\n\n// std::vector<int32_t> Field::as<std::vector<int32_t>>() const;\n// template <>\n// std::vector<int64_t> Field::as<std::vector<int64_t>>() const;\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/FilterBuilder.h",
    "content": "/**\n *\n *  @file FilterBuilder.h\n *  @author Ken Matsui\n *\n *  Copyright 2022, Ken Matsui.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/orm/TransformBuilder.h>\n#include <string>\n\nnamespace drogon\n{\nnamespace orm\n{\ntemplate <typename T, bool SelectAll>\nclass FilterBuilder : public TransformBuilder<T, SelectAll, false>\n{\n  public:\n    /**\n     * @brief A default constructor for derived classes.\n     *\n     * @return FilterBuilder The FilterBuilder itself.\n     */\n    FilterBuilder() = default;\n\n    /**\n     * @brief A copy constructor to be called by QueryBuilder.\n     *\n     * @param from The table.\n     * @param columns The columns.\n     *\n     * @return FilterBuilder The FilterBuilder itself.\n     */\n    FilterBuilder(const std::string &from, const std::string &columns)\n    {\n        this->from_ = from;\n        this->columns_ = columns;\n    }\n\n    /**\n     * @brief Filter rows whose value is the same as `value`.\n     *\n     * @param column The column to be filtered.\n     * @param value The value to filter rows.\n     *\n     * @return FilterBuilder& The FilterBuilder itself.\n     */\n    inline FilterBuilder &eq(const std::string &column,\n                             const std::string &value)\n    {\n        this->assert_column(column);\n        this->filters_.push_back({column, CompareOperator::EQ, value});\n        return *this;\n    }\n\n    /**\n     * @brief Filter rows whose value is NOT the same as `value`.\n     *\n     * @param column The column to be filtered.\n     * @param value The value to filter rows.\n     *\n     * @return FilterBuilder& The FilterBuilder itself.\n     */\n    inline FilterBuilder &neq(const std::string &column,\n                              const std::string &value)\n    {\n        this->assert_column(column);\n        this->filters_.push_back({column, CompareOperator::NE, value});\n        return *this;\n    }\n\n    /**\n     * @brief Filter rows whose value is greater than `value`.\n     *\n     * @param column The column to be filtered.\n     * @param value The value to filter rows.\n     *\n     * @return FilterBuilder& The FilterBuilder itself.\n     */\n    inline FilterBuilder &gt(const std::string &column,\n                             const std::string &value)\n    {\n        this->assert_column(column);\n        this->filters_.push_back({column, CompareOperator::GT, value});\n        return *this;\n    }\n\n    /**\n     * @brief Filter rows whose value is greater than or equal to `value`.\n     *\n     * @param column The column to be filtered.\n     * @param value The value to filter rows.\n     *\n     * @return FilterBuilder& The FilterBuilder itself.\n     */\n    inline FilterBuilder &gte(const std::string &column,\n                              const std::string &value)\n    {\n        this->assert_column(column);\n        this->filters_.push_back({column, CompareOperator::GE, value});\n        return *this;\n    }\n\n    /**\n     * @brief Filter rows whose value is less than `value`.\n     *\n     * @param column The column to be filtered.\n     * @param value The value to filter rows.\n     *\n     * @return FilterBuilder& The FilterBuilder itself.\n     */\n    inline FilterBuilder &lt(const std::string &column,\n                             const std::string &value)\n    {\n        this->assert_column(column);\n        this->filters_.push_back({column, CompareOperator::LT, value});\n        return *this;\n    }\n\n    /**\n     * @brief Filter rows whose value is less than or equal to `value`.\n     *\n     * @param column The column to be filtered.\n     * @param value The value to filter rows.\n     *\n     * @return FilterBuilder& The FilterBuilder itself.\n     */\n    inline FilterBuilder &lte(const std::string &column,\n                              const std::string &value)\n    {\n        this->assert_column(column);\n        this->filters_.push_back({column, CompareOperator::LE, value});\n        return *this;\n    }\n\n    /**\n     * @brief Filter rows whose value matches the `pattern`.\n     *\n     * @param column The column to be filtered.\n     * @param pattern The pattern to filter rows.\n     *\n     * @return FilterBuilder& The FilterBuilder itself.\n     */\n    inline FilterBuilder &like(const std::string &column,\n                               const std::string &pattern)\n    {\n        this->assert_column(column);\n        this->filters_.push_back({column, CompareOperator::Like, pattern});\n        return *this;\n    }\n};\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/FunctionTraits.h",
    "content": "/**\n *\n *  FunctionTraits.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <exception>\n#include <string>\n#include <tuple>\n#include <type_traits>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass Result;\nclass DrogonDbException;\n\nnamespace internal\n{\ntemplate <typename>\nstruct FunctionTraits;\n\ntemplate <>\nstruct FunctionTraits<void (*)()>\n{\n    using result_type = void;\n    template <std::size_t Index>\n    using argument = void;\n    static const std::size_t arity = 0;\n\n    static const bool isSqlCallback = false;\n    static const bool isExceptCallback = false;\n};\n\n// functor,lambda\ntemplate <typename Function>\nstruct FunctionTraits\n    : public FunctionTraits<\n          decltype(&std::remove_reference<Function>::type::operator())>\n{\n};\n\ntemplate <typename ClassType, typename ReturnType, typename... Arguments>\nstruct FunctionTraits<ReturnType (ClassType::*)(Arguments...) const>\n    : FunctionTraits<ReturnType (*)(Arguments...)>\n{\n};\n\ntemplate <typename ClassType, typename ReturnType, typename... Arguments>\nstruct FunctionTraits<ReturnType (ClassType::*)(Arguments...)>\n    : FunctionTraits<ReturnType (*)(Arguments...)>\n{\n};\n\ntemplate <>\nstruct FunctionTraits<void (*)(const Result &)>\n    : public FunctionTraits<void (*)()>\n{\n    static const bool isSqlCallback = true;\n    static const bool isStepResultCallback = false;\n    static const bool isExceptCallback = false;\n};\n\ntemplate <>\nstruct FunctionTraits<void (*)(const DrogonDbException &)>\n    : public FunctionTraits<void (*)()>\n{\n    static const bool isExceptCallback = true;\n    static const bool isSqlCallback = false;\n    static const bool isStepResultCallback = false;\n    static const bool isPtr = false;\n};\n\ntemplate <>\nstruct FunctionTraits<void (*)(const std::exception_ptr &)>\n    : public FunctionTraits<void (*)()>\n{\n    static const bool isExceptCallback = true;\n    static const bool isSqlCallback = false;\n    static const bool isStepResultCallback = false;\n    static const bool isPtr = true;\n};\n\ntemplate <typename ReturnType, typename... Arguments>\nstruct FunctionTraits<ReturnType (*)(bool, Arguments...)>\n    : FunctionTraits<ReturnType (*)(Arguments...)>\n{\n    static const bool isSqlCallback = true;\n    static const bool isStepResultCallback = true;\n};\n\ntemplate <typename ReturnType, typename... Arguments>\nstruct FunctionTraits<ReturnType (*)(Arguments...)>\n{\n    using result_type = ReturnType;\n\n    template <std::size_t Index>\n    using argument =\n        typename std::tuple_element<Index, std::tuple<Arguments...>>::type;\n\n    static const std::size_t arity = sizeof...(Arguments);\n\n    static const bool isSqlCallback = true;\n    static const bool isStepResultCallback = true;\n    static const bool isExceptCallback = false;\n    static const bool isPtr = false;\n};\n}  // namespace internal\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/Mapper.h",
    "content": "/**\n *\n *  @file Mapper.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <drogon/orm/Criteria.h>\n#include <drogon/orm/DbClient.h>\n#include <drogon/utils/Utilities.h>\n#include <string>\n#include <type_traits>\n#include <vector>\n\n#ifdef _WIN32\nusing ssize_t = std::intptr_t;\n#endif\n\nnamespace drogon\n{\nnamespace orm\n{\nenum class SortOrder\n{\n    ASC,\n    DESC\n};\n\nnamespace internal\n{\ntemplate <typename T, bool hasPrimaryKey = true>\nstruct Traits\n{\n    using type = typename T::PrimaryKeyType;\n};\n\ntemplate <typename T>\nstruct Traits<T, false>\n{\n    using type = int;\n};\n\ntemplate <typename T>\nstruct has_sqlForFindingByPrimaryKey\n{\n  private:\n    using yes = std::true_type;\n    using no = std::false_type;\n\n    template <typename U>\n    static auto test(int) -> decltype(U::sqlForFindingByPrimaryKey(), yes());\n\n    template <typename>\n    static no test(...);\n\n  public:\n    static constexpr bool value = std::is_same_v<decltype(test<T>(0)), yes>;\n};\n\ntemplate <typename T>\nstruct has_sqlForDeletingByPrimaryKey\n{\n  private:\n    using yes = std::true_type;\n    using no = std::false_type;\n\n    template <typename U>\n    static auto test(int)\n        -> decltype(U::sqlForDeletingByPrimaryKey().length(), yes());\n\n    template <typename>\n    static no test(...);\n\n  public:\n    static constexpr bool value = std::is_same_v<decltype(test<T>(0)), yes>;\n};\n}  // namespace internal\n\n/**\n * @brief The mapper template\n *\n * @tparam T The type of the model to be mapped.\n *\n * @details The mapping between the model object and the database table is\n * performed by the Mapper class template. The Mapper class template\n * encapsulates common operations such as adding, deleting, and changing, so\n * that the user can perform the above operations without writing a SQL\n * statement.\n *\n * The construction of the Mapper object is very simple. The template\n * parameter is the type of the model you want to access. The constructor has\n * only one parameter, which is the DbClient smart pointer mentioned earlier. As\n * mentioned earlier, the Transaction class is a subclass of DbClient, so you\n * can also construct a Mapper object with a smart pointer to a transaction,\n * which means that the Mapper mapping also supports transactions.\n *\n * Like DbClient, Mapper also provides asynchronous and synchronous interfaces.\n * The synchronous interface is blocked and may throw an exception. The returned\n * future object is blocked in the get() method and may throw an exception. The\n * normal asynchronous interface does not throw an exception, but returns the\n * result through two callbacks (result callback and exception callback). The\n * type of the exception callback is the same as that in the DbClient interface.\n * The result callback is also divided into several categories according to the\n * interface function.\n */\ntemplate <typename T>\nclass Mapper\n{\n  public:\n    /**\n     * @brief Construct a new Mapper object\n     *\n     * @param client The smart pointer to the database client object.\n     */\n    explicit Mapper(DbClientPtr client) : client_(std::move(client))\n    {\n    }\n\n    /**\n     * @brief Add a limit to the query.\n     *\n     * @param limit The limit\n     * @return Mapper<T>& The Mapper itself.\n     */\n    Mapper<T> &limit(size_t limit);\n\n    /**\n     * @brief Add a offset to the query.\n     *\n     * @param offset The offset.\n     * @return Mapper<T>& The Mapper itself.\n     */\n    Mapper<T> &offset(size_t offset);\n\n    /**\n     * @brief Set the order of the results.\n     *\n     * @param colName the column name, the results are sorted by that column\n     * @param order Ascending or descending order\n     * @return Mapper<T>& The Mapper itself.\n     */\n    Mapper<T> &orderBy(const std::string &colName,\n                       const SortOrder &order = SortOrder::ASC);\n\n    /**\n     * @brief Set the order of the results.\n     *\n     * @param colIndex the column index, the results are sorted by that column\n     * @param order Ascending or descending order\n     * @return Mapper<T>& The Mapper itself.\n     */\n    Mapper<T> &orderBy(size_t colIndex,\n                       const SortOrder &order = SortOrder::ASC);\n\n    /**\n     * @brief Set limit and offset to achieve pagination.\n     * This method will override limit() and offset(), and will be overridden by\n     * them.\n     *\n     * @param page The page number\n     * @param perPage The number of columns per page\n     * @return Mapper<T>& The Mapper itself.\n     */\n    Mapper<T> &paginate(size_t page, size_t perPage);\n\n    /**\n     * @brief Lock the result for updating.\n     *\n     * @return Mapper<T>& The Mapper itself.\n     */\n    Mapper<T> &forUpdate();\n\n    using SingleRowCallback = std::function<void(T)>;\n    using MultipleRowsCallback = std::function<void(std::vector<T>)>;\n    using CountCallback = std::function<void(const size_t)>;\n\n    using TraitsPKType = typename internal::\n        Traits<T, !std::is_same_v<typename T::PrimaryKeyType, void>>::type;\n\n    /**\n     * @brief Find a record by the primary key.\n     *\n     * @param key The value of the primary key.\n     * @return T The record of the primary key.\n     * @note If no hit record exists, an UnexpectedRows exception is thrown.\n     */\n\n    template <typename U = T>\n    inline T findByPrimaryKey(const TraitsPKType &key) noexcept(false)\n    {\n        if constexpr (!std::is_same_v<typename U::PrimaryKeyType, void>)\n        {\n            static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                          \"No primary key in the table!\");\n            static_assert(\n                internal::has_sqlForFindingByPrimaryKey<T>::value,\n                \"No function member named sqlForFindingByPrimaryKey, please \"\n                \"make sure that the model class is generated by the latest \"\n                \"version of drogon_ctl\");\n            // return findOne(Criteria(T::primaryKeyName, key));\n            std::string sql = T::sqlForFindingByPrimaryKey();\n            if (forUpdate_)\n            {\n                sql += \" for update\";\n            }\n            clear();\n            Result r(nullptr);\n            {\n                auto binder = *client_ << std::move(sql);\n                outputPrimaryKeyToBinder(key, binder);\n                binder << Mode::Blocking;\n                binder >> [&r](const Result &result) { r = result; };\n                binder.exec();  // exec may be throw exception;\n            }\n            if (r.size() == 0)\n            {\n                throw UnexpectedRows(\"0 rows found\");\n            }\n            else if (r.size() > 1)\n            {\n                throw UnexpectedRows(\"Found more than one row\");\n            }\n            auto row = r[0];\n            return T(row);\n        }\n        else\n        {\n            LOG_FATAL << \"The table must have a primary key\";\n            abort();\n        }\n    }\n\n    /**\n     * @brief Asynchronously find a record by the primary key.\n     *\n     * @param key The value of the primary key.\n     * @param rcb Is called when a record is found.\n     * @param ecb Is called when an error occurs or a record cannot be found.\n     */\n    template <typename U = T>\n    inline void findByPrimaryKey(const TraitsPKType &key,\n                                 const SingleRowCallback &rcb,\n                                 const ExceptionCallback &ecb) noexcept\n    {\n        if constexpr (!std::is_same_v<typename U::PrimaryKeyType, void>)\n        {\n            static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                          \"No primary key in the table!\");\n            static_assert(\n                internal::has_sqlForFindingByPrimaryKey<T>::value,\n                \"No function member named sqlForFindingByPrimaryKey, please \"\n                \"make sure that the model class is generated by the latest \"\n                \"version of drogon_ctl\");\n            // findOne(Criteria(T::primaryKeyName, key), rcb, ecb);\n            std::string sql = T::sqlForFindingByPrimaryKey();\n            if (forUpdate_)\n            {\n                sql += \" for update\";\n            }\n            clear();\n            auto binder = *client_ << std::move(sql);\n            outputPrimaryKeyToBinder(key, binder);\n            binder >> [ecb, rcb](const Result &r) {\n                if (r.size() == 0)\n                {\n                    ecb(UnexpectedRows(\"0 rows found\"));\n                }\n                else if (r.size() > 1)\n                {\n                    ecb(UnexpectedRows(\"Found more than one row\"));\n                }\n                else\n                {\n                    rcb(T(r[0]));\n                }\n            };\n            binder >> ecb;\n        }\n        else\n        {\n            LOG_FATAL << \"The table must have a primary key\";\n            abort();\n        }\n    }\n\n    /**\n     * @brief Asynchronously find a record by the primary key.\n     *\n     * @param key The value of the primary key.\n     * @return std::future<T> The future object with which user can get the\n     * result.\n     * @note If no hit record exists, an UnexpectedRows exception is thrown when\n     * user calls the get() method of the future object.\n     */\n    template <typename U = T>\n    inline std::future<T> findFutureByPrimaryKey(\n        const TraitsPKType &key) noexcept\n    {\n        if constexpr (!std::is_same_v<typename U::PrimaryKeyType, void>)\n        {\n            static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                          \"No primary key in the table!\");\n            static_assert(\n                internal::has_sqlForFindingByPrimaryKey<T>::value,\n                \"No function member named sqlForFindingByPrimaryKey, please \"\n                \"make sure that the model class is generated by the latest \"\n                \"version of drogon_ctl\");\n            // return findFutureOne(Criteria(T::primaryKeyName, key));\n            std::string sql = T::sqlForFindingByPrimaryKey();\n            if (forUpdate_)\n            {\n                sql += \" for update\";\n            }\n            clear();\n            auto binder = *client_ << std::move(sql);\n            outputPrimaryKeyToBinder(key, binder);\n\n            std::shared_ptr<std::promise<T>> prom =\n                std::make_shared<std::promise<T>>();\n            binder >> [prom](const Result &r) {\n                if (r.size() == 0)\n                {\n                    prom->set_exception(std::make_exception_ptr(\n                        UnexpectedRows(\"0 rows found\")));\n                }\n                else if (r.size() > 1)\n                {\n                    prom->set_exception(std::make_exception_ptr(\n                        UnexpectedRows(\"Found more than one row\")));\n                }\n                else\n                {\n                    prom->set_value(T(r[0]));\n                }\n            };\n            binder >>\n                [prom](const std::exception_ptr &e) { prom->set_exception(e); };\n            binder.exec();\n            return prom->get_future();\n        }\n        else\n        {\n            LOG_FATAL << \"The table must have a primary key\";\n            abort();\n        }\n    }\n\n    /**\n     * @brief Find all the records in the table.\n     *\n     * @return std::vector<T> The vector of all the records.\n     */\n    std::vector<T> findAll() noexcept(false);\n\n    /**\n     * @brief Asynchronously find all the records in the table.\n     *\n     * @param rcb is called with the result.\n     * @param ecb is called when an error occurs.\n     */\n    void findAll(const MultipleRowsCallback &rcb,\n                 const ExceptionCallback &ecb) noexcept;\n\n    /**\n     * @brief Asynchronously find all the records in the table.\n     *\n     * @return std::future<std::vector<T>> The future object with which user can\n     * get the result.\n     */\n    std::future<std::vector<T>> findFutureAll() noexcept;\n\n    /**\n     * @brief Get the count of rows that match the given criteria.\n     *\n     * @param criteria The criteria.\n     * @return size_t The number of rows.\n     */\n    size_t count(const Criteria &criteria = Criteria()) noexcept(false);\n\n    /**\n     * @brief Asynchronously get the number of rows that match the given\n     * criteria.\n     *\n     * @param criteria The criteria.\n     * @param rcb is clalled with the result.\n     * @param ecb is called when an error occurs.\n     */\n    void count(const Criteria &criteria,\n               const CountCallback &rcb,\n               const ExceptionCallback &ecb) noexcept;\n\n    /**\n     * @brief Asynchronously get the number of rows that match the given\n     * criteria.\n     *\n     * @param criteria The criteria.\n     * @return std::future<size_t> The future object with which user can get the\n     * number of rows\n     */\n    std::future<size_t> countFuture(\n        const Criteria &criteria = Criteria()) noexcept;\n\n    /**\n     * @brief Find one record that matches the given criteria.\n     *\n     * @param criteria The criteria.\n     * @return T The result record.\n     * @note if the number of rows is greater than one or equal to zero, an\n     * UnexpectedRows exception is thrown.\n     */\n    T findOne(const Criteria &criteria) noexcept(false);\n\n    /**\n     * @brief Asynchronously find one record that matches the given criteria.\n     *\n     * @param criteria The criteria.\n     * @param rcb is called with the result.\n     * @param ecb is called when an error occurs.\n     */\n    void findOne(const Criteria &criteria,\n                 const SingleRowCallback &rcb,\n                 const ExceptionCallback &ecb) noexcept;\n\n    /**\n     * @brief Asynchronously find one record that matches the given criteria.\n     *\n     * @param criteria The criteria.\n     * @return std::future<T> The future object with which user can get the\n     * result.\n     * @note if the number of rows is greater than one or equal to zero, an\n     * UnexpectedRows exception is thrown when the get() method of the future\n     * object is called.\n     */\n    std::future<T> findFutureOne(const Criteria &criteria) noexcept;\n\n    /**\n     * @brief Select the rows that match the given criteria.\n     *\n     * @param criteria The criteria.\n     * @return std::vector<T> The vector of rows that match the given criteria.\n     */\n    std::vector<T> findBy(const Criteria &criteria) noexcept(false);\n\n    /**\n     * @brief Asynchronously select the rows that match the given criteria.\n     *\n     * @param criteria The criteria.\n     * @param rcb is called with the result.\n     * @param ecb is called when an error occurs.\n     */\n    void findBy(const Criteria &criteria,\n                const MultipleRowsCallback &rcb,\n                const ExceptionCallback &ecb) noexcept;\n\n    /**\n     * @brief Asynchronously select the rows that match the given criteria.\n     *\n     * @param criteria The criteria.\n     * @return std::future<std::vector<T>> The future object with which user can\n     * get the result.\n     */\n    std::future<std::vector<T>> findFutureBy(const Criteria &criteria) noexcept;\n\n    /**\n     * @brief Insert a row into the table.\n     *\n     * @param obj The object to be inserted.\n     * @note The auto-increased primary key (if it exists) is set to the obj\n     * argument after the method returns.\n     */\n    void insert(T &obj) noexcept(false);\n\n    /**\n     * @brief Asynchronously insert a row into the table.\n     *\n     * @param obj The object to be inserted.\n     * @param rcb is called with the result (with the auto-increased primary key\n     * (if it exists)).\n     * @param ecb is called when an error occurs.\n     */\n    void insert(const T &obj,\n                const SingleRowCallback &rcb,\n                const ExceptionCallback &ecb) noexcept;\n\n    /**\n     * @brief Asynchronously insert a row into the table.\n     *\n     * @return std::future<T> The future object with which user can get the\n     * result (with the auto-increased primary key (if it exists)).\n     */\n    std::future<T> insertFuture(const T &) noexcept;\n\n    /**\n     * @brief Update a record.\n     *\n     * @param obj The record.\n     * @return size_t The number of updated records. It only could be 0 or 1.\n     * @note The table must have a primary key.\n     */\n    size_t update(const T &obj) noexcept(false);\n\n    /**\n     * @brief Asynchronously update a record.\n     *\n     * @param obj The record.\n     * @param rcb is called with the number of updated records.\n     * @param ecb is called when an error occurs.\n     * @note The table must have a primary key.\n     */\n    void update(const T &obj,\n                const CountCallback &rcb,\n                const ExceptionCallback &ecb) noexcept;\n\n    /**\n     * @brief Asynchronously update a record.\n     *\n     * @param obj The record.\n     * @return std::future<size_t> The future object with which user can get the\n     * number of updated records.\n     * @note The table must have a primary key.\n     */\n    std::future<size_t> updateFuture(const T &obj) noexcept;\n\n    /**\n     * @brief Update a record that match both the primary key and the given\n     * criteria.\n     *\n     * @param colNames Columns to update.\n     * @param criteria The criteria.\n     * @param args New value of target columns.\n     * @return size_t The number of updated records. It only could be 0 or 1.\n     * @note The table must have a primary key.\n     */\n    template <typename... Arguments>\n    size_t updateBy(const std::vector<std::string> &colNames,\n                    const Criteria &criteria,\n                    Arguments &&...args) noexcept(false);\n\n    /**\n     * @brief Asynchronously select the rows that match both the primary key and\n     * the given criteria.\n     *\n     * @param colNames Columns to update.\n     * @param criteria The criteria.\n     * @param rcb is called with the result.\n     * @param ecb is called when an error occurs.\n     * @param args New value of target columns.\n     */\n    template <typename... Arguments>\n    void updateBy(const std::vector<std::string> &colNames,\n                  const CountCallback &rcb,\n                  const ExceptionCallback &ecb,\n                  const Criteria &criteria,\n                  Arguments &&...args) noexcept;\n\n    /**\n     * @brief Asynchronously update a record that match both the primary key and\n     * the given criteria.\n     *\n     * @param colNames Columns to update.\n     * @param criteria The criteria.\n     * @param args New value of target columns.\n     * @return std::future<size_t> The future object with which user can get the\n     * number of updated records.\n     * @note The table must have a primary key.\n     */\n    template <typename... Arguments>\n    std::future<size_t> updateFutureBy(const std::vector<std::string> &colNames,\n                                       const Criteria &criteria,\n                                       Arguments &&...args) noexcept;\n\n    /**\n     * @brief Update some records that match the given criteria.\n     *\n     * @param colNames Columns to increment.\n     * @param criteria The criteria.\n     * @param args Specify the amount by which the columns should be\n     * incremented.\n     * @return size_t The number of updated records.\n     */\n    template <typename... Arguments>\n    size_t increment(const std::vector<std::string> &colNames,\n                     const Criteria &criteria,\n                     Arguments... args) noexcept(false);\n\n    /**\n     * @brief Asynchronously update some records that match the given criteria.\n     *\n     * @param colNames Columns to increment.\n     * @param criteria The criteria.\n     * @param rcb is called with the result.\n     * @param ecb is called when an error occurs.\n     * @param args Specify the amount by which the columns should be\n     * incremented.\n     */\n    template <typename... Arguments>\n    void increment(const std::vector<std::string> &colNames,\n                   const CountCallback &rcb,\n                   const ExceptionCallback &ecb,\n                   const Criteria &criteria,\n                   Arguments... args) noexcept;\n\n    /**\n     * @brief Asynchronously update some records that match the given criteria.\n     *\n     * @param colNames Columns to increment.\n     * @param criteria The criteria.\n     * @param args Specify the amount by which the columns should be\n     * incremented.\n     * @return std::future<size_t> The future object with which user can get the\n     * number of updated records.\n     */\n    template <typename... Arguments>\n    std::future<size_t> incrementFuture(\n        const std::vector<std::string> &colNames,\n        const Criteria &criteria,\n        Arguments... args) noexcept;\n\n    /**\n     * @brief Delete a record from the table.\n     *\n     * @param obj The record.\n     * @return size_t The number of deleted records.\n     * @note The table must have a primary key.\n     */\n    size_t deleteOne(const T &obj) noexcept(false);\n\n    /**\n     * @brief Asynchronously delete a record from the table.\n     *\n     * @param obj The record.\n     * @param rcb is called with the number of deleted records.\n     * @param ecb is called when an error occurs.\n     * @note The table must have a primary key.\n     */\n    void deleteOne(const T &obj,\n                   const CountCallback &rcb,\n                   const ExceptionCallback &ecb) noexcept;\n\n    /**\n     * @brief Asynchronously delete a record from the table.\n     *\n     * @param obj The record.\n     * @return std::future<size_t> The future object with which user can get the\n     * number of deleted records.\n     * @note The table must have a primary key.\n     */\n    std::future<size_t> deleteFutureOne(const T &obj) noexcept;\n\n    /**\n     * @brief Delete records that satisfy the given criteria.\n     *\n     * @param criteria The criteria.\n     * @return size_t The number of deleted records.\n     */\n    size_t deleteBy(const Criteria &criteria) noexcept(false);\n\n    /**\n     * @brief Delete records that match the given criteria asynchronously.\n     *\n     * @param criteria The criteria\n     * @param rcb is called with the number of deleted records.\n     * @param ecb is called when an error occurs.\n     */\n    void deleteBy(const Criteria &criteria,\n                  const CountCallback &rcb,\n                  const ExceptionCallback &ecb) noexcept;\n\n    /**\n     * @brief Delete records that match the given criteria asynchronously.\n     *\n     * @param criteria The criteria\n     * @return std::future<size_t> The future object with which user can get the\n     * number of deleted records\n     */\n    std::future<size_t> deleteFutureBy(const Criteria &criteria) noexcept;\n\n    /**\n     * @brief Delete the record that matches the given primary key.\n     *\n     * @param key The primary key.\n     * @return size_t The number of deleted records (1 or 0).\n     */\n    size_t deleteByPrimaryKey(const TraitsPKType &key) noexcept(false);\n\n    /**\n     * @brief Asynchronously delete the record that matches the given primary\n     * key.\n     *\n     * @param key The primary key.\n     * @param rcb is called with the number of deleted records.\n     * @param ecb is called when an error occurs.\n     */\n    void deleteByPrimaryKey(const TraitsPKType &key,\n                            const CountCallback &rcb,\n                            const ExceptionCallback &ecb) noexcept;\n\n    /**\n     * @brief Delete the record that matches the given primary key\n     * asynchronously.\n     *\n     * @param key The primary key.\n     * @return std::future<size_t> The future object with which user can get the\n     * number of deleted records\n     */\n    std::future<size_t> deleteFutureByPrimaryKey(\n        const TraitsPKType &key) noexcept;\n\n  protected:\n    DbClientPtr client_;\n    size_t limit_{0};\n    size_t offset_{0};\n    std::string orderByString_;\n    bool forUpdate_{false};\n\n    void clear()\n    {\n        limit_ = 0;\n        offset_ = 0;\n        orderByString_.clear();\n        forUpdate_ = false;\n    }\n\n    template <typename PKType = decltype(T::primaryKeyName)>\n    void makePrimaryKeyCriteria(std::string &sql)\n    {\n        if constexpr (std::is_same_v<const std::string, PKType>)\n        {\n            sql += \" where \";\n            sql += T::primaryKeyName;\n            sql += \" = $?\";\n        }\n        else if constexpr (std::is_same_v<const std::vector<std::string>,\n                                          PKType>)\n        {\n            sql += \" where \";\n            for (size_t i = 0; i < T::primaryKeyName.size(); ++i)\n            {\n                sql += T::primaryKeyName[i];\n                sql += \" = $?\";\n                if (i < (T::primaryKeyName.size() - 1))\n                {\n                    sql += \" and \";\n                }\n            }\n        }\n    }\n\n    template <typename PKType = decltype(T::primaryKeyName)>\n    void outputPrimaryKeyToBinder(const TraitsPKType &pk,\n                                  internal::SqlBinder &binder)\n    {\n        if constexpr (std::is_same_v<const std::string, PKType>)\n        {\n            binder << pk;\n        }\n        else if constexpr (std::is_same_v<const std::vector<std::string>,\n                                          PKType>)\n        {\n            tupleToBinder<typename T::PrimaryKeyType>(pk, binder);\n        }\n    }\n\n    template <typename TP, ssize_t N = std::tuple_size<TP>::value>\n    void tupleToBinder(const TP &t, internal::SqlBinder &binder)\n    {\n        if constexpr (N > 1)\n        {\n            tupleToBinder<TP, N - 1>(t, binder);\n            binder << std::get<N - 1>(t);\n        }\n        else if constexpr (N == 1)\n        {\n            binder << std::get<0>(t);\n        }\n    }\n\n    std::string replaceSqlPlaceHolder(const std::string &sqlStr,\n                                      const std::string &holderStr) const;\n};\n\ntemplate <typename T>\ninline T Mapper<T>::findOne(const Criteria &criteria) noexcept(false)\n{\n    std::string sql = \"select * from \";\n    sql += T::tableName;\n    bool hasParameters = false;\n    if (criteria)\n    {\n        sql += \" where \";\n        sql += criteria.criteriaString();\n        hasParameters = true;\n    }\n    sql.append(orderByString_);\n    if (limit_ > 0)\n    {\n        hasParameters = true;\n        sql.append(\" limit $?\");\n    }\n    if (offset_ > 0)\n    {\n        hasParameters = true;\n        sql.append(\" offset $?\");\n    }\n    if (hasParameters)\n        sql = replaceSqlPlaceHolder(sql, \"$?\");\n    if (forUpdate_)\n    {\n        sql += \" for update\";\n    }\n    Result r(nullptr);\n    {\n        auto binder = *client_ << std::move(sql);\n        if (criteria)\n            criteria.outputArgs(binder);\n        if (limit_ > 0)\n            binder << limit_;\n        if (offset_)\n            binder << offset_;\n        clear();\n        binder << Mode::Blocking;\n        binder >> [&r](const Result &result) { r = result; };\n        binder.exec();  // exec may be throw exception;\n    }\n    if (r.size() == 0)\n    {\n        throw UnexpectedRows(\"0 rows found\");\n    }\n    else if (r.size() > 1)\n    {\n        throw UnexpectedRows(\"Found more than one row\");\n    }\n    auto row = r[0];\n    return T(row);\n}\n\ntemplate <typename T>\ninline void Mapper<T>::findOne(const Criteria &criteria,\n                               const SingleRowCallback &rcb,\n                               const ExceptionCallback &ecb) noexcept\n{\n    std::string sql = \"select * from \";\n    sql += T::tableName;\n    bool hasParameters = false;\n    if (criteria)\n    {\n        sql += \" where \";\n        sql += criteria.criteriaString();\n        hasParameters = true;\n    }\n    sql.append(orderByString_);\n    if (limit_ > 0)\n    {\n        hasParameters = true;\n        sql.append(\" limit $?\");\n    }\n    if (offset_ > 0)\n    {\n        hasParameters = true;\n        sql.append(\" offset $?\");\n    }\n    if (hasParameters)\n        sql = replaceSqlPlaceHolder(sql, \"$?\");\n    if (forUpdate_)\n    {\n        sql += \" for update\";\n    }\n    auto binder = *client_ << std::move(sql);\n    if (criteria)\n        criteria.outputArgs(binder);\n    if (limit_ > 0)\n        binder << limit_;\n    if (offset_)\n        binder << offset_;\n    clear();\n    binder >> [ecb, rcb](const Result &r) {\n        if (r.size() == 0)\n        {\n            ecb(UnexpectedRows(\"0 rows found\"));\n        }\n        else if (r.size() > 1)\n        {\n            ecb(UnexpectedRows(\"Found more than one row\"));\n        }\n        else\n        {\n            rcb(T(r[0]));\n        }\n    };\n    binder >> ecb;\n}\n\ntemplate <typename T>\ninline std::future<T> Mapper<T>::findFutureOne(\n    const Criteria &criteria) noexcept\n{\n    std::string sql = \"select * from \";\n    sql += T::tableName;\n    bool hasParameters = false;\n    if (criteria)\n    {\n        sql += \" where \";\n        sql += criteria.criteriaString();\n        hasParameters = true;\n    }\n    sql.append(orderByString_);\n    if (limit_ > 0)\n    {\n        hasParameters = true;\n        sql.append(\" limit $?\");\n    }\n    if (offset_ > 0)\n    {\n        hasParameters = true;\n        sql.append(\" offset $?\");\n    }\n    if (hasParameters)\n        sql = replaceSqlPlaceHolder(sql, \"$?\");\n    if (forUpdate_)\n    {\n        sql += \" for update\";\n    }\n    auto binder = *client_ << std::move(sql);\n    if (criteria)\n        criteria.outputArgs(binder);\n    if (limit_ > 0)\n        binder << limit_;\n    if (offset_)\n        binder << offset_;\n    clear();\n    std::shared_ptr<std::promise<T>> prom = std::make_shared<std::promise<T>>();\n    binder >> [prom](const Result &r) {\n        if (r.size() == 0)\n        {\n            prom->set_exception(\n                std::make_exception_ptr(UnexpectedRows(\"0 rows found\")));\n        }\n        else if (r.size() > 1)\n        {\n            prom->set_exception(std::make_exception_ptr(\n                UnexpectedRows(\"Found more than one row\")));\n        }\n        else\n        {\n            prom->set_value(T(r[0]));\n        }\n    };\n    binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };\n    binder.exec();\n    return prom->get_future();\n}\n\ntemplate <typename T>\ninline std::vector<T> Mapper<T>::findBy(const Criteria &criteria) noexcept(\n    false)\n{\n    std::string sql = \"select * from \";\n    sql += T::tableName;\n    bool hasParameters = false;\n    if (criteria)\n    {\n        hasParameters = true;\n        sql += \" where \";\n        sql += criteria.criteriaString();\n    }\n    sql.append(orderByString_);\n    if (limit_ > 0)\n    {\n        hasParameters = true;\n        sql.append(\" limit $?\");\n    }\n    if (offset_ > 0)\n    {\n        hasParameters = true;\n        sql.append(\" offset $?\");\n    }\n    if (hasParameters)\n        sql = replaceSqlPlaceHolder(sql, \"$?\");\n    if (forUpdate_)\n    {\n        sql += \" for update\";\n    }\n    Result r(nullptr);\n    {\n        auto binder = *client_ << std::move(sql);\n        if (criteria)\n            criteria.outputArgs(binder);\n        if (limit_ > 0)\n            binder << limit_;\n        if (offset_)\n            binder << offset_;\n        clear();\n        binder << Mode::Blocking;\n        binder >> [&r](const Result &result) { r = result; };\n        binder.exec();  // exec may be throw exception;\n    }\n    std::vector<T> ret;\n    for (auto const &row : r)\n    {\n        ret.push_back(T(row));\n    }\n    return ret;\n}\n\ntemplate <typename T>\ninline void Mapper<T>::findBy(const Criteria &criteria,\n                              const MultipleRowsCallback &rcb,\n                              const ExceptionCallback &ecb) noexcept\n{\n    std::string sql = \"select * from \";\n    sql += T::tableName;\n    bool hasParameters = false;\n    if (criteria)\n    {\n        hasParameters = true;\n        sql += \" where \";\n        sql += criteria.criteriaString();\n    }\n    sql.append(orderByString_);\n    if (limit_ > 0)\n    {\n        hasParameters = true;\n        sql.append(\" limit $?\");\n    }\n    if (offset_ > 0)\n    {\n        hasParameters = true;\n        sql.append(\" offset $?\");\n    }\n    if (hasParameters)\n        sql = replaceSqlPlaceHolder(sql, \"$?\");\n    if (forUpdate_)\n    {\n        sql += \" for update\";\n    }\n    auto binder = *client_ << std::move(sql);\n    if (criteria)\n        criteria.outputArgs(binder);\n    if (limit_ > 0)\n        binder << limit_;\n    if (offset_)\n        binder << offset_;\n    clear();\n    binder >> [rcb](const Result &r) {\n        std::vector<T> ret;\n        for (auto const &row : r)\n        {\n            ret.emplace_back(row);\n        }\n        rcb(std::move(ret));\n    };\n    binder >> ecb;\n}\n\ntemplate <typename T>\ninline std::future<std::vector<T>> Mapper<T>::findFutureBy(\n    const Criteria &criteria) noexcept\n{\n    std::string sql = \"select * from \";\n    sql += T::tableName;\n    bool hasParameters = false;\n    if (criteria)\n    {\n        hasParameters = true;\n        sql += \" where \";\n        sql += criteria.criteriaString();\n    }\n    sql.append(orderByString_);\n    if (limit_ > 0)\n    {\n        hasParameters = true;\n        sql.append(\" limit $?\");\n    }\n    if (offset_ > 0)\n    {\n        hasParameters = true;\n        sql.append(\" offset $?\");\n    }\n    if (hasParameters)\n        sql = replaceSqlPlaceHolder(sql, \"$?\");\n    if (forUpdate_)\n    {\n        sql += \" for update\";\n    }\n    auto binder = *client_ << std::move(sql);\n    if (criteria)\n        criteria.outputArgs(binder);\n    if (limit_ > 0)\n        binder << limit_;\n    if (offset_)\n        binder << offset_;\n    clear();\n    std::shared_ptr<std::promise<std::vector<T>>> prom =\n        std::make_shared<std::promise<std::vector<T>>>();\n    binder >> [prom](const Result &r) {\n        std::vector<T> ret;\n        for (auto const &row : r)\n        {\n            ret.push_back(T(row));\n        }\n        prom->set_value(ret);\n    };\n    binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };\n    binder.exec();\n    return prom->get_future();\n}\n\ntemplate <typename T>\ninline std::vector<T> Mapper<T>::findAll() noexcept(false)\n{\n    return findBy(Criteria());\n}\n\ntemplate <typename T>\ninline void Mapper<T>::findAll(const MultipleRowsCallback &rcb,\n                               const ExceptionCallback &ecb) noexcept\n{\n    findBy(Criteria(), rcb, ecb);\n}\n\ntemplate <typename T>\ninline std::future<std::vector<T>> Mapper<T>::findFutureAll() noexcept\n{\n    return findFutureBy(Criteria());\n}\n\ntemplate <typename T>\ninline size_t Mapper<T>::count(const Criteria &criteria) noexcept(false)\n{\n    std::string sql = \"select count(*) from \";\n    sql += T::tableName;\n    if (criteria)\n    {\n        sql += \" where \";\n        sql += criteria.criteriaString();\n        sql = replaceSqlPlaceHolder(sql, \"$?\");\n    }\n    clear();\n    Result r(nullptr);\n    {\n        auto binder = *client_ << std::move(sql);\n        if (criteria)\n            criteria.outputArgs(binder);\n        binder << Mode::Blocking;\n        binder >> [&r](const Result &result) { r = result; };\n        binder.exec();  // exec may be throw exception;\n    }\n    assert(r.size() == 1);\n    return r[0][(Row::SizeType)0].as<size_t>();\n}\n\ntemplate <typename T>\ninline void Mapper<T>::count(const Criteria &criteria,\n                             const CountCallback &rcb,\n                             const ExceptionCallback &ecb) noexcept\n{\n    std::string sql = \"select count(*) from \";\n    sql += T::tableName;\n    if (criteria)\n    {\n        sql += \" where \";\n        sql += criteria.criteriaString();\n        sql = replaceSqlPlaceHolder(sql, \"$?\");\n    }\n    clear();\n    auto binder = *client_ << std::move(sql);\n    if (criteria)\n        criteria.outputArgs(binder);\n    binder >> [rcb](const Result &r) {\n        assert(r.size() == 1);\n        rcb(r[0][(Row::SizeType)0].as<size_t>());\n    };\n    binder >> ecb;\n}\n\ntemplate <typename T>\ninline std::future<size_t> Mapper<T>::countFuture(\n    const Criteria &criteria) noexcept\n{\n    std::string sql = \"select count(*) from \";\n    sql += T::tableName;\n    if (criteria)\n    {\n        sql += \" where \";\n        sql += criteria.criteriaString();\n        sql = replaceSqlPlaceHolder(sql, \"$?\");\n    }\n    clear();\n    auto binder = *client_ << std::move(sql);\n    if (criteria)\n        criteria.outputArgs(binder);\n\n    std::shared_ptr<std::promise<size_t>> prom =\n        std::make_shared<std::promise<size_t>>();\n    binder >> [prom](const Result &r) {\n        assert(r.size() == 1);\n        prom->set_value(r[0][(Row::SizeType)0].as<size_t>());\n    };\n    binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };\n    binder.exec();\n    return prom->get_future();\n}\n\ntemplate <typename T>\ninline void Mapper<T>::insert(T &obj) noexcept(false)\n{\n    clear();\n    Result r(nullptr);\n    bool needSelection = false;\n    {\n        auto binder = *client_ << obj.sqlForInserting(needSelection);\n        obj.outputArgs(binder);\n        binder << Mode::Blocking;\n        binder >> [&r](const Result &result) { r = result; };\n        binder.exec();  // Maybe throw exception;\n    }\n    assert(r.affectedRows() == 1);\n    if (client_->type() == ClientType::PostgreSQL)\n    {\n        if (needSelection)\n        {\n            assert(r.size() == 1);\n            obj = T(r[0]);\n        }\n    }\n    else  // Mysql or Sqlite3\n    {\n        auto id = r.insertId();\n        obj.updateId(id);\n        if (needSelection)\n        {\n            obj = findByPrimaryKey(obj.getPrimaryKey());\n        }\n    }\n}\n\ntemplate <typename T>\ninline void Mapper<T>::insert(const T &obj,\n                              const SingleRowCallback &rcb,\n                              const ExceptionCallback &ecb) noexcept\n{\n    clear();\n    bool needSelection = false;\n    auto binder = *client_ << obj.sqlForInserting(needSelection);\n    obj.outputArgs(binder);\n    auto client = client_;\n    binder >> [client, rcb, obj, needSelection, ecb](const Result &r) {\n        assert(r.affectedRows() == 1);\n        if (client->type() == ClientType::PostgreSQL)\n        {\n            if (needSelection)\n            {\n                assert(r.size() == 1);\n                rcb(T(r[0]));\n            }\n            else\n            {\n                rcb(obj);\n            }\n        }\n        else  // Mysql or Sqlite3\n        {\n            auto id = r.insertId();\n            auto newObj = obj;\n            newObj.updateId(id);\n            if (needSelection)\n            {\n                auto tmp = Mapper<T>(client);\n                tmp.findByPrimaryKey(newObj.getPrimaryKey(), rcb, ecb);\n            }\n            else\n            {\n                rcb(newObj);\n            }\n        }\n    };\n    binder >> ecb;\n}\n\ntemplate <typename T>\ninline std::future<T> Mapper<T>::insertFuture(const T &obj) noexcept\n{\n    clear();\n    bool needSelection = false;\n    auto binder = *client_ << obj.sqlForInserting(needSelection);\n    obj.outputArgs(binder);\n\n    std::shared_ptr<std::promise<T>> prom = std::make_shared<std::promise<T>>();\n    auto client = client_;\n    binder >> [client, prom, obj, needSelection](const Result &r) {\n        assert(r.affectedRows() == 1);\n        if (client->type() == ClientType::PostgreSQL)\n        {\n            if (needSelection)\n            {\n                assert(r.size() == 1);\n                prom->set_value(T(r[0]));\n            }\n            else\n            {\n                prom->set_value(obj);\n            }\n        }\n        else  // Mysql or Sqlite3\n        {\n            auto id = r.insertId();\n            auto newObj = obj;\n            newObj.updateId(id);\n            if (needSelection)\n            {\n                auto tmp = Mapper<T>(client);\n                tmp.findByPrimaryKey(\n                    newObj.getPrimaryKey(),\n                    [prom](T selObj) { prom->set_value(selObj); },\n                    [prom](const DrogonDbException &e) {\n                        prom->set_exception(\n                            std::make_exception_ptr(Failure(e.base().what())));\n                    });\n            }\n            else\n            {\n                prom->set_value(newObj);\n            }\n        }\n    };\n    binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };\n    binder.exec();\n    return prom->get_future();\n}\n\ntemplate <typename T>\ninline size_t Mapper<T>::update(const T &obj) noexcept(false)\n{\n    clear();\n    static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                  \"No primary key in the table!\");\n    std::vector<std::string> colNames = obj.updateColumns();\n    if (colNames.empty())\n    {\n        return 0;\n    }\n    std::string sql = \"update \";\n    sql += T::tableName;\n    sql += \" set \";\n    for (auto const &colName : colNames)\n    {\n        sql += colName;\n        sql += \" = $?,\";\n    }\n    sql[sql.length() - 1] = ' ';  // Replace the last ','\n\n    makePrimaryKeyCriteria(sql);\n\n    sql = replaceSqlPlaceHolder(sql, \"$?\");\n    Result r(nullptr);\n    {\n        auto binder = *client_ << std::move(sql);\n        obj.updateArgs(binder);\n        outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);\n        binder << Mode::Blocking;\n        binder >> [&r](const Result &result) { r = result; };\n        binder.exec();  // Maybe throw exception;\n    }\n    return r.affectedRows();\n}\n\ntemplate <typename T>\ntemplate <typename... Arguments>\nsize_t Mapper<T>::updateBy(const std::vector<std::string> &colNames,\n                           const Criteria &criteria,\n                           Arguments &&...args) noexcept(false)\n{\n    static_assert(sizeof...(args) > 0);\n    assert(colNames.size() == sizeof...(args));\n    clear();\n    std::string sql = \"update \";\n    sql += T::tableName;\n    sql += \" set \";\n    for (auto const &colName : colNames)\n    {\n        sql += colName;\n        sql += \" = $?,\";\n    }\n    sql[sql.length() - 1] = ' ';  // Replace the last ','\n\n    if (criteria)\n    {\n        sql += \" where \";\n        sql += criteria.criteriaString();\n    }\n\n    sql = replaceSqlPlaceHolder(sql, \"$?\");\n    Result r(nullptr);\n    {\n        auto binder = *client_ << std::move(sql);\n        (void)std::initializer_list<int>{\n            (binder << std::forward<Arguments>(args), 0)...};\n        if (criteria)\n            criteria.outputArgs(binder);\n        binder << Mode::Blocking;\n        binder >> [&r](const Result &result) { r = result; };\n        binder.exec();  // Maybe throw exception;\n    }\n    return r.affectedRows();\n}\n\ntemplate <typename T>\ninline void Mapper<T>::update(const T &obj,\n                              const CountCallback &rcb,\n                              const ExceptionCallback &ecb) noexcept\n{\n    clear();\n    static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                  \"No primary key in the table!\");\n\n    std::vector<std::string> colNames = obj.updateColumns();\n    if (colNames.empty())\n    {\n        rcb(0);\n        return;\n    }\n    std::string sql = \"update \";\n    sql += T::tableName;\n    sql += \" set \";\n    for (auto const &colName : colNames)\n    {\n        sql += colName;\n        sql += \" = $?,\";\n    }\n    sql[sql.length() - 1] = ' ';  // Replace the last ','\n\n    makePrimaryKeyCriteria(sql);\n\n    sql = replaceSqlPlaceHolder(sql, \"$?\");\n    auto binder = *client_ << std::move(sql);\n    obj.updateArgs(binder);\n    outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);\n    binder >> [rcb](const Result &r) { rcb(r.affectedRows()); };\n    binder >> ecb;\n}\n\ntemplate <typename T>\ntemplate <typename... Arguments>\nvoid Mapper<T>::updateBy(const std::vector<std::string> &colNames,\n                         const CountCallback &rcb,\n                         const ExceptionCallback &ecb,\n                         const Criteria &criteria,\n                         Arguments &&...args) noexcept\n{\n    static_assert(sizeof...(args) > 0);\n    assert(colNames.size() == sizeof...(args));\n    clear();\n    std::string sql = \"update \";\n    sql += T::tableName;\n    sql += \" set \";\n    for (auto const &colName : colNames)\n    {\n        sql += colName;\n        sql += \" = $?,\";\n    }\n    sql[sql.length() - 1] = ' ';  // Replace the last ','\n\n    if (criteria)\n    {\n        sql += \" where \";\n        sql += criteria.criteriaString();\n    }\n\n    sql = replaceSqlPlaceHolder(sql, \"$?\");\n    auto binder = *client_ << std::move(sql);\n    (void)std::initializer_list<int>{\n        (binder << std::forward<Arguments>(args), 0)...};\n    if (criteria)\n        criteria.outputArgs(binder);\n    binder >> [rcb](const Result &r) { rcb(r.affectedRows()); };\n    binder >> ecb;\n}\n\ntemplate <typename T>\ninline std::future<size_t> Mapper<T>::updateFuture(const T &obj) noexcept\n{\n    clear();\n    static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                  \"No primary key in the table!\");\n    std::vector<std::string> colNames = obj.updateColumns();\n    if (colNames.empty())\n    {\n        std::shared_ptr<std::promise<size_t>> prom =\n            std::make_shared<std::promise<size_t>>();\n        prom->set_value(0);\n        return prom->get_future();\n    }\n    std::string sql = \"update \";\n    sql += T::tableName;\n    sql += \" set \";\n    for (auto const &colName : colNames)\n    {\n        sql += colName;\n        sql += \" = $?,\";\n    }\n    sql[sql.length() - 1] = ' ';  // Replace the last ','\n\n    makePrimaryKeyCriteria(sql);\n\n    sql = replaceSqlPlaceHolder(sql, \"$?\");\n    auto binder = *client_ << std::move(sql);\n    obj.updateArgs(binder);\n    outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);\n\n    std::shared_ptr<std::promise<size_t>> prom =\n        std::make_shared<std::promise<size_t>>();\n    binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };\n    binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };\n    binder.exec();\n    return prom->get_future();\n}\n\ntemplate <typename T>\ntemplate <typename... Arguments>\ninline std::future<size_t> Mapper<T>::updateFutureBy(\n    const std::vector<std::string> &colNames,\n    const Criteria &criteria,\n    Arguments &&...args) noexcept\n{\n    static_assert(sizeof...(args) > 0);\n    assert(colNames.size() == sizeof...(args));\n    clear();\n    std::string sql = \"update \";\n    sql += T::tableName;\n    sql += \" set \";\n    for (auto const &colName : colNames)\n    {\n        sql += colName;\n        sql += \" = $?,\";\n    }\n    sql[sql.length() - 1] = ' ';  // Replace the last ','\n\n    if (criteria)\n    {\n        sql += \" where \";\n        sql += criteria.criteriaString();\n    }\n\n    sql = replaceSqlPlaceHolder(sql, \"$?\");\n    auto binder = *client_ << std::move(sql);\n    (void)std::initializer_list<int>{\n        (binder << std::forward<Arguments>(args), 0)...};\n    if (criteria)\n        criteria.outputArgs(binder);\n\n    std::shared_ptr<std::promise<size_t>> prom =\n        std::make_shared<std::promise<size_t>>();\n    binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };\n    binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };\n    binder.exec();\n    return prom->get_future();\n}\n\ntemplate <typename T>\ntemplate <typename... Arguments>\ninline size_t Mapper<T>::increment(const std::vector<std::string> &colNames,\n                                   const Criteria &criteria,\n                                   Arguments... args) noexcept(false)\n{\n    static_assert(sizeof...(args) > 0);\n    assert(colNames.size() == sizeof...(args));\n    clear();\n    std::string sql = \"update \";\n    sql += T::tableName;\n    sql += \" set \";\n\n    std::vector<const char *> temps;\n    (void)std::initializer_list<int>{(\n        [&args, &temps] {\n            args = (args < 0) ? (temps.push_back(\" - $?,\"), -args)\n                              : (temps.push_back(\" + $?,\"), args);\n        }(),\n        0)...};\n\n    for (int i = 0; i < sizeof...(args); ++i)\n    {\n        const auto &colName = colNames[i];\n        sql += colName;\n        sql += \" = \";\n        sql += colName;\n        sql += temps[i];\n    }\n    sql[sql.length() - 1] = ' ';  // Replace the last ','\n\n    if (criteria)\n    {\n        sql += \" where \";\n        sql += criteria.criteriaString();\n    }\n\n    sql = replaceSqlPlaceHolder(sql, \"$?\");\n    Result r(nullptr);\n    {\n        auto binder = *client_ << std::move(sql);\n        (void)std::initializer_list<int>{\n            (binder << std::forward<Arguments>(args), 0)...};\n        if (criteria)\n            criteria.outputArgs(binder);\n        binder << Mode::Blocking;\n        binder >> [&r](const Result &result) { r = result; };\n        binder.exec();  // Maybe throw exception;\n    }\n    return r.affectedRows();\n}\n\ntemplate <typename T>\ntemplate <typename... Arguments>\ninline void Mapper<T>::increment(const std::vector<std::string> &colNames,\n                                 const CountCallback &rcb,\n                                 const ExceptionCallback &ecb,\n                                 const Criteria &criteria,\n                                 Arguments... args) noexcept\n{\n    static_assert(sizeof...(args) > 0);\n    assert(colNames.size() == sizeof...(args));\n    clear();\n    std::string sql = \"update \";\n    sql += T::tableName;\n    sql += \" set \";\n\n    std::vector<const char *> temps;\n    (void)std::initializer_list<int>{(\n        [&args, &temps] {\n            args = args < 0 ? (temps.push_back(\" - $?,\"), -args)\n                            : (temps.push_back(\" + $?,\"), args);\n        }(),\n        0)...};\n\n    for (int i = 0; i < sizeof...(args); ++i)\n    {\n        const auto &colName = colNames[i];\n        sql += colName;\n        sql += \" = \";\n        sql += colName;\n        sql += temps[i];\n    }\n    sql[sql.length() - 1] = ' ';  // Replace the last ','\n\n    if (criteria)\n    {\n        sql += \" where \";\n        sql += criteria.criteriaString();\n    }\n\n    sql = replaceSqlPlaceHolder(sql, \"$?\");\n    auto binder = *client_ << std::move(sql);\n    (void)std::initializer_list<int>{\n        (binder << std::forward<Arguments>(args), 0)...};\n    if (criteria)\n        criteria.outputArgs(binder);\n    binder >> [rcb](const Result &r) { rcb(r.affectedRows()); };\n    binder >> ecb;\n}\n\ntemplate <typename T>\ntemplate <typename... Arguments>\ninline std::future<size_t> Mapper<T>::incrementFuture(\n    const std::vector<std::string> &colNames,\n    const Criteria &criteria,\n    Arguments... args) noexcept\n{\n    static_assert(sizeof...(args) > 0);\n    assert(colNames.size() == sizeof...(args));\n    clear();\n    std::string sql = \"update \";\n    sql += T::tableName;\n    sql += \" set \";\n\n    std::vector<const char *> temps;\n    (void)std::initializer_list<int>{(\n        [&args, &temps] {\n            args = args < 0 ? (temps.push_back(\" - $?,\"), -args)\n                            : (temps.push_back(\" + $?,\"), args);\n        }(),\n        0)...};\n\n    for (int i = 0; i < sizeof...(args); ++i)\n    {\n        const auto &colName = colNames[i];\n        sql += colName;\n        sql += \" = \";\n        sql += colName;\n        sql += temps[i];\n    }\n    sql[sql.length() - 1] = ' ';  // Replace the last ','\n\n    if (criteria)\n    {\n        sql += \" where \";\n        sql += criteria.criteriaString();\n    }\n\n    sql = replaceSqlPlaceHolder(sql, \"$?\");\n    auto binder = *client_ << std::move(sql);\n    (void)std::initializer_list<int>{\n        (binder << std::forward<Arguments>(args), 0)...};\n    if (criteria)\n        criteria.outputArgs(binder);\n\n    std::shared_ptr<std::promise<size_t>> prom =\n        std::make_shared<std::promise<size_t>>();\n    binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };\n    binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };\n    binder.exec();\n    return prom->get_future();\n}\n\ntemplate <typename T>\ninline size_t Mapper<T>::deleteOne(const T &obj) noexcept(false)\n{\n    clear();\n    static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                  \"No primary key in the table!\");\n    std::string sql = \"delete from \";\n    sql += T::tableName;\n\n    sql += \" \";  // Replace the last ','\n\n    makePrimaryKeyCriteria(sql);\n\n    sql = replaceSqlPlaceHolder(sql, \"$?\");\n    Result r(nullptr);\n    {\n        auto binder = *client_ << std::move(sql);\n        outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);\n        binder << Mode::Blocking;\n        binder >> [&r](const Result &result) { r = result; };\n        binder.exec();  // Maybe throw exception;\n    }\n    return r.affectedRows();\n}\n\ntemplate <typename T>\ninline void Mapper<T>::deleteOne(const T &obj,\n                                 const CountCallback &rcb,\n                                 const ExceptionCallback &ecb) noexcept\n{\n    clear();\n    static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                  \"No primary key in the table!\");\n    std::string sql = \"delete from \";\n    sql += T::tableName;\n    sql += \" \";\n\n    makePrimaryKeyCriteria(sql);\n\n    sql = replaceSqlPlaceHolder(sql, \"$?\");\n    auto binder = *client_ << std::move(sql);\n    outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);\n    binder >> [rcb](const Result &r) { rcb(r.affectedRows()); };\n    binder >> ecb;\n}\n\ntemplate <typename T>\ninline std::future<size_t> Mapper<T>::deleteFutureOne(const T &obj) noexcept\n{\n    clear();\n    static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                  \"No primary key in the table!\");\n    std::string sql = \"delete from \";\n    sql += T::tableName;\n    sql += \" \";\n\n    makePrimaryKeyCriteria(sql);\n\n    sql = replaceSqlPlaceHolder(sql, \"$?\");\n    auto binder = *client_ << std::move(sql);\n    outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);\n\n    std::shared_ptr<std::promise<size_t>> prom =\n        std::make_shared<std::promise<size_t>>();\n    binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };\n    binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };\n    binder.exec();\n    return prom->get_future();\n}\n\ntemplate <typename T>\ninline size_t Mapper<T>::deleteBy(const Criteria &criteria) noexcept(false)\n{\n    clear();\n    static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                  \"No primary key in the table!\");\n    std::string sql = \"delete from \";\n    sql += T::tableName;\n\n    if (criteria)\n    {\n        sql += \" where \";\n        sql += criteria.criteriaString();\n        sql = replaceSqlPlaceHolder(sql, \"$?\");\n    }\n\n    Result r(nullptr);\n    {\n        auto binder = *client_ << std::move(sql);\n        if (criteria)\n        {\n            criteria.outputArgs(binder);\n        }\n        binder << Mode::Blocking;\n        binder >> [&r](const Result &result) { r = result; };\n        binder.exec();  // Maybe throw exception;\n    }\n    return r.affectedRows();\n}\n\ntemplate <typename T>\ninline void Mapper<T>::deleteBy(const Criteria &criteria,\n                                const CountCallback &rcb,\n                                const ExceptionCallback &ecb) noexcept\n{\n    clear();\n    static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                  \"No primary key in the table!\");\n    std::string sql = \"delete from \";\n    sql += T::tableName;\n\n    if (criteria)\n    {\n        sql += \" where \";\n        sql += criteria.criteriaString();\n        sql = replaceSqlPlaceHolder(sql, \"$?\");\n    }\n\n    auto binder = *client_ << std::move(sql);\n    if (criteria)\n    {\n        criteria.outputArgs(binder);\n    }\n    binder >> [rcb](const Result &r) { rcb(r.affectedRows()); };\n    binder >> ecb;\n}\n\ntemplate <typename T>\ninline std::future<size_t> Mapper<T>::deleteFutureBy(\n    const Criteria &criteria) noexcept\n{\n    clear();\n    static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                  \"No primary key in the table!\");\n    std::string sql = \"delete from \";\n    sql += T::tableName;\n    if (criteria)\n    {\n        sql += \" where \";\n        sql += criteria.criteriaString();\n        sql = replaceSqlPlaceHolder(sql, \"$?\");\n    }\n    auto binder = *client_ << std::move(sql);\n    if (criteria)\n    {\n        criteria.outputArgs(binder);\n    }\n\n    std::shared_ptr<std::promise<size_t>> prom =\n        std::make_shared<std::promise<size_t>>();\n    binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };\n    binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };\n    binder.exec();\n    return prom->get_future();\n}\n\ntemplate <typename T>\ninline Mapper<T> &Mapper<T>::limit(size_t limit)\n{\n    assert(limit > 0);\n    limit_ = limit;\n    return *this;\n}\n\ntemplate <typename T>\ninline Mapper<T> &Mapper<T>::offset(size_t offset)\n{\n    offset_ = offset;\n    return *this;\n}\n\ntemplate <typename T>\ninline Mapper<T> &Mapper<T>::orderBy(const std::string &colName,\n                                     const SortOrder &order)\n{\n    if (orderByString_.empty())\n    {\n        orderByString_ =\n            utils::formattedString(\" order by %s\", colName.c_str());\n        if (order == SortOrder::DESC)\n        {\n            orderByString_ += \" desc\";\n        }\n    }\n    else\n    {\n        orderByString_ += \",\";\n        orderByString_ += colName;\n        if (order == SortOrder::DESC)\n        {\n            orderByString_ += \" desc\";\n        }\n    }\n    return *this;\n}\n\ntemplate <typename T>\ninline Mapper<T> &Mapper<T>::orderBy(size_t colIndex, const SortOrder &order)\n{\n    std::string colName = T::getColumnName(colIndex);\n    assert(!colName.empty());\n    return orderBy(colName, order);\n}\n\ntemplate <typename T>\ninline Mapper<T> &Mapper<T>::paginate(size_t page, size_t perPage)\n{\n    assert(page > 0 && perPage > 0);\n    return limit(perPage).offset((page - 1) * perPage);\n}\n\ntemplate <typename T>\ninline Mapper<T> &Mapper<T>::forUpdate()\n{\n    forUpdate_ = true;\n    return *this;\n}\n\ntemplate <typename T>\ninline std::string Mapper<T>::replaceSqlPlaceHolder(\n    const std::string &sqlStr,\n    const std::string &holderStr) const\n{\n    if (client_->type() == ClientType::PostgreSQL)\n    {\n        std::string::size_type startPos = 0;\n        std::string::size_type pos;\n        std::stringstream ret;\n        size_t phCount = 1;\n        do\n        {\n            pos = sqlStr.find(holderStr, startPos);\n            if (pos == std::string::npos)\n            {\n                ret << sqlStr.substr(startPos);\n                return ret.str();\n            }\n            ret << sqlStr.substr(startPos, pos - startPos);\n            ret << \"$\";\n            ret << phCount++;\n            startPos = pos + holderStr.length();\n        } while (1);\n    }\n    else if (client_->type() == ClientType::Mysql ||\n             client_->type() == ClientType::Sqlite3)\n    {\n        std::string::size_type startPos = 0;\n        std::string::size_type pos;\n        std::stringstream ret;\n        do\n        {\n            pos = sqlStr.find(holderStr, startPos);\n            if (pos == std::string::npos)\n            {\n                ret << sqlStr.substr(startPos);\n                return ret.str();\n            }\n            ret << sqlStr.substr(startPos, pos - startPos);\n            ret << \"?\";\n            startPos = pos + holderStr.length();\n        } while (1);\n    }\n    else\n    {\n        return sqlStr;\n    }\n}\n\ntemplate <typename T>\ninline size_t Mapper<T>::deleteByPrimaryKey(\n    const typename Mapper<T>::TraitsPKType &key) noexcept(false)\n{\n    static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                  \"No primary key in the table!\");\n    static_assert(internal::has_sqlForDeletingByPrimaryKey<T>::value,\n                  \"No function member named sqlForDeletingByPrimaryKey, please \"\n                  \"make sure that the model class is generated by the latest \"\n                  \"version of drogon_ctl\");\n    clear();\n    Result r(nullptr);\n    {\n        auto binder = *client_ << T::sqlForDeletingByPrimaryKey();\n        outputPrimaryKeyToBinder(key, binder);\n        binder << Mode::Blocking;\n        binder >> [&r](const Result &result) { r = result; };\n        binder.exec();  // exec may be throw exception;\n    }\n    return r.affectedRows();\n}\n\ntemplate <typename T>\ninline void Mapper<T>::deleteByPrimaryKey(\n    const typename Mapper<T>::TraitsPKType &key,\n    const CountCallback &rcb,\n    const ExceptionCallback &ecb) noexcept\n{\n    static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                  \"No primary key in the table!\");\n    static_assert(internal::has_sqlForDeletingByPrimaryKey<T>::value,\n                  \"No function member named sqlForDeletingByPrimaryKey, please \"\n                  \"make sure that the model class is generated by the latest \"\n                  \"version of drogon_ctl\");\n    clear();\n    auto binder = *client_ << T::sqlForDeletingByPrimaryKey();\n    outputPrimaryKeyToBinder(key, binder);\n    binder >>\n        [rcb = std::move(rcb)](const Result &r) { rcb(r.affectedRows()); };\n    binder >> ecb;\n}\n\ntemplate <typename T>\ninline std::future<size_t> Mapper<T>::deleteFutureByPrimaryKey(\n    const typename Mapper<T>::TraitsPKType &key) noexcept\n{\n    static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,\n                  \"No primary key in the table!\");\n    static_assert(internal::has_sqlForDeletingByPrimaryKey<T>::value,\n                  \"No function member named sqlForDeletingByPrimaryKey, please \"\n                  \"make sure that the model class is generated by the latest \"\n                  \"version of drogon_ctl\");\n    clear();\n    auto binder = *client_ << T::sqlForDeletingByPrimaryKey();\n    outputPrimaryKeyToBinder(key, binder);\n\n    std::shared_ptr<std::promise<size_t>> prom =\n        std::make_shared<std::promise<size_t>>();\n    binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };\n    binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };\n    binder.exec();\n    return prom->get_future();\n}\n\n/**\n * @brief Convert std::vector<Value> where Value can be JSON to JSON.\n *\n * @param container The container to be converted.\n * @return Json::Value The JSON value converted.\n */\ntemplate <typename Value>\ninline Json::Value toJson(const std::vector<Value> &container)\n{\n    Json::Value values(Json::arrayValue);\n    for (const Value &c : container)\n    {\n        values.append(c.toJson());\n    }\n    return values;\n}\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/QueryBuilder.h",
    "content": "/**\n *\n *  @file QueryBuilder.h\n *  @author Ken Matsui\n *\n *  Copyright 2022, Ken Matsui.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/orm/FilterBuilder.h>\n#include <string>\n\nnamespace drogon\n{\nnamespace orm\n{\ntemplate <typename T>\nclass QueryBuilder : public FilterBuilder<T, true>\n{\n    /**\n     * @brief When a user does not set the table name explicitly, then retrieve\n     * it from model `T`.\n     *\n     * @return std::string The table name\n     */\n    inline const std::string &getTableName() const\n    {\n        return this->from_.empty() ? T::tableName : this->from_;\n    }\n\n  public:\n    /**\n     * @brief Set from which table to return.\n     *\n     * @param table The table.\n     *\n     * @return QueryBuilder& The QueryBuilder itself.\n     */\n    inline QueryBuilder &from(const std::string &table)\n    {\n        this->from_ = table;\n        return *this;\n    }\n\n    /**\n     * @brief Select specific columns.\n     *\n     * @param columns The columns.\n     *\n     * @return FilterBuilder<T, false> A new FilterBuilder.\n     *\n     * @note If you would return all rows, please use the `selectAll` method.\n     * The method can return rows as model type `T`.\n     */\n    inline FilterBuilder<T, false> select(const std::string &columns) const\n    {\n        return {getTableName(), columns};\n    }\n\n    /**\n     * @brief Select all columns.\n     *\n     * @return FilterBuilder<T, true> A new FilterBuilder.\n     */\n    inline FilterBuilder<T, true> selectAll() const\n    {\n        return {getTableName(), \"*\"};\n    }\n};\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/RestfulController.h",
    "content": "/**\n *\n *  @file RestfulController.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <drogon/drogon.h>\n#include <drogon/orm/DbClient.h>\n#include <drogon/orm/Mapper.h>\n#include <trantor/utils/NonCopyable.h>\n#include <string>\n#include <functional>\n#include <vector>\n\nnamespace drogon\n{\n/**\n * @brief this class is a helper class for the implementation of restful api\n * controllers generated by the drogon_ctl command.\n */\nclass DROGON_EXPORT RestfulController : trantor::NonCopyable\n{\n  public:\n    void enableMasquerading(const std::vector<std::string> &pMasqueradingVector)\n    {\n        masquerading_ = true;\n        masqueradingVector_ = pMasqueradingVector;\n        for (size_t i = 0; i < masqueradingVector_.size(); ++i)\n        {\n            masqueradingMap_.insert(\n                std::pair<std::string, size_t>{masqueradingVector_[i], i});\n        }\n    }\n\n    void disableMasquerading()\n    {\n        masquerading_ = false;\n        masqueradingVector_ = columnsVector_;\n        for (size_t i = 0; i < masqueradingVector_.size(); ++i)\n        {\n            masqueradingMap_.insert(\n                std::pair<std::string, size_t>{masqueradingVector_[i], i});\n        }\n    }\n\n    void registerAJsonValidator(\n        const std::string &fieldName,\n        const std::function<bool(const Json::Value &, std::string &)>\n            &validator)\n    {\n        validators_.emplace_back(fieldName, validator);\n    }\n\n    void registerAJsonValidator(\n        const std::string &fieldName,\n        std::function<bool(const Json::Value &, std::string &)> &&validator)\n    {\n        validators_.emplace_back(fieldName, std::move(validator));\n    }\n\n    /**\n     * @brief make a criteria object for searching by ORM.\n     *\n     * @param pJson the json object presenting search criteria.\n     * The json object must be an array of depth 3.\n     * for example:\n     * [\n     *    [\n     *       [\"color\",\"=\",\"red\"], //AND\n     *       [\"price\",\"<\",1000]\n     *    ], //OR\n     *    [\n     *        [\"color\",\"=\",\"white\"], //AND\n     *        [\"price\",\"<\",800],  //AND\n     *        [\"brand\",\"!=\",null]\n     *    ]\n     * ]\n     * @return orm::Criteria\n     */\n\n    orm::Criteria makeCriteria(const Json::Value &pJson) noexcept(false);\n\n  protected:\n    RestfulController(const std::vector<std::string> &columnsVector)\n        : columnsVector_(columnsVector)\n    {\n    }\n\n    std::vector<std::string> fieldsSelector(const std::set<std::string> &fields)\n    {\n        std::vector<std::string> ret;\n        for (auto &field : masqueradingVector_)\n        {\n            if (!field.empty() && fields.find(field) != fields.end())\n            {\n                ret.emplace_back(field);\n            }\n            else\n            {\n                ret.emplace_back(std::string{});\n            }\n        }\n        return ret;\n    }\n\n    template <typename T>\n    Json::Value makeJson(const HttpRequestPtr &req, const T &obj)\n    {\n        auto &queryParams = req->parameters();\n        auto iter = queryParams.find(\"fields\");\n        if (masquerading_)\n        {\n            if (iter != queryParams.end())\n            {\n                auto fields = utils::splitStringToSet(iter->second, \",\");\n                return obj.toMasqueradedJson(fieldsSelector(fields));\n            }\n            else\n            {\n                return obj.toMasqueradedJson(masqueradingVector_);\n            }\n        }\n        else\n        {\n            if (iter != queryParams.end())\n            {\n                auto fields = utils::splitString(iter->second, \",\");\n                return obj.toMasqueradedJson(fields);\n            }\n            else\n            {\n                return obj.toJson();\n            }\n        }\n    }\n\n    bool doCustomValidations(const Json::Value &pJson, std::string &err)\n    {\n        for (auto &validator : validators_)\n        {\n            if (pJson.isMember(validator.first))\n            {\n                if (!validator.second(pJson[validator.first], err))\n                {\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    bool isMasquerading() const\n    {\n        return masquerading_;\n    }\n\n    const std::vector<std::string> &masqueradingVector() const\n    {\n        return masqueradingVector_;\n    }\n\n  private:\n    bool masquerading_{true};\n    std::vector<std::string> masqueradingVector_;\n    std::vector<\n        std::pair<std::string,\n                  std::function<bool(const Json::Value &, std::string &)>>>\n        validators_;\n    std::unordered_map<std::string, size_t> masqueradingMap_;\n    const std::vector<std::string> columnsVector_;\n};\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/Result.h",
    "content": "/**\n *\n *  @file Result.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n// Taken from libpqxx and modified.\n// The license for libpqxx can be found in the COPYING file.\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <memory>\n#include <string>\n#include <future>\n#include <algorithm>\n#include <assert.h>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass ConstResultIterator;\nclass ConstReverseResultIterator;\nclass Row;\nclass ResultImpl;\nusing ResultImplPtr = std::shared_ptr<ResultImpl>;\n\nenum class SqlStatus\n{\n    Ok,\n    End\n};\n\n/// Result set containing data returned by a query or command.\n/** This behaves as a container (as defined by the C++ standard library) and\n * provides random access const iterators to iterate over its rows.  A row\n * can also be accessed by indexing a result R by the row's zero-based\n * number:\n *\n * @code\n *\tfor (Result::SizeType i=0; i < R.size(); ++i) Process(R[i]);\n * @endcode\n *\n * Result sets in libpqxx are lightweight, Reference-counted wrapper objects\n * which are relatively small and cheap to copy.  Think of a result object as\n * a \"smart pointer\" to an underlying result set.\n */\nclass DROGON_EXPORT Result\n{\n  public:\n    explicit Result(ResultImplPtr ptr) : resultPtr_(std::move(ptr))\n    {\n    }\n\n    Result(const Result &r) noexcept = default;\n    Result(Result &&) noexcept = default;\n    Result &operator=(const Result &r) noexcept;\n    Result &operator=(Result &&) noexcept;\n    using DifferenceType = long;\n    using SizeType = size_t;\n    using Reference = Row;\n    using ConstIterator = ConstResultIterator;\n    using Iterator = ConstIterator;\n    using RowSizeType = unsigned long;\n    using FieldSizeType = unsigned long;\n\n    using ConstReverseIterator = ConstReverseResultIterator;\n    using ReverseIterator = ConstReverseIterator;\n\n    // C++ type type definition compatibility\n    using value_type = Row;\n    using size_type = SizeType;\n    using difference_type = DifferenceType;\n    using reference = Reference;\n    using const_reference = const Reference;\n    using iterator = Iterator;\n    using const_iterator = ConstIterator;\n    using reverse_iterator = ConstReverseIterator;\n    using const_reverse_iterator = ConstReverseIterator;\n\n    SizeType size() const noexcept;\n\n    SizeType capacity() const noexcept\n    {\n        return size();\n    }\n\n    ConstIterator begin() const noexcept;\n    ConstIterator cbegin() const noexcept;\n    ConstIterator end() const noexcept;\n    ConstIterator cend() const noexcept;\n\n    ConstReverseIterator rbegin() const;\n    ConstReverseIterator crbegin() const;\n    ConstReverseIterator rend() const;\n    ConstReverseIterator crend() const;\n\n    bool empty() const noexcept\n    {\n        return size() == 0;\n    }\n\n    Reference front() const noexcept;\n    Reference back() const noexcept;\n\n    Reference operator[](SizeType index) const noexcept;\n    Reference at(SizeType index) const;\n    void swap(Result &) noexcept;\n\n    /// Number of columns in result.\n    RowSizeType columns() const noexcept;\n\n    /// Name of column with this number (throws exception if it doesn't exist)\n    const char *columnName(RowSizeType number) const;\n\n    /// If command was @c INSERT, @c UPDATE, or @c DELETE: number of affected\n    /// rows\n    /**\n     * @return Number of affected rows if last command was @c INSERT, @c UPDATE,\n     * or @c DELETE; zero for all other commands.\n     */\n    SizeType affectedRows() const noexcept;\n\n    /// For Mysql, Sqlite3 databases, return the auto-incrementing primary key\n    /// after inserting\n    /**\n     * For postgreSQL databases, this method always returns zero, One can use\n     * the following sql to get auto-incrementing id:\n     *   insert into table_name volumn1, volumn2 values(....) returning id;\n     */\n    unsigned long long insertId() const noexcept;\n\n#ifdef _MSC_VER\n    Result() noexcept = default;\n#endif\n\n  private:\n    ResultImplPtr resultPtr_;\n\n    friend class Field;\n    friend class Row;\n    /// Number of given column (throws exception if it doesn't exist).\n    RowSizeType columnNumber(const char colName[]) const;\n\n    /// Number of given column (throws exception if it doesn't exist).\n    RowSizeType columnNumber(const std::string &name) const\n    {\n        return columnNumber(name.c_str());\n    }\n\n    /// Get the column oid, for postgresql database\n    int oid(RowSizeType column) const noexcept;\n\n    const char *getValue(SizeType row, RowSizeType column) const;\n    bool isNull(SizeType row, RowSizeType column) const;\n    FieldSizeType getLength(SizeType row, RowSizeType column) const;\n};\n\ninline void swap(Result &one, Result &two) noexcept\n{\n    one.swap(two);\n}\n}  // namespace orm\n}  // namespace drogon\n\n#ifndef _MSC_VER\nnamespace std\n{\ntemplate <>\ninline void swap(drogon::orm::Result &one, drogon::orm::Result &two) noexcept\n{\n    one.swap(two);\n}\n}  // namespace std\n#endif\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/ResultIterator.h",
    "content": "/**\n *\n *  ResultIterator.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n// Taken from libpqxx and modified.\n// The license for libpqxx can be found in the COPYING file.\n\n#pragma once\n\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass ConstResultIterator : protected Row\n{\n  public:\n    using iterator_category = std::random_access_iterator_tag;\n    using pointer = const Row *;\n    using reference = const Row &;\n    using value_type = const Row;\n    using size_type = Result::SizeType;\n    using difference_type = Result::DifferenceType;\n\n    // ConstResultIterator(const Row &t) noexcept : Row(t) {}\n\n    pointer operator->()\n    {\n        return this;\n    }\n\n    reference operator*()\n    {\n        return *this;\n    }\n\n    ConstResultIterator operator++(int);\n\n    ConstResultIterator &operator++()\n    {\n        ++index_;\n        return *this;\n    }\n\n    ConstResultIterator operator--(int);\n\n    ConstResultIterator &operator--()\n    {\n        --index_;\n        return *this;\n    }\n\n    ConstResultIterator &operator+=(difference_type i)\n    {\n        index_ += i;\n        return *this;\n    }\n\n    ConstResultIterator &operator-=(difference_type i)\n    {\n        index_ -= i;\n        return *this;\n    }\n\n    bool operator==(const ConstResultIterator &other) const\n    {\n        return index_ == other.index_;\n    }\n\n    bool operator!=(const ConstResultIterator &other) const\n    {\n        return index_ != other.index_;\n    }\n\n    bool operator>(const ConstResultIterator &other) const\n    {\n        return index_ > other.index_;\n    }\n\n    bool operator<(const ConstResultIterator &other) const\n    {\n        return index_ < other.index_;\n    }\n\n    bool operator>=(const ConstResultIterator &other) const\n    {\n        return index_ >= other.index_;\n    }\n\n    bool operator<=(const ConstResultIterator &other) const\n    {\n        return index_ <= other.index_;\n    }\n\n    ConstResultIterator(const ConstResultIterator &) noexcept = default;\n    ConstResultIterator(ConstResultIterator &&) noexcept = default;\n\n  private:\n    friend class Result;\n\n    ConstResultIterator(const Result &r, SizeType index) noexcept\n        : Row(r, index)\n    {\n    }\n};\n\nclass ConstReverseResultIterator : private ConstResultIterator\n{\n  public:\n    using super = ConstResultIterator;\n    using iterator_type = ConstResultIterator;\n    using iterator_type::difference_type;\n    using iterator_type::iterator_category;\n    using iterator_type::pointer;\n    using iterator_type::reference;\n\n    // using iterator_type::value_type;\n\n    ConstReverseResultIterator(const ConstReverseResultIterator &rhs)\n        : ConstResultIterator(rhs)\n    {\n    }\n\n    explicit ConstReverseResultIterator(const ConstResultIterator &rhs)\n        : ConstResultIterator(rhs)\n    {\n        super::operator--();\n    }\n\n    ConstResultIterator base() const noexcept;\n\n    using iterator_type::operator->;\n    using iterator_type::operator*;\n\n    ConstReverseResultIterator operator++(int);\n\n    ConstReverseResultIterator &operator++()\n    {\n        iterator_type::operator--();\n        return *this;\n    }\n\n    ConstReverseResultIterator operator--(int);\n\n    ConstReverseResultIterator &operator--()\n    {\n        iterator_type::operator++();\n        return *this;\n    }\n\n    ConstReverseResultIterator &operator+=(difference_type i)\n    {\n        iterator_type::operator-=(i);\n        return *this;\n    }\n\n    ConstReverseResultIterator &operator-=(difference_type i)\n    {\n        iterator_type::operator+=(i);\n        return *this;\n    }\n\n    bool operator==(const ConstReverseResultIterator &other) const\n    {\n        return index_ == other.index_;\n    }\n\n    bool operator!=(const ConstReverseResultIterator &other) const\n    {\n        return index_ != other.index_;\n    }\n\n    bool operator>(const ConstReverseResultIterator &other) const\n    {\n        return index_ < other.index_;\n    }\n\n    bool operator<(const ConstReverseResultIterator &other) const\n    {\n        return index_ > other.index_;\n    }\n\n    bool operator>=(const ConstReverseResultIterator &other) const\n    {\n        return index_ <= other.index_;\n    }\n\n    bool operator<=(const ConstReverseResultIterator &other) const\n    {\n        return index_ >= other.index_;\n    }\n};\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/Row.h",
    "content": "/**\n *\n *  @file Row.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n// Taken from libpqxx and modified.\n// The license for libpqxx can be found in the COPYING file.\n\n#pragma once\n\n#include <drogon/exports.h>\n#include <drogon/orm/Result.h>\n#include <string>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass Field;\nclass ConstRowIterator;\nclass ConstReverseRowIterator;\n\n/// Reference to one row in a result.\n/**\n * A row represents one row (also called a row) in a query result set.\n * It also acts as a container mapping column numbers or names to field\n * values (see below):\n *\n * @code\n *\tcout << row[\"date\"].as<std::string>() << \": \" <<\n *row[\"name\"].as<std::string>() << endl;\n * @endcode\n *\n * The row itself acts like a (non-modifyable) container, complete with its\n * own const_iterator and const_reverse_iterator.\n */\nclass DROGON_EXPORT Row\n{\n  public:\n    using SizeType = size_t;\n    using Reference = Field;\n    using ConstIterator = ConstRowIterator;\n    using Iterator = ConstIterator;\n    using ConstReverseIterator = ConstReverseRowIterator;\n\n    using DifferenceType = long;\n\n    Reference operator[](SizeType index) const noexcept;\n    Reference operator[](int index) const noexcept;\n    Reference operator[](const char columnName[]) const;\n    Reference operator[](const std::string &columnName) const;\n\n    Reference at(SizeType index) const;\n    Reference at(const char columnName[]) const;\n    Reference at(const std::string &columnName) const;\n\n    SizeType size() const;\n\n    SizeType capacity() const noexcept\n    {\n        return size();\n    }\n\n    ConstIterator begin() const noexcept;\n    ConstIterator cbegin() const noexcept;\n    ConstIterator end() const noexcept;\n    ConstIterator cend() const noexcept;\n\n    ConstReverseIterator rbegin() const;\n    ConstReverseIterator crbegin() const;\n    ConstReverseIterator rend() const;\n    ConstReverseIterator crend() const;\n\n#ifdef _MSC_VER\n    Row() noexcept = default;\n#endif\n\n    Row(const Row &r) noexcept = default;\n    Row(Row &&) noexcept = default;\n    Row &operator=(const Row &) noexcept = default;\n\n  private:\n    Result result_;\n\n  protected:\n    friend class Field;\n    /**\n     * Row number\n     * You'd expect this to be a size_t, but due to the way reverse iterators\n     * are related to regular iterators, it must be allowed to underflow to -1.\n     */\n    long index_{0};\n    Row::SizeType end_{0};\n    friend class Result;\n    Row(const Result &r, SizeType index) noexcept;\n};\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/RowIterator.h",
    "content": "/**\n *\n *  RowIterator.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n// Taken from libpqxx and modified.\n// The license for libpqxx can be found in the COPYING file.\n\n#pragma once\n\n#include <drogon/orm/Field.h>\n#include <drogon/orm/Row.h>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass ConstRowIterator : protected Field\n{\n  public:\n    using pointer = const Field *;\n    using reference = const Field &;\n    using value_type = const Field;\n    using size_type = Row::SizeType;\n    using difference_type = Row::DifferenceType;\n    using iterator_category = std::random_access_iterator_tag;\n\n    // ConstRowIterator(const Field &t) noexcept : Field(t) {}\n\n    pointer operator->()\n    {\n        return this;\n    }\n\n    reference operator*()\n    {\n        return *this;\n    }\n\n    ConstRowIterator operator++(int);\n\n    ConstRowIterator &operator++()\n    {\n        ++column_;\n        return *this;\n    }\n\n    ConstRowIterator operator--(int);\n\n    ConstRowIterator &operator--()\n    {\n        --column_;\n        return *this;\n    }\n\n    ConstRowIterator &operator+=(difference_type i)\n    {\n        column_ += i;\n        return *this;\n    }\n\n    ConstRowIterator &operator-=(difference_type i)\n    {\n        column_ -= i;\n        return *this;\n    }\n\n    bool operator==(const ConstRowIterator &other) const\n    {\n        return column_ == other.column_;\n    }\n\n    bool operator!=(const ConstRowIterator &other) const\n    {\n        return column_ != other.column_;\n    }\n\n    bool operator>(const ConstRowIterator &other) const\n    {\n        return column_ > other.column_;\n    }\n\n    bool operator<(const ConstRowIterator &other) const\n    {\n        return column_ < other.column_;\n    }\n\n    bool operator>=(const ConstRowIterator &other) const\n    {\n        return column_ >= other.column_;\n    }\n\n    bool operator<=(const ConstRowIterator &other) const\n    {\n        return column_ <= other.column_;\n    }\n\n  private:\n    friend class Row;\n\n    ConstRowIterator(const Row &r, SizeType column) noexcept : Field(r, column)\n    {\n    }\n};\n\nclass ConstReverseRowIterator : private ConstRowIterator\n{\n  public:\n    using super = ConstRowIterator;\n    using iterator_type = ConstRowIterator;\n    using iterator_type::difference_type;\n    using iterator_type::iterator_category;\n    using iterator_type::pointer;\n    using iterator_type::reference;\n\n    // using iterator_type::value_type;\n\n    ConstReverseRowIterator(const ConstReverseRowIterator &rhs)\n        : ConstRowIterator(rhs)\n    {\n    }\n\n    explicit ConstReverseRowIterator(const ConstRowIterator &rhs)\n        : ConstRowIterator(rhs)\n    {\n        super::operator--();\n    }\n\n    ConstRowIterator base() const noexcept;\n\n    using iterator_type::operator->;\n    using iterator_type::operator*;\n\n    ConstReverseRowIterator operator++(int);\n\n    ConstReverseRowIterator &operator++()\n    {\n        iterator_type::operator--();\n        return *this;\n    }\n\n    ConstReverseRowIterator operator--(int);\n\n    ConstReverseRowIterator &operator--()\n    {\n        iterator_type::operator++();\n        return *this;\n    }\n\n    ConstReverseRowIterator &operator+=(difference_type i)\n    {\n        iterator_type::operator-=(i);\n        return *this;\n    }\n\n    ConstReverseRowIterator &operator-=(difference_type i)\n    {\n        iterator_type::operator+=(i);\n        return *this;\n    }\n\n    bool operator==(const ConstReverseRowIterator &other) const\n    {\n        return column_ == other.column_;\n    }\n\n    bool operator!=(const ConstReverseRowIterator &other) const\n    {\n        return column_ != other.column_;\n    }\n\n    bool operator>(const ConstReverseRowIterator &other) const\n    {\n        return column_ < other.column_;\n    }\n\n    bool operator<(const ConstReverseRowIterator &other) const\n    {\n        return column_ > other.column_;\n    }\n\n    bool operator>=(const ConstReverseRowIterator &other) const\n    {\n        return column_ <= other.column_;\n    }\n\n    bool operator<=(const ConstReverseRowIterator &other) const\n    {\n        return column_ >= other.column_;\n    }\n};\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/SqlBinder.h",
    "content": "/**\n *\n *  @file SqlBinder.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n#include <drogon/exports.h>\n#include <drogon/orm/DbTypes.h>\n#include <drogon/orm/Exception.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/FunctionTraits.h>\n#include <drogon/orm/ResultIterator.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/RowIterator.h>\n#include <string_view>\n#include <json/writer.h>\n#include <trantor/utils/Logger.h>\n#include <trantor/utils/NonCopyable.h>\n#include <json/json.h>\n#include <functional>\n#include <iostream>\n#include <map>\n#include <memory>\n#include <mutex>\n#include <sstream>\n#include <string.h>\n#include <string>\n#include <vector>\n#include <optional>\n#include <type_traits>\n#ifdef _WIN32\n#include <winsock2.h>\n#else  // some Unix-like OS\n#include <arpa/inet.h>\n#endif\n\n#if defined __linux__ || defined __FreeBSD__ || defined __OpenBSD__ || \\\n    defined __MINGW32__ || defined __HAIKU__\n\n#ifdef __linux__\n#include <endian.h>  // __BYTE_ORDER __LITTLE_ENDIAN\n#elif defined __FreeBSD__ || defined __OpenBSD__\n#include <sys/endian.h>  // _BYTE_ORDER _LITTLE_ENDIAN\n#define __BYTE_ORDER _BYTE_ORDER\n#define __LITTLE_ENDIAN _LITTLE_ENDIAN\n#elif defined __MINGW32__\n#include <sys/param.h>  // BYTE_ORDER LITTLE_ENDIAN\n#define __BYTE_ORDER BYTE_ORDER\n#define __LITTLE_ENDIAN LITTLE_ENDIAN\n#endif\n\n#include <algorithm>  // std::reverse()\n\ntemplate <typename T>\nconstexpr T htonT(T value) noexcept\n{\n#if __BYTE_ORDER == __LITTLE_ENDIAN\n    return (std::reverse(reinterpret_cast<char *>(&value),\n                         reinterpret_cast<char *>(&value) + sizeof(T)),\n            value);\n#else\n    return value;\n#endif\n}\n\n#if (!defined _WIN32) || (defined _WIN32 && _WIN32_WINNT < _WIN32_WINNT_WIN8)\ninline uint64_t htonll(uint64_t value)\n{\n    return htonT<uint64_t>(value);\n}\n\ninline uint64_t ntohll(uint64_t value)\n{\n    return htonll(value);\n}\n#endif\n#endif\n\nnamespace drogon\n{\nnamespace orm\n{\nenum class ClientType\n{\n    PostgreSQL = 0,\n    Mysql,\n    Sqlite3\n};\n\nenum Sqlite3Type\n{\n    Sqlite3TypeChar = 0,\n    Sqlite3TypeShort,\n    Sqlite3TypeInt,\n    Sqlite3TypeInt64,\n    Sqlite3TypeDouble,\n    Sqlite3TypeText,\n    Sqlite3TypeBlob,\n    Sqlite3TypeNull\n};\n\nclass DbClient;\nusing QueryCallback = std::function<void(const Result &)>;\nusing ExceptPtrCallback = std::function<void(const std::exception_ptr &)>;\nenum class Mode\n{\n    NonBlocking,\n    Blocking\n};\n\nstruct RawParameter\n{\n    std::shared_ptr<void> obj;\n    const char *parameter;\n    int length;\n    int format;\n};\n\nnamespace internal\n{\ntemplate <typename T>\nstruct VectorTypeTraits\n{\n    static const bool isVector = false;\n    static const bool isPtrVector = false;\n    using ItemsType = T;\n};\n\ntemplate <typename T>\nstruct VectorTypeTraits<std::vector<std::shared_ptr<T>>>\n{\n    static const bool isVector = true;\n    static const bool isPtrVector = true;\n    using ItemsType = T;\n};\n\ntemplate <>\nstruct VectorTypeTraits<std::string>\n{\n    static const bool isVector = false;\n    static const bool isPtrVector = false;\n    using ItemsType = std::string;\n};\n\n// we only accept value type or const lreference type or rreference type as\n// handle method parameters type\ntemplate <typename T>\nstruct CallbackArgTypeTraits\n{\n    static const bool isValid = true;\n};\n\ntemplate <typename T>\nstruct CallbackArgTypeTraits<T *>\n{\n    static const bool isValid = false;\n};\n\ntemplate <typename T>\nstruct CallbackArgTypeTraits<T &>\n{\n    static const bool isValid = false;\n};\n\ntemplate <typename T>\nstruct CallbackArgTypeTraits<T &&>\n{\n    static const bool isValid = true;\n};\n\ntemplate <typename T>\nstruct CallbackArgTypeTraits<const T &>\n{\n    static const bool isValid = true;\n};\n\nclass CallbackHolderBase\n{\n  public:\n    virtual ~CallbackHolderBase() = default;\n    virtual void execCallback(const Result &result) = 0;\n};\n\ntemplate <typename Function>\nclass CallbackHolder : public CallbackHolderBase\n{\n  public:\n    void execCallback(const Result &result) override\n    {\n        run(result);\n    }\n\n    template <typename T>\n    explicit CallbackHolder(T &&function) : function_(std::forward<T>(function))\n    {\n        static_assert(traits::isSqlCallback,\n                      \"Your sql callback function type is wrong!\");\n    }\n\n  private:\n    Function function_;\n    using traits = FunctionTraits<Function>;\n    template <std::size_t Index>\n    using NthArgumentType = typename traits::template argument<Index>;\n    static const size_t argumentCount = traits::arity;\n\n    template <bool isStep = traits::isStepResultCallback>\n    void run(const Result &result)\n    {\n        if constexpr (isStep)\n        {\n            if (result.empty())\n            {\n                run(nullptr, true);\n                return;\n            }\n            for (auto const &row : result)\n            {\n                run(&row, false);\n            }\n            run(nullptr, true);\n        }\n        else\n        {\n            static_assert(argumentCount == 0,\n                          \"Your sql callback function type is wrong!\");\n            function_(result);\n        }\n    }\n\n    template <typename... Values, std::size_t Boundary = argumentCount>\n    void run(const Row *const row, bool isNull, Values &&...values)\n    {\n        if constexpr (sizeof...(Values) < Boundary)\n        {\n            // call this function recursively until parameter's count equals to\n            // the count of target function parameters\n            static_assert(\n                CallbackArgTypeTraits<\n                    NthArgumentType<sizeof...(Values)>>::isValid,\n                \"your sql callback function argument type must be value \"\n                \"type or \"\n                \"const \"\n                \"left-reference type\");\n            using ValueType =\n                typename std::remove_cv<typename std::remove_reference<\n                    NthArgumentType<sizeof...(Values)>>::type>::type;\n            ValueType value = ValueType();\n            if (row && row->size() > sizeof...(Values))\n            {\n                // if(!VectorTypeTraits<ValueType>::isVector)\n                //     value = (*row)[sizeof...(Values)].as<ValueType>();\n                // else\n                //     ; // value =\n                //     (*row)[sizeof...(Values)].asArray<VectorTypeTraits<ValueType>::ItemsType>();\n                value = makeValue<ValueType>(\n                    (*row)[(Row::SizeType)sizeof...(Values)]);\n            }\n\n            run(row, isNull, std::forward<Values>(values)..., std::move(value));\n        }\n        else if constexpr (sizeof...(Values) == Boundary)\n        {\n            function_(isNull, std::move(values)...);\n        }\n    }\n\n    template <typename ValueType>\n    ValueType makeValue(const Field &field)\n    {\n        if constexpr (VectorTypeTraits<ValueType>::isVector)\n        {\n            return field\n                .asArray<typename VectorTypeTraits<ValueType>::ItemsType>();\n        }\n        else\n        {\n            return field.as<ValueType>();\n        }\n    }\n};\n\nclass DROGON_EXPORT SqlBinder : public trantor::NonCopyable\n{\n    using self = SqlBinder;\n\n  public:\n    SqlBinder(const std::string &sql, DbClient &client, ClientType type)\n        : sqlPtr_(std::make_shared<std::string>(sql)),\n          sqlViewPtr_(sqlPtr_->data()),\n          sqlViewLength_(sqlPtr_->length()),\n          client_(client),\n          type_(type)\n    {\n    }\n\n    SqlBinder(std::string &&sql, DbClient &client, ClientType type)\n        : sqlPtr_(std::make_shared<std::string>(std::move(sql))),\n          sqlViewPtr_(sqlPtr_->data()),\n          sqlViewLength_(sqlPtr_->length()),\n          client_(client),\n          type_(type)\n    {\n    }\n\n    SqlBinder(const char *sql,\n              size_t sqlLength,\n              DbClient &client,\n              ClientType type)\n        : sqlViewPtr_(sql),\n          sqlViewLength_(sqlLength),\n          client_(client),\n          type_(type)\n    {\n    }\n\n    SqlBinder(SqlBinder &&that) noexcept\n        : sqlPtr_(std::move(that.sqlPtr_)),\n          sqlViewPtr_(that.sqlViewPtr_),\n          sqlViewLength_(that.sqlViewLength_),\n          client_(that.client_),\n          parametersNumber_(that.parametersNumber_),\n          parameters_(std::move(that.parameters_)),\n          lengths_(std::move(that.lengths_)),\n          formats_(std::move(that.formats_)),\n          objs_(std::move(that.objs_)),\n          mode_(that.mode_),\n          callbackHolder_(std::move(that.callbackHolder_)),\n          exceptionCallback_(std::move(that.exceptionCallback_)),\n          exceptionPtrCallback_(std::move(that.exceptionPtrCallback_)),\n          execed_(that.execed_),\n          destructed_(that.destructed_),\n          isExceptionPtr_(that.isExceptionPtr_),\n          type_(that.type_)\n    {\n        // set the execed_ to true to avoid the same sql being executed twice.\n        that.execed_ = true;\n    }\n\n    SqlBinder &operator=(SqlBinder &&that) = delete;\n    ~SqlBinder();\n\n    template <typename CallbackType,\n              typename traits =\n                  FunctionTraits<typename std::decay<CallbackType>::type>>\n    self &operator>>(CallbackType &&callback)\n    {\n        if constexpr (traits::isExceptCallback)\n        {\n            if constexpr (traits::isPtr)\n            {\n                // LOG_DEBUG << \"ptr callback\";\n                isExceptionPtr_ = true;\n                exceptionPtrCallback_ = std::forward<CallbackType>(callback);\n                return *this;\n            }\n            else\n            {\n                isExceptionPtr_ = false;\n                exceptionCallback_ = std::forward<CallbackType>(callback);\n                return *this;\n            }\n        }\n        else if constexpr (traits::isSqlCallback)\n        {\n            callbackHolder_ = std::shared_ptr<CallbackHolderBase>(\n                new CallbackHolder<typename std::decay<CallbackType>::type>(\n                    std::forward<CallbackType>(callback)));\n            return *this;\n        }\n    }\n\n    template <typename T>\n    self &operator<<(T &&parameter)\n    {\n        using ParaType = std::remove_cv_t<std::remove_reference_t<T>>;\n        ++parametersNumber_;\n        std::shared_ptr<void> obj = std::make_shared<ParaType>(parameter);\n        if (type_ == ClientType::PostgreSQL)\n        {\n            switch (sizeof(T))\n            {\n                case 2:\n                    *std::static_pointer_cast<uint16_t>(obj) =\n                        htons((uint16_t)parameter);\n                    break;\n                case 4:\n                    *std::static_pointer_cast<uint32_t>(obj) =\n                        htonl((uint32_t)parameter);\n                    break;\n                case 8:\n                    *std::static_pointer_cast<uint64_t>(obj) =\n                        htonll((uint64_t)parameter);\n                    break;\n                case 1:\n                default:\n                    break;\n            }\n            objs_.push_back(obj);\n            parameters_.push_back((char *)obj.get());\n            lengths_.push_back(sizeof(T));\n            formats_.push_back(1);\n        }\n        else if (type_ == ClientType::Mysql)\n        {\n            objs_.push_back(obj);\n            parameters_.push_back((char *)obj.get());\n            lengths_.push_back(0);\n            formats_.push_back(getMysqlType<ParaType>());\n        }\n        else if (type_ == ClientType::Sqlite3)\n        {\n            objs_.push_back(obj);\n            parameters_.push_back((char *)obj.get());\n            lengths_.push_back(0);\n            switch (sizeof(T))\n            {\n                case 1:\n                    formats_.push_back(Sqlite3TypeChar);\n                    break;\n                case 2:\n                    formats_.push_back(Sqlite3TypeShort);\n                    break;\n                case 4:\n                    formats_.push_back(Sqlite3TypeInt);\n                    break;\n                case 8:\n                    formats_.push_back(Sqlite3TypeInt64);\n                default:\n                    break;\n            }\n        }\n        // LOG_TRACE << \"Bind parameter:\" << parameter;\n        return *this;\n    }\n\n    self &operator<<(const RawParameter &);\n\n    self &operator<<(RawParameter &param)\n    {\n        return operator<<((const RawParameter &)param);\n    }\n\n    self &operator<<(RawParameter &&);\n\n    // template <>\n    self &operator<<(const char str[])\n    {\n        return operator<<(std::string(str));\n    }\n\n    self &operator<<(char str[])\n    {\n        return operator<<(std::string(str));\n    }\n\n    self &operator<<(const std::string_view &str);\n\n    self &operator<<(std::string_view &&str)\n    {\n        return operator<<((const std::string_view &)str);\n    }\n\n    self &operator<<(std::string_view &str)\n    {\n        return operator<<((const std::string_view &)str);\n    }\n\n    self &operator<<(const std::string &str);\n\n    self &operator<<(std::string &str)\n    {\n        return operator<<((const std::string &)str);\n    }\n\n    self &operator<<(std::string &&str);\n\n    self &operator<<(trantor::Date date)\n    {\n        return operator<<(date.toDbStringLocal());\n    }\n\n    self &operator<<(const std::vector<char> &v);\n\n    self &operator<<(std::vector<char> &v)\n    {\n        return operator<<((const std::vector<char> &)v);\n    }\n\n    self &operator<<(std::vector<char> &&v);\n\n    self &operator<<(float f)\n    {\n        if (type_ == ClientType::Sqlite3)\n        {\n            return operator<<((double)f);\n        }\n        return operator<<(std::to_string(f));\n    }\n\n    self &operator<<(double f);\n    self &operator<<(std::nullptr_t);\n    self &operator<<(DefaultValue dv);\n\n    self &operator<<(const Mode &mode)\n    {\n        mode_ = mode;\n        return *this;\n    }\n\n    self &operator<<(Mode &mode)\n    {\n        mode_ = mode;\n        return *this;\n    }\n\n    self &operator<<(Mode &&mode)\n    {\n        mode_ = mode;\n        return *this;\n    }\n\n    template <typename T>\n    self &operator<<(const std::optional<T> &parameter)\n    {\n        if (parameter)\n        {\n            return *this << parameter.value();\n        }\n        return *this << nullptr;\n    }\n\n    template <typename T>\n    self &operator<<(std::optional<T> &parameter)\n    {\n        if (parameter)\n        {\n            return *this << parameter.value();\n        }\n        return *this << nullptr;\n    }\n\n    template <typename T>\n    self &operator<<(std::optional<T> &&parameter)\n    {\n        if (parameter)\n        {\n            return *this << std::move(parameter.value());\n        }\n        return *this << nullptr;\n    }\n\n    self &operator<<(const Json::Value &j) noexcept(true)\n    {\n        switch (j.type())\n        {\n            case Json::nullValue:\n                return *this << nullptr;\n            case Json::intValue:\n                return *this << j.asInt64();\n            case Json::uintValue:\n                return *this << j.asUInt64();\n            case Json::realValue:\n                return *this << j.asDouble();\n            case Json::stringValue:\n                return *this << j.asString();\n            case Json::booleanValue:\n                return *this << j.asBool();\n            case Json::arrayValue:\n            case Json::objectValue:\n            default:\n                static Json::StreamWriterBuilder jsonBuilder;\n                std::once_flag once_json;\n                std::call_once(once_json,\n                               []() { jsonBuilder[\"indentation\"] = \"\"; });\n                return *this << Json::writeString(jsonBuilder, j);\n        }\n    }\n\n    self &operator<<(Json::Value &j) noexcept(true)\n    {\n        return *this << static_cast<const Json::Value &>(j);\n    }\n\n    self &operator<<(Json::Value &&j) noexcept(true)\n    {\n        return *this << static_cast<const Json::Value &>(j);\n    }\n\n    void exec() noexcept(false);\n\n  private:\n    static int getMysqlTypeBySize(size_t size);\n\n    template <typename T>\n    static int getMysqlType()\n    {\n        if constexpr (std::is_same_v<T, bool>)\n        {\n            return MySqlTiny;\n        }\n        else if constexpr (std::is_same_v<T, int8_t> ||\n                           std::is_same_v<T, signed char>)\n        {\n            return MySqlTiny;\n        }\n        else if constexpr (std::is_same_v<T, uint8_t> ||\n                           std::is_same_v<T, unsigned char>)\n        {\n            return MySqlUTiny;\n        }\n        else if constexpr (std::is_same_v<T, int16_t> ||\n                           std::is_same_v<T, short>)\n        {\n            return MySqlShort;\n        }\n        else if constexpr (std::is_same_v<T, uint16_t> ||\n                           std::is_same_v<T, unsigned short>)\n        {\n            return MySqlUShort;\n        }\n        else if constexpr (std::is_same_v<T, int32_t> ||\n                           (std::is_same_v<T, int> && sizeof(int) == 4) ||\n                           (std::is_same_v<T, long> && sizeof(long) == 4))\n        {\n            return MySqlLong;\n        }\n        else if constexpr (std::is_same_v<T, uint32_t> ||\n                           (std::is_same_v<T, unsigned int> &&\n                            sizeof(unsigned int) == 4) ||\n                           (std::is_same_v<T, unsigned long> &&\n                            sizeof(unsigned long) == 4))\n        {\n            return MySqlULong;\n        }\n        else if constexpr (std::is_same_v<T, int64_t> ||\n                           std::is_same_v<T, long long> ||\n                           (std::is_same_v<T, long> && sizeof(long) == 8))\n        {\n            return MySqlLongLong;\n        }\n        else if constexpr (std::is_same_v<T, uint64_t> ||\n                           std::is_same_v<T, unsigned long long> ||\n                           (std::is_same_v<T, unsigned long> &&\n                            sizeof(unsigned long) == 8))\n        {\n            return MySqlULongLong;\n        }\n        else if constexpr (std::is_same_v<T, char>)\n        {\n            if constexpr (std::is_signed_v<char>)\n            {\n                return MySqlTiny;\n            }\n            else\n            {\n                return MySqlUTiny;\n            }\n        }\n        else\n        {\n            static_assert(sizeof(T) == 0, \"Unsupported type for MySQL binding\");\n        }\n    }\n\n    std::shared_ptr<std::string> sqlPtr_;\n    const char *sqlViewPtr_;\n    size_t sqlViewLength_;\n    DbClient &client_;\n    size_t parametersNumber_{0};\n    std::vector<const char *> parameters_;\n    std::vector<int> lengths_;\n    std::vector<int> formats_;\n    std::vector<std::shared_ptr<void>> objs_;\n    Mode mode_{Mode::NonBlocking};\n    std::shared_ptr<CallbackHolderBase> callbackHolder_;\n    DrogonDbExceptionCallback exceptionCallback_;\n    ExceptPtrCallback exceptionPtrCallback_;\n    bool execed_{false};\n    bool destructed_{false};\n    bool isExceptionPtr_{false};\n    ClientType type_;\n};\n\n}  // namespace internal\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/inc/drogon/orm/TransformBuilder.h",
    "content": "/**\n *\n *  @file TransformBuilder.h\n *  @author Ken Matsui\n *\n *  Copyright 2022, Ken Matsui.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/orm/BaseBuilder.h>\n#include <string>\n#include <type_traits>\n\nnamespace drogon\n{\nnamespace orm\n{\ntemplate <typename T, bool SelectAll, bool Single>\nclass TransformBuilder : public BaseBuilder<T, SelectAll, Single>\n{\n  public:\n    /**\n     * @brief A default constructor for derived classes.\n     *\n     * @return TransformBuilder The TransformBuilder itself.\n     */\n    TransformBuilder() = default;\n\n    /**\n     * @brief A copy constructor from a non `Single` builder to `Single`\n     * builder, used by the `single` method.\n     *\n     * @return TransformBuilder The TransformBuilder itself.\n     *\n     * @note This function is enabled only when `Single` is true.\n     */\n    template <bool SI = Single, std::enable_if_t<SI, std::nullptr_t> = nullptr>\n    TransformBuilder(const TransformBuilder<T, SelectAll, false> &tb)\n    {\n        this->from_ = tb.from_;\n        this->columns_ = tb.columns_;\n        this->filters_ = tb.filters_;\n        this->limit_ = tb.limit_;\n        this->offset_ = tb.offset_;\n        this->orders_ = tb.orders_;\n    }\n\n    /**\n     * @brief Limit the result to `count`.\n     *\n     * @param count The number of rows to be limited.\n     *\n     * @return TransformBuilder& The TransformBuilder itself.\n     */\n    inline TransformBuilder &limit(std::uint64_t count)\n    {\n        this->limit_ = count;\n        return *this;\n    }\n\n    /**\n     * @brief Add a offset to the query.\n     *\n     * @param offset The offset.\n     *\n     * @return TransformBuilder& The TransformBuilder itself.\n     */\n    inline TransformBuilder &offset(std::uint64_t count)\n    {\n        this->offset_ = count;\n        return *this;\n    }\n\n    /**\n     * @brief Limit the result to an inclusive range.\n     *\n     * @param from The first index to limit the result.\n     * @param to The last index to limit the result.\n     *\n     * @return TransformBuilder& The TransformBuilder itself.\n     */\n    inline TransformBuilder &range(std::uint64_t from, std::uint64_t to)\n    {\n        this->offset_ = from;\n        this->limit_ = to - from + 1;  // inclusive\n        return *this;\n    }\n\n    /**\n     * @brief Order the result.\n     *\n     * @param column The column to order by.\n     * @param asc If `true`, ascending order. If `false`, descending order.\n     *\n     * @return TransformBuilder& The TransformBuilder itself.\n     */\n    inline TransformBuilder &order(const std::string &column, bool asc = true)\n    {\n        this->assert_column(column);\n        this->orders_.emplace_back(column, asc);\n        return *this;\n    }\n\n    /**\n     * @brief Ensure returning only one row.\n     *\n     * @return TransformBuilder<T, SelectAll, true> The TransformBuilder where\n     * Single is true and all else is the same.\n     *\n     * @note This function can be called only once throughout an instance of a\n     * builder.\n     */\n    template <bool SI = Single, std::enable_if_t<!SI, std::nullptr_t> = nullptr>\n    inline TransformBuilder<T, SelectAll, true> single() const\n    {\n        return {*this};\n    }\n};\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/src/ArrayParser.cc",
    "content": "/** Handling of SQL arrays.\n *\n * Copyright (c) 2018, Jeroen T. Vermeulen.\n *\n * See COPYING for copyright license.  If you did not receive a file called\n * COPYING with this source code, please notify the distributor of this mistake,\n * or contact the author.\n */\n\n// Taken from libpqxx and modified\n\n/**\n *\n *  ArrayParser.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <cassert>\n#include <cstddef>\n#include <cstring>\n#include <utility>\n\n#include <drogon/orm/ArrayParser.h>\n#include <drogon/orm/Exception.h>\n\nusing namespace drogon::orm;\n\nnamespace\n{\n/// Find the end of a single-quoted SQL string in an SQL array.\n/** Assumes UTF-8 or an ASCII-superset single-byte encoding.\n * This is used for array parsing, where the database may send us strings\n * stored in the array, in a quoted and escaped format.\n *\n * Returns the address of the first character after the closing quote.\n */\nconst char *scan_single_quoted_string(const char begin[])\n{\n    const char *here = begin;\n    assert(*here == '\\'');\n    for (here++; *here; here++)\n    {\n        switch (*here)\n        {\n            case '\\'':\n                // Escaped quote, or closing quote.\n                here++;\n                // If the next character is a quote, we've got a double single\n                // quote. That's how SQL escapes embedded quotes in a string.\n                // Terrible idea, but it's what we have.\n                if (*here != '\\'')\n                    return here;\n                // Otherwise, we've found the end of the string.  Return the\n                // address of the very next byte.\n                break;\n            case '\\\\':\n                // Backslash escape.  Skip ahead by one more character.\n                here++;\n                if (!*here)\n                    throw ArgumentError(\"SQL string ends in escape: \" +\n                                        std::string(begin));\n                break;\n        }\n    }\n    throw ArgumentError(\"Null byte in SQL string: \" + std::string(begin));\n}\n\n/// Parse a single-quoted SQL string: un-quote it and un-escape it.\n/** Assumes UTF-8 or an ASCII-superset single-byte encoding.\n */\nstd::string parse_single_quoted_string(const char begin[], const char end[])\n{\n    // There have to be at least 2 characters: the opening and closing quotes.\n    assert(begin + 1 < end);\n    assert(*begin == '\\'');\n    assert(*(end - 1) == '\\'');\n\n    std::string output;\n    // Maximum output size is same as the input size, minus the opening and\n    // closing quotes.  In the worst case, the real number could be half that.\n    // Usually it'll be a pretty close estimate.\n    output.reserve(std::size_t(end - begin - 2));\n    for (const char *here = begin + 1; here < end - 1; here++)\n    {\n        auto c = *here;\n        // Skip escapes.\n        if (c == '\\'' || c == '\\\\')\n            here++;\n        output.push_back(c);\n    }\n\n    return output;\n}\n\n/// Find the end of a double-quoted SQL string in an SQL array.\n/** Assumes UTF-8 or an ASCII-superset single-byte encoding.\n */\nconst char *scan_double_quoted_string(const char begin[])\n{\n    const char *here = begin;\n    assert(*here == '\"');\n    for (here++; *here; here++)\n    {\n        switch (*here)\n        {\n            case '\\\\':\n                // Backslash escape.  Skip ahead by one more character.\n                here++;\n                if (!*here)\n                    throw ArgumentError(\"SQL string ends in escape: \" +\n                                        std::string(begin));\n                break;\n            case '\"':\n                return here + 1;\n        }\n    }\n    throw ArgumentError(\"Null byte in SQL string: \" + std::string(begin));\n}\n\n/// Parse a double-quoted SQL string: un-quote it and un-escape it.\n/** Assumes UTF-8 or an ASCII-superset single-byte encoding.\n */\nstd::string parse_double_quoted_string(const char begin[], const char end[])\n{\n    // There have to be at least 2 characters: the opening and closing quotes.\n    assert(begin + 1 < end);\n    assert(*begin == '\"');\n    assert(*(end - 1) == '\"');\n\n    std::string output;\n    // Maximum output size is same as the input size, minus the opening and\n    // closing quotes.  In the worst case, the real number could be half that.\n    // Usually it'll be a pretty close estimate.\n    output.reserve(std::size_t(end - begin - 2));\n    for (const char *here = begin + 1; here < end - 1; here++)\n    {\n        auto c = *here;\n        // Skip escapes.\n        if (c == '\\\\')\n            here++;\n        output.push_back(c);\n    }\n\n    return output;\n}\n\n/// Find the end of an unquoted string in an SQL array.\n/** Assumes UTF-8 or an ASCII-superset single-byte encoding.\n */\nconst char *scan_unquoted_string(const char begin[])\n{\n    assert(*begin != '\\'');\n    assert(*begin != '\"');\n\n    const char *p;\n    for (p = begin; *p != ',' && *p != ';' && *p != '}'; p++)\n        ;\n    return p;\n}\n\n/// Parse an unquoted SQL string.\n/** Here, the special unquoted value NULL means a null value, not a string\n * that happens to spell \"NULL\".\n */\nstd::string parse_unquoted_string(const char begin[], const char end[])\n{\n    return std::string(begin, end);\n}\n\n}  // namespace\n\nArrayParser::ArrayParser(const char input[]) : pos_(input)\n{\n}\n\nstd::pair<ArrayParser::juncture, std::string> ArrayParser::getNext()\n{\n    ArrayParser::juncture found;\n    std::string value;\n    const char *end;\n\n    if (pos_ == nullptr)\n    {\n        found = juncture::done;\n        end = nullptr;\n    }\n    else\n        switch (*pos_)\n        {\n            case '\\0':\n                found = juncture::done;\n                end = pos_;\n                break;\n            case '{':\n                found = juncture::row_start;\n                end = pos_ + 1;\n                break;\n            case '}':\n                found = juncture::row_end;\n                end = pos_ + 1;\n                break;\n            case '\\'':\n                found = juncture::string_value;\n                end = scan_single_quoted_string(pos_);\n                value = parse_single_quoted_string(pos_, end);\n                break;\n            case '\"':\n                found = juncture::string_value;\n                end = scan_double_quoted_string(pos_);\n                value = parse_double_quoted_string(pos_, end);\n                break;\n            default:\n                end = scan_unquoted_string(pos_);\n                value = parse_unquoted_string(pos_, end);\n                if (value == \"NULL\")\n                {\n                    // In this one situation, as a special case, NULL means a\n                    // null field, not a string that happens to spell \"NULL\".\n                    value.clear();\n                    found = juncture::null_value;\n                }\n                else\n                {\n                    // The normal case: we just parsed an unquoted string.  The\n                    // value is what we need.\n                    found = juncture::string_value;\n                }\n                break;\n        }\n\n    // Skip a field separator following a string (or null).\n    if (end != nullptr && (*end == ',' || *end == ';'))\n        end++;\n\n    pos_ = end;\n    return std::make_pair(found, value);\n}\n"
  },
  {
    "path": "orm_lib/src/Criteria.cc",
    "content": "/**\n *\n *  @file Criteria.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/orm/Criteria.h>\n#include <json/json.h>\n\nnamespace drogon\n{\nnamespace orm\n{\nconst Criteria operator&&(Criteria cond1, Criteria cond2)\n{\n    bool cond1valid = (bool)cond1, cond2valid = (bool)cond2;\n    assert(cond1valid || cond2valid);\n    if (cond1valid && !cond2valid)\n    {\n        return cond1;\n    }\n    if (!cond1valid && cond2valid)\n    {\n        return cond2;\n    }\n    Criteria cond;\n    cond.conditionString_ = \"( \";\n    cond.conditionString_ += cond1.conditionString_;\n    cond.conditionString_ += \" ) and ( \";\n    cond.conditionString_ += cond2.conditionString_;\n    cond.conditionString_ += \" )\";\n    auto cond1Ptr = std::make_shared<Criteria>(std::move(cond1));\n    auto cond2Ptr = std::make_shared<Criteria>(std::move(cond2));\n    cond.outputArgumentsFunc_ = [cond1Ptr,\n                                 cond2Ptr](internal::SqlBinder &binder) {\n        if (cond1Ptr->outputArgumentsFunc_)\n        {\n            cond1Ptr->outputArgumentsFunc_(binder);\n        }\n        if (cond2Ptr->outputArgumentsFunc_)\n        {\n            cond2Ptr->outputArgumentsFunc_(binder);\n        }\n    };\n    return cond;\n}\n\nconst Criteria operator||(Criteria cond1, Criteria cond2)\n{\n    bool cond1valid = (bool)cond1, cond2valid = (bool)cond2;\n    assert(cond1valid || cond2valid);\n    if (cond1valid && !cond2valid)\n    {\n        return cond1;\n    }\n    if (!cond1valid && cond2valid)\n    {\n        return cond2;\n    }\n    Criteria cond;\n    cond.conditionString_ = \"( \";\n    cond.conditionString_ += cond1.conditionString_;\n    cond.conditionString_ += \" ) or ( \";\n    cond.conditionString_ += cond2.conditionString_;\n    cond.conditionString_ += \" )\";\n    auto cond1Ptr = std::make_shared<Criteria>(std::move(cond1));\n    auto cond2Ptr = std::make_shared<Criteria>(std::move(cond2));\n    cond.outputArgumentsFunc_ = [cond1Ptr,\n                                 cond2Ptr](internal::SqlBinder &binder) {\n        if (cond1Ptr->outputArgumentsFunc_)\n        {\n            cond1Ptr->outputArgumentsFunc_(binder);\n        }\n        if (cond2Ptr->outputArgumentsFunc_)\n        {\n            cond2Ptr->outputArgumentsFunc_(binder);\n        }\n    };\n    return cond;\n}\n\nCriteria::Criteria(const Json::Value &json) noexcept(false)\n{\n    if (!json.isArray() || json.size() != 3)\n    {\n        throw std::runtime_error(\"Json format error\");\n    }\n    if (!json[0].isString() || !json[1].isString())\n    {\n        throw std::runtime_error(\"Json format error\");\n    }\n    conditionString_ = json[0].asString();\n    if (!json[2].isNull() && !json[2].isArray())\n    {\n        if (json[1].asString() == \"in\")\n        {\n            throw std::runtime_error(\"Json format error\");\n        }\n        conditionString_.append(json[1].asString());\n        conditionString_.append(\"$?\");\n        outputArgumentsFunc_ =\n            [arg = json[2].asString()](internal::SqlBinder &binder) {\n                binder << arg;\n            };\n    }\n    else if (json[2].isNull())\n    {\n        if (json[1].asString() == \"=\")\n        {\n            conditionString_.append(\" is null\");\n        }\n        else if (json[1].asString() == \"!=\")\n        {\n            conditionString_.append(\" is not null\");\n        }\n        else\n        {\n            throw std::runtime_error(\"Json format error\");\n        }\n    }\n    else\n    {\n        assert(json[2].isArray());\n        if (json[1].asString() != \"in\")\n        {\n            throw std::runtime_error(\"Json format error\");\n        }\n        conditionString_.append(\" in (\");\n        for (size_t i = 0; i < json[2].size(); ++i)\n        {\n            if (i < json[2].size() - 1)\n            {\n                conditionString_.append(\"$?,\");\n            }\n            else\n            {\n                conditionString_.append(\"$?\");\n            }\n        }\n        conditionString_.append(1, ')');\n        outputArgumentsFunc_ = [args = json[2]](internal::SqlBinder &binder) {\n            for (auto &arg : args)\n            {\n                binder << arg.asString();\n            }\n        };\n    }\n}\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/src/DbClient.cc",
    "content": "/**\n *\n *  DbClient.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"DbClientImpl.h\"\n#include <drogon/config.h>\n#include <drogon/orm/DbClient.h>\nusing namespace drogon::orm;\nusing namespace drogon;\n\nDbClient::~DbClient() = default;\n\norm::internal::SqlBinder DbClient::operator<<(const std::string &sql)\n{\n    return orm::internal::SqlBinder(sql, *this, type_);\n}\n\norm::internal::SqlBinder DbClient::operator<<(std::string &&sql)\n{\n    return orm::internal::SqlBinder(std::move(sql), *this, type_);\n}\n\nstd::shared_ptr<DbClient> DbClient::newPgClient(const std::string &connInfo,\n                                                size_t connNum,\n                                                bool autoBatch)\n{\n#if USE_POSTGRESQL\n    auto client = std::make_shared<DbClientImpl>(connInfo,\n                                                 connNum,\n#if LIBPQ_SUPPORTS_BATCH_MODE\n                                                 ClientType::PostgreSQL,\n                                                 autoBatch);\n#else\n                                                 ClientType::PostgreSQL);\n#endif\n    client->init();\n    return client;\n#else\n    LOG_FATAL << \"PostgreSQL is not supported!\";\n    exit(1);\n    (void)(connInfo);\n    (void)(connNum);\n#endif\n}\n\nstd::shared_ptr<DbClient> DbClient::newMysqlClient(const std::string &connInfo,\n                                                   size_t connNum)\n{\n#if USE_MYSQL\n    auto client = std::make_shared<DbClientImpl>(connInfo,\n                                                 connNum,\n#if LIBPQ_SUPPORTS_BATCH_MODE\n                                                 ClientType::Mysql,\n                                                 false);\n#else\n                                                 ClientType::Mysql);\n#endif\n    client->init();\n    return client;\n#else\n    LOG_FATAL << \"Mysql is not supported!\";\n    exit(1);\n    (void)(connInfo);\n    (void)(connNum);\n#endif\n}\n\nstd::shared_ptr<DbClient> DbClient::newSqlite3Client(\n    const std::string &connInfo,\n    size_t connNum)\n{\n#if USE_SQLITE3\n    auto client = std::make_shared<DbClientImpl>(connInfo,\n                                                 connNum,\n#if LIBPQ_SUPPORTS_BATCH_MODE\n                                                 ClientType::Sqlite3,\n                                                 false);\n#else\n                                                 ClientType::Sqlite3);\n#endif\n    client->init();\n    return client;\n#else\n    LOG_FATAL << \"Sqlite3 is not supported!\";\n    exit(1);\n    (void)(connInfo);\n    (void)(connNum);\n#endif\n}\n"
  },
  {
    "path": "orm_lib/src/DbClientImpl.cc",
    "content": "/**\n *\n *  @file DbClientImpl.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"DbClientImpl.h\"\n#include \"DbConnection.h\"\n#include \"../../lib/src/TaskTimeoutFlag.h\"\n#include <drogon/config.h>\n#include <string_view>\n#if USE_POSTGRESQL\n#include \"postgresql_impl/PgConnection.h\"\n#endif\n#if USE_MYSQL\n#include \"mysql_impl/MysqlConnection.h\"\n#endif\n#if USE_SQLITE3\n#include \"sqlite3_impl/Sqlite3Connection.h\"\n#endif\n#include \"TransactionImpl.h\"\n#include <drogon/drogon.h>\n#include <drogon/orm/DbClient.h>\n#include <drogon/orm/Exception.h>\n#include <iostream>\n#include <memory>\n#include <sstream>\n#include <stdio.h>\n#ifndef _WIN32\n#include <sys/select.h>\n#include <unistd.h>\n#endif\n#include <thread>\n#include <trantor/net/EventLoop.h>\n#include <trantor/net/Channel.h>\n#include <unordered_set>\n#include <vector>\n\nusing namespace drogon;\nusing namespace drogon::orm;\n\nDbClientImpl::DbClientImpl(const std::string &connInfo,\n                           size_t connNum,\n#if LIBPQ_SUPPORTS_BATCH_MODE\n                           ClientType type,\n                           bool autoBatch)\n#else\n                           ClientType type)\n#endif\n    : numberOfConnections_(connNum),\n#if LIBPQ_SUPPORTS_BATCH_MODE\n      autoBatch_(autoBatch),\n#endif\n      loops_(type == ClientType::Sqlite3\n                 ? 1\n                 : (connNum < std::thread::hardware_concurrency()\n                        ? connNum\n                        : std::thread::hardware_concurrency()),\n             \"DbLoop\")\n{\n    type_ = type;\n    connectionInfo_ = connInfo;\n    LOG_TRACE << \"type=\" << (int)type;\n    assert(connNum > 0);\n}\n\nvoid DbClientImpl::init()\n{\n    // LOG_DEBUG << loops_.getLoopNum();\n    loops_.start();\n    if (type_ == ClientType::PostgreSQL || type_ == ClientType::Mysql)\n    {\n        for (size_t i = 0; i < numberOfConnections_; ++i)\n        {\n            auto loop = loops_.getNextLoop();\n            loop->runInLoop([this, loop]() { newConnection(loop); });\n        }\n    }\n    else if (type_ == ClientType::Sqlite3)\n    {\n        sharedMutexPtr_ = std::make_shared<SharedMutex>();\n        assert(sharedMutexPtr_);\n\n        for (size_t i = 0; i < numberOfConnections_; ++i)\n        {\n            newConnection(nullptr);\n        }\n    }\n}\n\nDbClientImpl::~DbClientImpl() noexcept\n{\n    closeAll();\n}\n\nvoid DbClientImpl::closeAll()\n{\n    decltype(connections_) connections;\n    {\n        std::lock_guard<std::mutex> lock(connectionsMutex_);\n        connections.swap(connections_);\n        readyConnections_.clear();\n        busyConnections_.clear();\n    }\n    for (auto const &conn : connections)\n    {\n        conn->disconnect();\n    }\n}\n\nvoid DbClientImpl::execSql(\n    const char *sql,\n    size_t sqlLength,\n    size_t paraNum,\n    std::vector<const char *> &&parameters,\n    std::vector<int> &&length,\n    std::vector<int> &&format,\n    ResultCallback &&rcb,\n    std::function<void(const std::exception_ptr &)> &&exceptCallback)\n{\n    assert(paraNum == parameters.size());\n    assert(paraNum == length.size());\n    assert(paraNum == format.size());\n    assert(rcb);\n    if (timeout_ > 0.0)\n    {\n        execSqlWithTimeout(sql,\n                           sqlLength,\n                           paraNum,\n                           std::move(parameters),\n                           std::move(length),\n                           std::move(format),\n                           std::move(rcb),\n                           std::move(exceptCallback));\n        return;\n    }\n    DbConnectionPtr conn;\n    bool busy = false;\n    {\n        std::lock_guard<std::mutex> guard(connectionsMutex_);\n\n        if (readyConnections_.size() == 0)\n        {\n            if (sqlCmdBuffer_.size() > 200000)\n            {\n                // too many queries in buffer;\n                busy = true;\n            }\n            else\n            {\n                // LOG_TRACE << \"Push query to buffer\";\n                std::shared_ptr<SqlCmd> cmd =\n                    std::make_shared<SqlCmd>(std::string_view{sql, sqlLength},\n                                             paraNum,\n                                             std::move(parameters),\n                                             std::move(length),\n                                             std::move(format),\n                                             std::move(rcb),\n                                             std::move(exceptCallback));\n                sqlCmdBuffer_.push_back(std::move(cmd));\n            }\n        }\n        else\n        {\n            auto iter = readyConnections_.begin();\n            busyConnections_.insert(*iter);\n            conn = *iter;\n            readyConnections_.erase(iter);\n        }\n    }\n    if (conn)\n    {\n        conn->execSql({sql, sqlLength},\n                      paraNum,\n                      std::move(parameters),\n                      std::move(length),\n                      std::move(format),\n                      std::move(rcb),\n                      std::move(exceptCallback));\n        return;\n    }\n    if (busy)\n    {\n        auto exceptPtr =\n            std::make_exception_ptr(Failure(\"Too many queries in buffer\"));\n        exceptCallback(exceptPtr);\n        return;\n    }\n}\n\nvoid DbClientImpl::newTransactionAsync(\n    const std::function<void(const std::shared_ptr<Transaction> &)> &callback)\n{\n    DbConnectionPtr conn;\n    {\n        std::lock_guard<std::mutex> lock(connectionsMutex_);\n        if (!readyConnections_.empty())\n        {\n            auto iter = readyConnections_.begin();\n            busyConnections_.insert(*iter);\n            conn = *iter;\n            readyConnections_.erase(iter);\n        }\n        else\n        {\n            auto callbackPtr = std::make_shared<\n                std::function<void(const std::shared_ptr<Transaction> &)>>(\n                callback);\n            if (timeout_ > 0.0)\n            {\n                auto newCallbackPtr =\n                    std::make_shared<std::weak_ptr<std::function<void(\n                        const std::shared_ptr<Transaction> &)>>>();\n                auto timeoutFlagPtr = std::make_shared<TaskTimeoutFlag>(\n                    loops_.getNextLoop(),\n                    std::chrono::duration<double>(timeout_),\n                    [newCallbackPtr, callbackPtr, this]() {\n                        auto cbPtr = (*newCallbackPtr).lock();\n                        if (cbPtr)\n                        {\n                            std::lock_guard<std::mutex> lock(connectionsMutex_);\n                            for (auto iter = transCallbacks_.begin();\n                                 iter != transCallbacks_.end();\n                                 ++iter)\n                            {\n                                if (cbPtr == *iter)\n                                {\n                                    transCallbacks_.erase(iter);\n                                    break;\n                                }\n                            }\n                        }\n                        (*callbackPtr)(nullptr);\n                    });\n                callbackPtr = std::make_shared<\n                    std::function<void(const std::shared_ptr<Transaction> &)>>(\n                    [callbackPtr, timeoutFlagPtr](\n                        const std::shared_ptr<Transaction> &trans) {\n                        if (timeoutFlagPtr->done())\n                            return;\n                        (*callbackPtr)(trans);\n                    });\n                (*newCallbackPtr) = callbackPtr;\n                timeoutFlagPtr->runTimer();\n            }\n            transCallbacks_.push_back(callbackPtr);\n        }\n    }\n    if (conn)\n    {\n        makeTrans(conn,\n                  std::function<void(const std::shared_ptr<Transaction> &)>(\n                      callback));\n    }\n}\n\nvoid DbClientImpl::makeTrans(\n    const DbConnectionPtr &conn,\n    std::function<void(const std::shared_ptr<Transaction> &)> &&callback)\n{\n    std::weak_ptr<DbClientImpl> weakThis = shared_from_this();\n    auto trans = std::make_shared<TransactionImpl>(\n        type_, conn, std::function<void(bool)>(), [weakThis, conn]() {\n            auto thisPtr = weakThis.lock();\n            if (!thisPtr)\n                return;\n            if (conn->status() == ConnectStatus::Bad)\n            {\n                return;\n            }\n            {\n                std::lock_guard<std::mutex> guard(thisPtr->connectionsMutex_);\n                if (thisPtr->connections_.find(conn) ==\n                    thisPtr->connections_.end())\n                {\n                    // connection is broken and removed\n                    assert(thisPtr->busyConnections_.find(conn) ==\n                               thisPtr->busyConnections_.end() &&\n                           thisPtr->readyConnections_.find(conn) ==\n                               thisPtr->readyConnections_.end());\n\n                    return;\n                }\n            }\n            conn->loop()->queueInLoop([weakThis, conn]() {\n                auto thisPtr = weakThis.lock();\n                if (!thisPtr)\n                    return;\n                std::weak_ptr<DbConnection> weakConn = conn;\n                conn->setIdleCallback([weakThis, weakConn]() {\n                    auto thisPtr = weakThis.lock();\n                    if (!thisPtr)\n                        return;\n                    auto connPtr = weakConn.lock();\n                    if (!connPtr)\n                        return;\n                    thisPtr->handleNewTask(connPtr);\n                });\n                thisPtr->handleNewTask(conn);\n            });\n        });\n    trans->doBegin();\n    if (timeout_ > 0.0)\n    {\n        trans->setTimeout(timeout_);\n    }\n    conn->loop()->queueInLoop(\n        [callback = std::move(callback), trans]() { callback(trans); });\n}\n\nstd::shared_ptr<Transaction> DbClientImpl::newTransaction(\n    const std::function<void(bool)> &commitCallback) noexcept(false)\n{\n    std::promise<std::shared_ptr<Transaction>> pro;\n    auto f = pro.get_future();\n    newTransactionAsync([&pro](const std::shared_ptr<Transaction> &trans) {\n        pro.set_value(trans);\n    });\n    auto trans = f.get();\n    if (!trans)\n    {\n        throw TimeoutError(\"Timeout, no connection available for transaction\");\n    }\n    trans->setCommitCallback(commitCallback);\n    return trans;\n}\n\nvoid DbClientImpl::handleNewTask(const DbConnectionPtr &connPtr)\n{\n    std::function<void(const std::shared_ptr<Transaction> &)> transCallback;\n    std::shared_ptr<SqlCmd> cmd;\n    {\n        std::lock_guard<std::mutex> guard(connectionsMutex_);\n        if (!transCallbacks_.empty())\n        {\n            transCallback = std::move(*(transCallbacks_.front()));\n            transCallbacks_.pop_front();\n        }\n        else if (!sqlCmdBuffer_.empty())\n        {\n            cmd = std::move(sqlCmdBuffer_.front());\n            sqlCmdBuffer_.pop_front();\n        }\n        else\n        {\n            // Connection is idle, put it into the readyConnections_ set;\n            busyConnections_.erase(connPtr);\n            readyConnections_.insert(connPtr);\n        }\n    }\n    if (transCallback)\n    {\n        makeTrans(connPtr, std::move(transCallback));\n        return;\n    }\n    if (cmd)\n    {\n        connPtr->execSql(std::move(cmd->sql_),\n                         cmd->parametersNumber_,\n                         std::move(cmd->parameters_),\n                         std::move(cmd->lengths_),\n                         std::move(cmd->formats_),\n                         std::move(cmd->callback_),\n                         std::move(cmd->exceptionCallback_));\n        return;\n    }\n}\n\nDbConnectionPtr DbClientImpl::newConnection(trantor::EventLoop *loop)\n{\n    DbConnectionPtr connPtr;\n    if (type_ == ClientType::PostgreSQL)\n    {\n#if USE_POSTGRESQL\n#if LIBPQ_SUPPORTS_BATCH_MODE\n        connPtr =\n            std::make_shared<PgConnection>(loop, connectionInfo_, autoBatch_);\n#else\n        connPtr = std::make_shared<PgConnection>(loop, connectionInfo_, false);\n#endif\n#else\n        return nullptr;\n#endif\n    }\n    else if (type_ == ClientType::Mysql)\n    {\n#if USE_MYSQL\n        connPtr = std::make_shared<MysqlConnection>(loop, connectionInfo_);\n#else\n        return nullptr;\n#endif\n    }\n    else if (type_ == ClientType::Sqlite3)\n    {\n#if USE_SQLITE3\n        connPtr = std::make_shared<Sqlite3Connection>(loop,\n                                                      connectionInfo_,\n                                                      sharedMutexPtr_);\n#else\n        return nullptr;\n#endif\n    }\n    else\n    {\n        return nullptr;\n        (void)(loop);\n    }\n    std::weak_ptr<DbClientImpl> weakPtr = shared_from_this();\n    connPtr->setCloseCallback([weakPtr](const DbConnectionPtr &closeConnPtr) {\n        // Erase the connection\n        auto thisPtr = weakPtr.lock();\n        if (!thisPtr)\n            return;\n        {\n            std::lock_guard<std::mutex> guard(thisPtr->connectionsMutex_);\n            thisPtr->readyConnections_.erase(closeConnPtr);\n            thisPtr->busyConnections_.erase(closeConnPtr);\n            assert(thisPtr->connections_.find(closeConnPtr) !=\n                   thisPtr->connections_.end());\n            thisPtr->connections_.erase(closeConnPtr);\n        }\n        // Reconnect after 1 second\n        auto loop = closeConnPtr->loop();\n        // closeConnPtr may be not valid. Close the connection file descriptor.\n        closeConnPtr->disconnect();\n        loop->runAfter(1, [weakPtr, loop, closeConnPtr] {\n            auto thisPtr = weakPtr.lock();\n            if (!thisPtr)\n                return;\n\n            thisPtr->newConnection(loop);\n        });\n    });\n    connPtr->setOkCallback([weakPtr](const DbConnectionPtr &okConnPtr) {\n        LOG_TRACE << \"connected!\";\n        auto thisPtr = weakPtr.lock();\n        if (!thisPtr)\n            return;\n        {\n            std::lock_guard<std::mutex> guard(thisPtr->connectionsMutex_);\n            thisPtr->busyConnections_.insert(\n                okConnPtr);  // For new connections, this sentence is\n                             // necessary\n        }\n        thisPtr->handleNewTask(okConnPtr);\n    });\n    std::weak_ptr<DbConnection> weakConn = connPtr;\n    connPtr->setIdleCallback([weakPtr, weakConn]() {\n        auto thisPtr = weakPtr.lock();\n        if (!thisPtr)\n            return;\n        auto connPtr = weakConn.lock();\n        if (!connPtr)\n            return;\n        thisPtr->handleNewTask(connPtr);\n    });\n\n    {\n        std::lock_guard<std::mutex> guard(connectionsMutex_);\n        connections_.insert(connPtr);\n    }\n\n    // Init database connection only after all callbacks are set and connPtr\n    // is added to connections_.\n    connPtr->init();\n\n    // std::cout<<\"newConn end\"<<connPtr<<std::endl;\n    return connPtr;\n}\n\nbool DbClientImpl::hasAvailableConnections() const noexcept\n{\n    std::lock_guard<std::mutex> lock(connectionsMutex_);\n    return (!readyConnections_.empty()) || (!busyConnections_.empty());\n}\n\nvoid DbClientImpl::execSqlWithTimeout(\n    const char *sql,\n    size_t sqlLength,\n    size_t paraNum,\n    std::vector<const char *> &&parameters,\n    std::vector<int> &&length,\n    std::vector<int> &&format,\n    ResultCallback &&rcb,\n    std::function<void(const std::exception_ptr &)> &&ecb)\n{\n    DbConnectionPtr conn;\n    assert(timeout_ > 0.0);\n    auto cmd = std::make_shared<std::weak_ptr<SqlCmd>>();\n    bool busy = false;\n    auto ecpPtr =\n        std::make_shared<std::function<void(const std::exception_ptr &)>>(\n            std::move(ecb));\n    auto timeoutFlagPtr = std::make_shared<drogon::TaskTimeoutFlag>(\n        loops_.getNextLoop(),\n        std::chrono::duration<double>(timeout_),\n        [cmd, ecpPtr, thisPtr = shared_from_this()]() {\n            auto cbPtr = (*cmd).lock();\n            if (cbPtr)\n            {\n                std::lock_guard<std::mutex> lock(thisPtr->connectionsMutex_);\n                for (auto iter = thisPtr->sqlCmdBuffer_.begin();\n                     iter != thisPtr->sqlCmdBuffer_.end();\n                     ++iter)\n                {\n                    if (*iter == cbPtr)\n                    {\n                        thisPtr->sqlCmdBuffer_.erase(iter);\n                        break;\n                    }\n                }\n            }\n            (*ecpPtr)(\n                std::make_exception_ptr(TimeoutError(\"SQL execution timeout\")));\n        });\n    auto resultCallback = [rcb = std::move(rcb),\n                           timeoutFlagPtr](const Result &result) {\n        if (timeoutFlagPtr->done())\n            return;\n        rcb(result);\n    };\n\n    auto exceptionCallback = [ecpPtr,\n                              timeoutFlagPtr](const std::exception_ptr &err) {\n        if (timeoutFlagPtr->done())\n            return;\n        (*ecpPtr)(err);\n    };\n\n    {\n        std::lock_guard<std::mutex> guard(connectionsMutex_);\n\n        if (readyConnections_.size() == 0)\n        {\n            if (sqlCmdBuffer_.size() > 200000)\n            {\n                // too many queries in buffer;\n                busy = true;\n            }\n            else\n            {\n                // LOG_TRACE << \"Push query to buffer\";\n                auto command =\n                    std::make_shared<SqlCmd>(std::string_view{sql, sqlLength},\n                                             paraNum,\n                                             std::move(parameters),\n                                             std::move(length),\n                                             std::move(format),\n                                             std::move(resultCallback),\n                                             std::move(exceptionCallback));\n                sqlCmdBuffer_.emplace_back(command);\n                *cmd = command;\n            }\n        }\n        else\n        {\n            auto iter = readyConnections_.begin();\n            busyConnections_.insert(*iter);\n            conn = *iter;\n            readyConnections_.erase(iter);\n        }\n    }\n    if (conn)\n    {\n        conn->execSql(std::string_view{sql, sqlLength},\n                      paraNum,\n                      std::move(parameters),\n                      std::move(length),\n                      std::move(format),\n                      std::move(resultCallback),\n                      std::move(exceptionCallback));\n        timeoutFlagPtr->runTimer();\n        return;\n    }\n\n    if (busy)\n    {\n        exceptionCallback(\n            std::make_exception_ptr(Failure(\"Too many queries in buffer\")));\n        return;\n    }\n\n    timeoutFlagPtr->runTimer();\n}\n"
  },
  {
    "path": "orm_lib/src/DbClientImpl.h",
    "content": "/**\n *\n *  @file DbClientImpl.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"DbConnection.h\"\n#include <drogon/orm/DbClient.h>\n#include <trantor/net/EventLoopThreadPool.h>\n#include <functional>\n#include <list>\n#include <memory>\n#include <string>\n#include <thread>\n#include <unordered_set>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClientImpl : public DbClient,\n                     public std::enable_shared_from_this<DbClientImpl>\n{\n  public:\n    DbClientImpl(const std::string &connInfo,\n                 size_t connNum,\n#if LIBPQ_SUPPORTS_BATCH_MODE\n                 ClientType type,\n                 bool autoBatch);\n#else\n                 ClientType type);\n#endif\n    ~DbClientImpl() noexcept override;\n    void execSql(const char *sql,\n                 size_t sqlLength,\n                 size_t paraNum,\n                 std::vector<const char *> &&parameters,\n                 std::vector<int> &&length,\n                 std::vector<int> &&format,\n                 ResultCallback &&rcb,\n                 std::function<void(const std::exception_ptr &)>\n                     &&exceptCallback) override;\n    std::shared_ptr<Transaction> newTransaction(\n        const std::function<void(bool)> &commitCallback =\n            std::function<void(bool)>()) noexcept(false) override;\n    void newTransactionAsync(\n        const std::function<void(const std::shared_ptr<Transaction> &)>\n            &callback) override;\n    bool hasAvailableConnections() const noexcept override;\n\n    void setTimeout(double timeout) override\n    {\n        timeout_ = timeout;\n    }\n\n    void init();\n    void closeAll() override;\n\n  private:\n    size_t numberOfConnections_;\n    trantor::EventLoopThreadPool loops_;\n    std::shared_ptr<SharedMutex> sharedMutexPtr_;\n    double timeout_{-1.0};\n#if LIBPQ_SUPPORTS_BATCH_MODE\n    bool autoBatch_{false};\n#endif\n    DbConnectionPtr newConnection(trantor::EventLoop *loop);\n\n    void makeTrans(\n        const DbConnectionPtr &conn,\n        std::function<void(const std::shared_ptr<Transaction> &)> &&callback);\n\n    mutable std::mutex connectionsMutex_;\n    std::unordered_set<DbConnectionPtr> connections_;\n    std::unordered_set<DbConnectionPtr> readyConnections_;\n    std::unordered_set<DbConnectionPtr> busyConnections_;\n\n    std::list<std::shared_ptr<\n        std::function<void(const std::shared_ptr<Transaction> &)>>>\n        transCallbacks_;\n\n    std::deque<std::shared_ptr<SqlCmd>> sqlCmdBuffer_;\n\n    void handleNewTask(const DbConnectionPtr &connPtr);\n    void execSqlWithTimeout(\n        const char *sql,\n        size_t sqlLength,\n        size_t paraNum,\n        std::vector<const char *> &&parameters,\n        std::vector<int> &&length,\n        std::vector<int> &&format,\n        ResultCallback &&rcb,\n        std::function<void(const std::exception_ptr &)> &&exceptCallback);\n};\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/src/DbClientLockFree.cc",
    "content": "/**\n *\n *  DbClientLockFree.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"DbClientLockFree.h\"\n#include \"DbConnection.h\"\n#include \"TransactionImpl.h\"\n#include \"../../lib/src/TaskTimeoutFlag.h\"\n#include <drogon/config.h>\n#include <drogon/drogon.h>\n#include <drogon/orm/DbClient.h>\n#include <drogon/orm/Exception.h>\n#include <trantor/net/EventLoop.h>\n#include <trantor/net/Channel.h>\n#include <exception>\n#include <memory>\n#include <thread>\n#include <vector>\n#include <unordered_set>\n\n#if USE_POSTGRESQL\n#include \"postgresql_impl/PgConnection.h\"\n#endif\n#if USE_MYSQL\n#include \"mysql_impl/MysqlConnection.h\"\n#endif\n\n#ifndef _WIN32\n#include <unistd.h>\n#endif\n\nusing namespace drogon::orm;\n\nDbClientLockFree::DbClientLockFree(const std::string &connInfo,\n                                   trantor::EventLoop *loop,\n                                   ClientType type,\n#if LIBPQ_SUPPORTS_BATCH_MODE\n                                   size_t connectionNumberPerLoop,\n                                   bool autoBatch)\n#else\n                                   size_t connectionNumberPerLoop)\n#endif\n    : connectionInfo_(connInfo),\n      loop_(loop),\n#if LIBPQ_SUPPORTS_BATCH_MODE\n      autoBatch_(autoBatch),\n#endif\n      numberOfConnections_(connectionNumberPerLoop)\n{\n    type_ = type;\n    LOG_TRACE << \"type=\" << (int)type;\n    if (type == ClientType::PostgreSQL || type == ClientType::Mysql)\n    {\n        loop_->queueInLoop([this]() {\n            for (size_t i = 0; i < numberOfConnections_; ++i)\n                newConnection();\n        });\n    }\n    else\n    {\n        LOG_ERROR << \"No supported database type:\" << (int)type;\n    }\n}\n\nDbClientLockFree::~DbClientLockFree() noexcept\n{\n    closeAll();\n}\n\nvoid DbClientLockFree::closeAll()\n{\n    for (auto &conn : connections_)\n    {\n        conn->disconnect();\n    }\n    connections_.clear();\n}\n\nvoid DbClientLockFree::execSql(\n    const char *sql,\n    size_t sqlLength,\n    size_t paraNum,\n    std::vector<const char *> &&parameters,\n    std::vector<int> &&length,\n    std::vector<int> &&format,\n    ResultCallback &&rcb,\n    std::function<void(const std::exception_ptr &)> &&exceptCallback)\n{\n    assert(paraNum == parameters.size());\n    assert(paraNum == length.size());\n    assert(paraNum == format.size());\n    assert(rcb);\n    loop_->assertInLoopThread();\n    if (timeout_ > 0.0)\n    {\n        execSqlWithTimeout(sql,\n                           sqlLength,\n                           paraNum,\n                           std::move(parameters),\n                           std::move(length),\n                           std::move(format),\n                           std::move(rcb),\n                           std::move(exceptCallback));\n        return;\n    }\n    if (!connections_.empty() && sqlCmdBuffer_.empty() &&\n        transCallbacks_.empty())\n    {\n#if (!LIBPQ_SUPPORTS_BATCH_MODE)\n        for (auto &conn : connections_)\n        {\n            if (!conn->isWorking() &&\n                (transSet_.empty() || transSet_.find(conn) == transSet_.end()))\n            {\n                conn->execSql(\n                    std::string_view{sql, sqlLength},\n                    paraNum,\n                    std::move(parameters),\n                    std::move(length),\n                    std::move(format),\n                    [rcb = std::move(rcb), this](const Result &r) {\n                        if (sqlCmdBuffer_.empty())\n                        {\n                            rcb(r);\n                        }\n                        else\n                        {\n                            loop_->queueInLoop(\n                                [rcb = std::move(rcb), r]() { rcb(r); });\n                        }\n                    },\n                    std::move(exceptCallback));\n                return;\n            }\n        }\n#else\n        if (type_ != ClientType::PostgreSQL)\n        {\n            for (auto &conn : connections_)\n            {\n                if (!conn->isWorking() &&\n                    (transSet_.empty() ||\n                     transSet_.find(conn) == transSet_.end()))\n                {\n                    conn->execSql(\n                        std::string_view{sql, sqlLength},\n                        paraNum,\n                        std::move(parameters),\n                        std::move(length),\n                        std::move(format),\n                        [rcb = std::move(rcb), this](const Result &r) {\n                            if (sqlCmdBuffer_.empty())\n                            {\n                                rcb(r);\n                            }\n                            else\n                            {\n                                loop_->queueInLoop(\n                                    [rcb = std::move(rcb), r]() { rcb(r); });\n                            }\n                        },\n                        std::move(exceptCallback));\n                    return;\n                }\n            }\n        }\n        else\n        {\n            /// pg batch mode\n            for (size_t i = 0; i < connections_.size(); ++i)\n            {\n                auto &conn = connections_[connectionPos_++];\n                if (connectionPos_ >= connections_.size())\n                    connectionPos_ = 0;\n                if (transSet_.empty() ||\n                    transSet_.find(conn) == transSet_.end())\n                {\n                    conn->execSql(std::string_view{sql, sqlLength},\n                                  paraNum,\n                                  std::move(parameters),\n                                  std::move(length),\n                                  std::move(format),\n                                  std::move(rcb),\n                                  std::move(exceptCallback));\n                    return;\n                }\n            }\n        }\n\n#endif\n    }\n\n    if (sqlCmdBuffer_.size() > 20000)\n    {\n        // too many queries in buffer;\n        auto exceptPtr =\n            std::make_exception_ptr(Failure(\"Too many queries in buffer\"));\n        exceptCallback(exceptPtr);\n        return;\n    }\n\n    // LOG_TRACE << \"Push query to buffer\";\n    sqlCmdBuffer_.emplace_back(std::make_shared<SqlCmd>(\n        std::string_view{sql, sqlLength},\n        paraNum,\n        std::move(parameters),\n        std::move(length),\n        std::move(format),\n        [rcb = std::move(rcb), this](const Result &r) {\n            if (sqlCmdBuffer_.empty())\n            {\n                rcb(r);\n            }\n            else\n            {\n                loop_->queueInLoop([rcb = std::move(rcb), r]() { rcb(r); });\n            }\n        },\n        std::move(exceptCallback)));\n}\n\nstd::shared_ptr<Transaction> DbClientLockFree::newTransaction(\n    const std::function<void(bool)> &) noexcept(false)\n{\n    // Don't support transaction;\n    LOG_ERROR\n        << \"You can't use the synchronous interface in the fast Database \"\n           \"client, please use the asynchronous version (newTransactionAsync)\";\n    assert(0);\n    return nullptr;\n}\n\nvoid DbClientLockFree::newTransactionAsync(\n    const std::function<void(const std::shared_ptr<Transaction> &)> &callback)\n{\n    loop_->assertInLoopThread();\n    for (auto &conn : connections_)\n    {\n        if (!conn->isWorking() && transSet_.find(conn) == transSet_.end())\n        {\n            makeTrans(conn,\n                      std::function<void(const std::shared_ptr<Transaction> &)>(\n                          callback));\n            return;\n        }\n    }\n\n    auto callbackPtr = std::make_shared<\n        std::function<void(const std::shared_ptr<Transaction> &)>>(callback);\n    if (timeout_ > 0.0)\n    {\n        auto newCallbackPtr = std::make_shared<std::weak_ptr<\n            std::function<void(const std::shared_ptr<Transaction> &)>>>();\n        auto timeoutFlagPtr = std::make_shared<TaskTimeoutFlag>(\n            loop_,\n            std::chrono::duration<double>(timeout_),\n            [callbackPtr, this, newCallbackPtr]() {\n                auto cbPtr = (*newCallbackPtr).lock();\n                if (cbPtr)\n                {\n                    for (auto iter = transCallbacks_.begin();\n                         iter != transCallbacks_.end();\n                         ++iter)\n                    {\n                        if (cbPtr == *iter)\n                        {\n                            transCallbacks_.erase(iter);\n                            break;\n                        }\n                    }\n                }\n                (*callbackPtr)(nullptr);\n            });\n        callbackPtr = std::make_shared<\n            std::function<void(const std::shared_ptr<Transaction> &)>>(\n            [callbackPtr,\n             timeoutFlagPtr](const std::shared_ptr<Transaction> &trans) {\n                if (timeoutFlagPtr->done())\n                    return;\n                (*callbackPtr)(trans);\n            });\n        *newCallbackPtr = callbackPtr;\n        timeoutFlagPtr->runTimer();\n    }\n    transCallbacks_.push_back(callbackPtr);\n}\n\nvoid DbClientLockFree::makeTrans(\n    const DbConnectionPtr &conn,\n    std::function<void(const std::shared_ptr<Transaction> &)> &&callback)\n{\n    std::weak_ptr<DbClientLockFree> weakThis = shared_from_this();\n    auto trans = std::make_shared<TransactionImpl>(\n        type_, conn, std::function<void(bool)>(), [weakThis, conn]() {\n            auto thisPtr = weakThis.lock();\n            if (!thisPtr)\n                return;\n\n            if (conn->status() == ConnectStatus::Bad)\n            {\n                return;\n            }\n            if (!thisPtr->transCallbacks_.empty())\n            {\n                auto callback = std::move(thisPtr->transCallbacks_.front());\n                thisPtr->transCallbacks_.pop_front();\n                thisPtr->makeTrans(conn, std::move(*callback));\n                return;\n            }\n\n            for (auto &connPtr : thisPtr->connections_)\n            {\n                if (connPtr == conn)\n                {\n                    conn->loop()->queueInLoop([weakThis, conn]() {\n                        auto thisPtr = weakThis.lock();\n                        if (!thisPtr)\n                            return;\n                        std::weak_ptr<DbConnection> weakConn = conn;\n                        conn->setIdleCallback([weakThis, weakConn]() {\n                            auto thisPtr = weakThis.lock();\n                            if (!thisPtr)\n                                return;\n                            auto connPtr = weakConn.lock();\n                            if (!connPtr)\n                                return;\n                            thisPtr->handleNewTask(connPtr);\n                        });\n                        thisPtr->transSet_.erase(conn);\n                        thisPtr->handleNewTask(conn);\n                    });\n                    break;\n                }\n            }\n        });\n    transSet_.insert(conn);\n    trans->doBegin();\n    if (timeout_ > 0.0)\n    {\n        trans->setTimeout(timeout_);\n    }\n    conn->loop()->queueInLoop(\n        [callback = std::move(callback), trans] { callback(trans); });\n}\n\nvoid DbClientLockFree::handleNewTask(const DbConnectionPtr &conn)\n{\n    assert(conn);\n    assert(!conn->isWorking());\n\n    if (!transCallbacks_.empty())\n    {\n        auto callback = std::move(transCallbacks_.front());\n        transCallbacks_.pop_front();\n        makeTrans(conn, std::move(*callback));\n        return;\n    }\n\n    if (!sqlCmdBuffer_.empty())\n    {\n#if LIBPQ_SUPPORTS_BATCH_MODE\n        if (type_ != ClientType::PostgreSQL)\n        {\n            std::shared_ptr<SqlCmd> cmd = std::move(sqlCmdBuffer_.front());\n            sqlCmdBuffer_.pop_front();\n            conn->execSql(std::move(cmd->sql_),\n                          cmd->parametersNumber_,\n                          std::move(cmd->parameters_),\n                          std::move(cmd->lengths_),\n                          std::move(cmd->formats_),\n                          std::move(cmd->callback_),\n                          std::move(cmd->exceptionCallback_));\n        }\n        else\n        {\n            std::deque<std::shared_ptr<SqlCmd>> cmds;\n            using std::swap;\n            swap(cmds, sqlCmdBuffer_);\n            conn->batchSql(std::move(cmds));\n        }\n#else\n        std::shared_ptr<SqlCmd> cmd = std::move(sqlCmdBuffer_.front());\n        sqlCmdBuffer_.pop_front();\n        conn->execSql(std::move(cmd->sql_),\n                      cmd->parametersNumber_,\n                      std::move(cmd->parameters_),\n                      std::move(cmd->lengths_),\n                      std::move(cmd->formats_),\n                      std::move(cmd->callback_),\n                      std::move(cmd->exceptionCallback_));\n#endif\n        return;\n    }\n}\n\nDbConnectionPtr DbClientLockFree::newConnection()\n{\n    DbConnectionPtr connPtr;\n    if (type_ == ClientType::PostgreSQL)\n    {\n#if USE_POSTGRESQL\n#if LIBPQ_SUPPORTS_BATCH_MODE\n        connPtr =\n            std::make_shared<PgConnection>(loop_, connectionInfo_, autoBatch_);\n#else\n        connPtr = std::make_shared<PgConnection>(loop_, connectionInfo_, false);\n#endif\n#else\n        return nullptr;\n#endif\n    }\n    else if (type_ == ClientType::Mysql)\n    {\n#if USE_MYSQL\n        connPtr = std::make_shared<MysqlConnection>(loop_, connectionInfo_);\n#else\n        return nullptr;\n#endif\n    }\n    else\n    {\n        return nullptr;\n    }\n\n    std::weak_ptr<DbClientLockFree> weakPtr = shared_from_this();\n    connPtr->setCloseCallback([weakPtr](const DbConnectionPtr &closeConnPtr) {\n        // Erase the connection\n        auto thisPtr = weakPtr.lock();\n        if (!thisPtr)\n            return;\n\n        auto iter = std::find(thisPtr->connections_.begin(),\n                              thisPtr->connections_.end(),\n                              closeConnPtr);\n        if (iter != thisPtr->connections_.end())\n            thisPtr->connections_.erase(iter);\n\n        iter = std::find(thisPtr->connectionHolders_.begin(),\n                         thisPtr->connectionHolders_.end(),\n                         closeConnPtr);\n        if (iter != thisPtr->connectionHolders_.end())\n            thisPtr->connectionHolders_.erase(iter);\n\n        thisPtr->transSet_.erase(closeConnPtr);\n        // Reconnect after 1 second\n        thisPtr->loop_->runAfter(1, [weakPtr, closeConnPtr] {\n            auto thisPtr = weakPtr.lock();\n            if (!thisPtr)\n                return;\n            thisPtr->newConnection();\n        });\n    });\n    connPtr->setOkCallback([weakPtr](const DbConnectionPtr &okConnPtr) {\n        LOG_TRACE << \"connected!\";\n        auto thisPtr = weakPtr.lock();\n        if (!thisPtr)\n            return;\n        thisPtr->connections_.push_back(okConnPtr);\n        thisPtr->handleNewTask(okConnPtr);\n    });\n    std::weak_ptr<DbConnection> weakConnPtr = connPtr;\n    connPtr->setIdleCallback([weakPtr, weakConnPtr]() {\n        auto thisPtr = weakPtr.lock();\n        if (!thisPtr)\n            return;\n        auto connPtr = weakConnPtr.lock();\n        if (!connPtr)\n            return;\n        thisPtr->handleNewTask(connPtr);\n    });\n\n    connectionHolders_.push_back(connPtr);\n\n    // Init database connection only after all callbacks are set and connPtr\n    // is added to connectionHolders_.\n    connPtr->init();\n\n    // std::cout<<\"newConn end\"<<connPtr<<std::endl;\n    return connPtr;\n}\n\nbool DbClientLockFree::hasAvailableConnections() const noexcept\n{\n    return !connections_.empty();\n}\n\nvoid DbClientLockFree::execSqlWithTimeout(\n    const char *sql,\n    size_t sqlLength,\n    size_t paraNum,\n    std::vector<const char *> &&parameters,\n    std::vector<int> &&length,\n    std::vector<int> &&format,\n    ResultCallback &&rcb,\n    std::function<void(const std::exception_ptr &)> &&ecb)\n{\n    auto commandPtr = std::make_shared<std::weak_ptr<SqlCmd>>();\n    auto ecpPtr =\n        std::make_shared<std::function<void(const std::exception_ptr &)>>(\n            std::move(ecb));\n    auto timeoutFlagPtr = std::make_shared<drogon::TaskTimeoutFlag>(\n        loop_,\n        std::chrono::duration<double>(timeout_),\n        [commandPtr, ecpPtr, thisPtr = shared_from_this()]() {\n            auto cbPtr = (*commandPtr).lock();\n            if (cbPtr)\n            {\n                for (auto iter = thisPtr->sqlCmdBuffer_.begin();\n                     iter != thisPtr->sqlCmdBuffer_.end();\n                     ++iter)\n                {\n                    if (*iter == cbPtr)\n                    {\n                        thisPtr->sqlCmdBuffer_.erase(iter);\n                        break;\n                    }\n                }\n            }\n            (*ecpPtr)(\n                std::make_exception_ptr(TimeoutError(\"SQL execution timeout\")));\n        });\n    auto resultCallback = [rcb = std::move(rcb),\n                           timeoutFlagPtr](const Result &result) {\n        if (timeoutFlagPtr->done())\n            return;\n        rcb(result);\n    };\n\n    auto exceptionCallback = [ecpPtr,\n                              timeoutFlagPtr](const std::exception_ptr &err) {\n        if (timeoutFlagPtr->done())\n            return;\n        (*ecpPtr)(err);\n    };\n    if (!connections_.empty() && sqlCmdBuffer_.empty() &&\n        transCallbacks_.empty())\n    {\n#if (!LIBPQ_SUPPORTS_BATCH_MODE)\n        for (auto &conn : connections_)\n        {\n            if (!conn->isWorking() &&\n                (transSet_.empty() || transSet_.find(conn) == transSet_.end()))\n            {\n                conn->execSql(\n                    std::string_view{sql, sqlLength},\n                    paraNum,\n                    std::move(parameters),\n                    std::move(length),\n                    std::move(format),\n                    [resultCallback = std::move(resultCallback),\n                     this](const Result &r) {\n                        if (sqlCmdBuffer_.empty())\n                        {\n                            resultCallback(r);\n                        }\n                        else\n                        {\n                            loop_->queueInLoop(\n                                [resultCallback = std::move(resultCallback),\n                                 r]() { resultCallback(r); });\n                        }\n                    },\n                    std::move(exceptionCallback));\n                timeoutFlagPtr->runTimer();\n                return;\n            }\n        }\n#else\n        if (type_ != ClientType::PostgreSQL)\n        {\n            for (auto &conn : connections_)\n            {\n                if (!conn->isWorking() &&\n                    (transSet_.empty() ||\n                     transSet_.find(conn) == transSet_.end()))\n                {\n                    conn->execSql(\n                        std::string_view{sql, sqlLength},\n                        paraNum,\n                        std::move(parameters),\n                        std::move(length),\n                        std::move(format),\n                        [resultCallback = std::move(resultCallback),\n                         this](const Result &r) {\n                            if (sqlCmdBuffer_.empty())\n                            {\n                                resultCallback(r);\n                            }\n                            else\n                            {\n                                loop_->queueInLoop(\n                                    [resultCallback = std::move(resultCallback),\n                                     r]() { resultCallback(r); });\n                            }\n                        },\n                        std::move(exceptionCallback));\n                    timeoutFlagPtr->runTimer();\n                    return;\n                }\n            }\n        }\n        else\n        {\n            /// pg batch mode\n            for (size_t i = 0; i < connections_.size(); ++i)\n            {\n                auto &conn = connections_[connectionPos_++];\n                if (connectionPos_ >= connections_.size())\n                    connectionPos_ = 0;\n                if (transSet_.empty() ||\n                    transSet_.find(conn) == transSet_.end())\n                {\n                    conn->execSql(std::string_view{sql, sqlLength},\n                                  paraNum,\n                                  std::move(parameters),\n                                  std::move(length),\n                                  std::move(format),\n                                  std::move(resultCallback),\n                                  std::move(exceptionCallback));\n                    timeoutFlagPtr->runTimer();\n                    return;\n                }\n            }\n        }\n\n#endif\n    }\n\n    if (sqlCmdBuffer_.size() > 20000)\n    {\n        // too many queries in buffer;\n        exceptionCallback(\n            std::make_exception_ptr(Failure(\"Too many queries in buffer\")));\n        return;\n    }\n\n    // LOG_TRACE << \"Push query to buffer\";\n    auto cmdPtr = std::make_shared<SqlCmd>(\n        std::string_view{sql, sqlLength},\n        paraNum,\n        std::move(parameters),\n        std::move(length),\n        std::move(format),\n        [resultCallback = std::move(resultCallback), this](const Result &r) {\n            if (sqlCmdBuffer_.empty())\n            {\n                resultCallback(r);\n            }\n            else\n            {\n                loop_->queueInLoop([resultCallback = std::move(resultCallback),\n                                    r]() { resultCallback(r); });\n            }\n        },\n        std::move(exceptionCallback));\n    sqlCmdBuffer_.emplace_back(cmdPtr);\n    *commandPtr = cmdPtr;\n    timeoutFlagPtr->runTimer();\n}\n"
  },
  {
    "path": "orm_lib/src/DbClientLockFree.h",
    "content": "/**\n *\n *  @file DbClientLockFree.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"DbConnection.h\"\n#include <drogon/orm/DbClient.h>\n#include <trantor/net/EventLoopThreadPool.h>\n#include <functional>\n#include <memory>\n#include <queue>\n#include <string>\n#include <thread>\n#include <unordered_set>\n#include <list>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClientLockFree : public DbClient,\n                         public std::enable_shared_from_this<DbClientLockFree>\n{\n  public:\n    DbClientLockFree(const std::string &connInfo,\n                     trantor::EventLoop *loop,\n                     ClientType type,\n#if LIBPQ_SUPPORTS_BATCH_MODE\n                     size_t connectionNumberPerLoop,\n                     bool autoBatch);\n#else\n                     size_t connectionNumberPerLoop);\n#endif\n\n    ~DbClientLockFree() noexcept override;\n    void execSql(const char *sql,\n                 size_t sqlLength,\n                 size_t paraNum,\n                 std::vector<const char *> &&parameters,\n                 std::vector<int> &&length,\n                 std::vector<int> &&format,\n                 ResultCallback &&rcb,\n                 std::function<void(const std::exception_ptr &)>\n                     &&exceptCallback) override;\n    std::shared_ptr<Transaction> newTransaction(\n        const std::function<void(bool)> &commitCallback =\n            std::function<void(bool)>()) noexcept(false) override;\n    void newTransactionAsync(\n        const std::function<void(const std::shared_ptr<Transaction> &)>\n            &callback) override;\n    bool hasAvailableConnections() const noexcept override;\n\n    void setTimeout(double timeout) override\n    {\n        timeout_ = timeout;\n    }\n\n    void closeAll() override;\n\n  private:\n    std::string connectionInfo_;\n    trantor::EventLoop *loop_;\n    DbConnectionPtr newConnection();\n    const size_t numberOfConnections_;\n    std::vector<DbConnectionPtr> connections_;\n    std::vector<DbConnectionPtr> connectionHolders_;\n    std::unordered_set<DbConnectionPtr> transSet_;\n    std::deque<std::shared_ptr<SqlCmd>> sqlCmdBuffer_;\n\n    std::list<std::shared_ptr<\n        std::function<void(const std::shared_ptr<Transaction> &)>>>\n        transCallbacks_;\n\n    double timeout_{-1.0};\n\n    void makeTrans(\n        const DbConnectionPtr &conn,\n        std::function<void(const std::shared_ptr<Transaction> &)> &&callback);\n    void execSqlWithTimeout(\n        const char *sql,\n        size_t sqlLength,\n        size_t paraNum,\n        std::vector<const char *> &&parameters,\n        std::vector<int> &&length,\n        std::vector<int> &&format,\n        ResultCallback &&rcb,\n        std::function<void(const std::exception_ptr &)> &&ecb);\n    void handleNewTask(const DbConnectionPtr &conn);\n#if LIBPQ_SUPPORTS_BATCH_MODE\n    size_t connectionPos_{0};  // Used for pg batch mode.\n    bool autoBatch_{false};\n#endif\n};\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/src/DbClientManager.cc",
    "content": "/**\n *\n *  @file DbClientManager.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"../../lib/src/DbClientManager.h\"\n#include \"DbClientLockFree.h\"\n#include <drogon/config.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/utils/Utilities.h>\n#include <algorithm>\n\nusing namespace drogon::orm;\nusing namespace drogon;\n\ninline std::string escapeConnString(const std::string &str)\n{\n    bool beQuoted = str.empty() || (str.find(' ') != std::string::npos);\n\n    std::string escaped;\n    escaped.reserve(str.size());\n    for (auto ch : str)\n    {\n        if (ch == '\\'')\n            escaped.push_back('\\\\');\n        else if (ch == '\\\\')\n            escaped.push_back('\\\\');\n        escaped.push_back(ch);\n    }\n\n    if (beQuoted)\n        return \"'\" + escaped + \"'\";\n    return escaped;\n}\n\nstatic void initFastDbClients(IOThreadStorage<orm::DbClientPtr> &storage,\n                              const std::vector<trantor::EventLoop *> &ioLoops,\n                              const std::string &connInfo,\n                              ClientType dbType,\n                              size_t connNum,\n                              bool autoBatch,\n                              double timeout)\n{\n    storage.init([&](orm::DbClientPtr &c, size_t idx) {\n        assert(idx == ioLoops[idx]->index());\n        LOG_TRACE << \"create fast database client for the thread \" << idx;\n        c = std::shared_ptr<orm::DbClient>(\n            new drogon::orm::DbClientLockFree(connInfo,\n                                              ioLoops[idx],\n                                              dbType,\n#if LIBPQ_SUPPORTS_BATCH_MODE  // Bad code\n                                              connNum,\n                                              autoBatch));\n#else\n                                              connNum));\n#endif\n        if (timeout > 0.0)\n        {\n            c->setTimeout(timeout);\n        }\n    });\n}\n\nvoid DbClientManager::createDbClients(\n    const std::vector<trantor::EventLoop *> &ioLoops)\n{\n    assert(dbClientsMap_.empty());\n    assert(dbFastClientsMap_.empty());\n    for (auto &dbInfo : dbInfos_)\n    {\n        if (std::holds_alternative<PostgresConfig>(dbInfo.config_))\n        {\n            auto &cfg = std::get<PostgresConfig>(dbInfo.config_);\n            if (cfg.isFast)\n            {\n                dbFastClientsMap_[cfg.name] =\n                    IOThreadStorage<orm::DbClientPtr>();\n                initFastDbClients(dbFastClientsMap_[cfg.name],\n                                  ioLoops,\n                                  dbInfo.connectionInfo_,\n                                  ClientType::PostgreSQL,\n                                  cfg.connectionNumber,\n                                  cfg.autoBatch,\n                                  cfg.timeout);\n            }\n            else\n            {\n                dbClientsMap_[cfg.name] =\n                    drogon::orm::DbClient::newPgClient(dbInfo.connectionInfo_,\n                                                       cfg.connectionNumber,\n                                                       cfg.autoBatch);\n                if (cfg.timeout > 0.0)\n                {\n                    dbClientsMap_[cfg.name]->setTimeout(cfg.timeout);\n                }\n            }\n        }\n        else if (std::holds_alternative<MysqlConfig>(dbInfo.config_))\n        {\n            auto &cfg = std::get<MysqlConfig>(dbInfo.config_);\n\n            if (cfg.isFast)\n            {\n                dbFastClientsMap_[cfg.name] =\n                    IOThreadStorage<orm::DbClientPtr>();\n                initFastDbClients(dbFastClientsMap_[cfg.name],\n                                  ioLoops,\n                                  dbInfo.connectionInfo_,\n                                  ClientType::Mysql,\n                                  cfg.connectionNumber,\n                                  false,\n                                  cfg.timeout);\n            }\n            else\n            {\n                dbClientsMap_[cfg.name] = drogon::orm::DbClient::newMysqlClient(\n                    dbInfo.connectionInfo_, cfg.connectionNumber);\n                if (cfg.timeout > 0.0)\n                {\n                    dbClientsMap_[cfg.name]->setTimeout(cfg.timeout);\n                }\n            }\n        }\n        else if (std::holds_alternative<Sqlite3Config>(dbInfo.config_))\n        {\n            auto &cfg = std::get<Sqlite3Config>(dbInfo.config_);\n            dbClientsMap_[cfg.name] =\n                drogon::orm::DbClient::newSqlite3Client(dbInfo.connectionInfo_,\n                                                        cfg.connectionNumber);\n            if (cfg.timeout > 0.0)\n            {\n                dbClientsMap_[cfg.name]->setTimeout(cfg.timeout);\n            }\n        }\n    }\n}\n\nstatic std::string buildConnStr(const std::string &host,\n                                unsigned short port,\n                                const std::string &databaseName,\n                                const std::string &username,\n                                const std::string &password,\n                                const std::string &characterSet)\n{\n    auto connStr =\n        utils::formattedString(\"host=%s port=%u dbname=%s user=%s\",\n                               escapeConnString(host).c_str(),\n                               port,\n                               escapeConnString(databaseName).c_str(),\n                               escapeConnString(username).c_str());\n    if (!password.empty())\n    {\n        connStr += \" password=\";\n        connStr += escapeConnString(password);\n    }\n    if (!characterSet.empty())\n    {\n        connStr += \" client_encoding=\";\n        connStr += escapeConnString(characterSet);\n    }\n    return connStr;\n}\n\nvoid DbClientManager::addDbClient(const DbConfig &config)\n{\n    if (std::holds_alternative<PostgresConfig>(config))\n    {\n#if USE_POSTGRESQL\n        auto &cfg = std::get<PostgresConfig>(config);\n        auto connStr = buildConnStr(cfg.host,\n                                    cfg.port,\n                                    cfg.databaseName,\n                                    cfg.username,\n                                    cfg.password,\n                                    cfg.characterSet);\n        // For valid connection options, see:\n        // https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-OPTIONS\n        if (!cfg.connectOptions.empty())\n        {\n            std::string optionStr = \" options='\";\n            for (auto const &[key, value] : cfg.connectOptions)\n            {\n                optionStr += \" -c \";\n                optionStr += escapeConnString(key);\n                optionStr += \"=\";\n                optionStr += escapeConnString(value);\n            }\n            optionStr += \"'\";\n            connStr += optionStr;\n        }\n        dbInfos_.emplace_back(DbInfo{connStr, config});\n#else\n        std::cout << \"The PostgreSQL is not supported in current drogon build, \"\n                     \"please install the development library first.\"\n                  << std::endl;\n        exit(1);\n#endif\n    }\n    else if (std::holds_alternative<MysqlConfig>(config))\n    {\n#if USE_MYSQL\n        auto cfg = std::get<MysqlConfig>(config);\n        auto connStr = buildConnStr(cfg.host,\n                                    cfg.port,\n                                    cfg.databaseName,\n                                    cfg.username,\n                                    cfg.password,\n                                    cfg.characterSet);\n        dbInfos_.emplace_back(DbInfo{connStr, config});\n#else\n        std::cout << \"The Mysql is not supported in current drogon build, \"\n                     \"please install the development library first.\"\n                  << std::endl;\n        exit(1);\n#endif\n    }\n    else if (std::holds_alternative<Sqlite3Config>(config))\n    {\n#if USE_SQLITE3\n        auto cfg = std::get<Sqlite3Config>(config);\n        std::string connStr = \"filename=\" + cfg.filename;\n        dbInfos_.emplace_back(DbInfo{connStr, config});\n#else\n        std::cout << \"The Sqlite3 is not supported in current drogon build, \"\n                     \"please install the development library first.\"\n                  << std::endl;\n        exit(1);\n#endif\n    }\n    else\n    {\n        assert(false && \"Should not happen, unknown DbConfig alternatives\");\n    }\n}\n\nbool DbClientManager::areAllDbClientsAvailable() const noexcept\n{\n    for (auto const &pair : dbClientsMap_)\n    {\n        if (!(pair.second)->hasAvailableConnections())\n            return false;\n    }\n    auto loop = trantor::EventLoop::getEventLoopOfCurrentThread();\n    if (loop && loop->index() < app().getThreadNum())\n    {\n        for (auto const &pair : dbFastClientsMap_)\n        {\n            if (!(*(pair.second))->hasAvailableConnections())\n                return false;\n        }\n    }\n    return true;\n}\n\nDbClientManager::~DbClientManager()\n{\n    for (auto &pair : dbClientsMap_)\n    {\n        pair.second->closeAll();\n    }\n    for (auto &pair : dbFastClientsMap_)\n    {\n        pair.second.init([](DbClientPtr &clientPtr, size_t index) {\n            // the main loop;\n            std::promise<void> p;\n            auto f = p.get_future();\n            drogon::getIOThreadStorageLoop(index)->runInLoop(\n                [&clientPtr, &p]() {\n                    clientPtr->closeAll();\n                    p.set_value();\n                });\n            f.get();\n        });\n    }\n}\n"
  },
  {
    "path": "orm_lib/src/DbConnection.cc",
    "content": "/**\n *\n *  @file DbConnection.cc\n *  @author Martin Chang\n *\n *  Copyright 2020, Martin Chang.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"DbConnection.h\"\n\n#include <regex>\n\nusing namespace drogon::orm;\n\nstd::map<std::string, std::string> DbConnection::parseConnString(\n    const std::string &connInfo)\n{\n    static const std::regex re(\n        R\"((\\w+) *= *('(?:[^\\n]|\\\\[^\\n])+'|(?:\\S|\\\\\\S)+))\");\n    std::smatch what;\n    std::map<std::string, std::string> params;\n    std::string str = connInfo;\n\n    while (std::regex_search(str, what, re))\n    {\n        assert(what.size() == 3);\n        std::string key = what[1];\n        std::string rawValue = what[2];\n        std::string value;\n        bool quoted =\n            !rawValue.empty() && rawValue[0] == '\\'' && rawValue.back() == '\\'';\n\n        value.reserve(rawValue.size());\n        for (size_t i = 0; i < rawValue.size(); i++)\n        {\n            if (quoted && (i == 0 || i == rawValue.size() - 1))\n                continue;\n            else if (rawValue[i] == '\\\\' && i != rawValue.size() - 1)\n            {\n                value.push_back(rawValue[i + 1]);\n                i += 1;\n                continue;\n            }\n\n            value.push_back(rawValue[i]);\n        }\n        params[key] = value;\n        str = what.suffix().str();\n    }\n    return params;\n}\n"
  },
  {
    "path": "orm_lib/src/DbConnection.h",
    "content": "/**\n *\n *  @file DbConnection.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/config.h>\n#include <drogon/orm/DbClient.h>\n#include <string_view>\n#include <trantor/net/EventLoop.h>\n#include <trantor/utils/NonCopyable.h>\n#include <functional>\n#include <iostream>\n#include <memory>\n#include <mutex>\n#include <shared_mutex>\n#include <string>\n\nnamespace drogon\n{\nnamespace orm\n{\n#if __cplusplus >= 201703L | defined _WIN32\nusing SharedMutex = std::shared_mutex;\n#else\nusing SharedMutex = std::shared_timed_mutex;\n#endif\n\nenum class ConnectStatus\n{\n    None = 0,\n    Connecting,\n    SettingCharacterSet,\n    Ok,\n    Bad\n};\n\nstruct SqlCmd\n{\n    std::string_view sql_;\n    size_t parametersNumber_;\n    std::vector<const char *> parameters_;\n    std::vector<int> lengths_;\n    std::vector<int> formats_;\n    QueryCallback callback_;\n    ExceptPtrCallback exceptionCallback_;\n    std::string preparingStatement_;\n#if LIBPQ_SUPPORTS_BATCH_MODE\n    bool isChanging_{false};\n#endif\n    SqlCmd(std::string_view &&sql,\n           size_t paraNum,\n           std::vector<const char *> &&parameters,\n           std::vector<int> &&length,\n           std::vector<int> &&format,\n           QueryCallback &&cb,\n           ExceptPtrCallback &&exceptCb)\n        : sql_(std::move(sql)),\n          parametersNumber_(paraNum),\n          parameters_(std::move(parameters)),\n          lengths_(std::move(length)),\n          formats_(std::move(format)),\n          callback_(std::move(cb)),\n          exceptionCallback_(std::move(exceptCb))\n    {\n    }\n};\n\nclass DbConnection;\nusing DbConnectionPtr = std::shared_ptr<DbConnection>;\n\nclass DbConnection : public trantor::NonCopyable\n{\n  public:\n    using DbConnectionCallback = std::function<void(const DbConnectionPtr &)>;\n\n    explicit DbConnection(trantor::EventLoop *loop) : loop_(loop)\n    {\n    }\n\n    virtual void init(){};\n\n    void setOkCallback(const DbConnectionCallback &cb)\n    {\n        okCallback_ = cb;\n    }\n\n    void setCloseCallback(const DbConnectionCallback &cb)\n    {\n        closeCallback_ = cb;\n    }\n\n    void setIdleCallback(const std::function<void()> &cb)\n    {\n        idleCb_ = cb;\n    }\n\n    virtual void execSql(\n        std::string_view &&sql,\n        size_t paraNum,\n        std::vector<const char *> &&parameters,\n        std::vector<int> &&length,\n        std::vector<int> &&format,\n        ResultCallback &&rcb,\n        std::function<void(const std::exception_ptr &)> &&exceptCallback) = 0;\n    virtual void batchSql(\n        std::deque<std::shared_ptr<SqlCmd>> &&sqlCommands) = 0;\n\n    virtual ~DbConnection()\n    {\n        LOG_TRACE << \"Destruct DbConn \" << this;\n    }\n\n    ConnectStatus status() const\n    {\n        return status_;\n    }\n\n    trantor::EventLoop *loop()\n    {\n        return loop_;\n    }\n\n    virtual void disconnect() = 0;\n\n    bool isWorking() const\n    {\n        return isWorking_;\n    }\n\n  protected:\n    QueryCallback callback_;\n    trantor::EventLoop *loop_;\n    std::function<void()> idleCb_;\n    ConnectStatus status_{ConnectStatus::None};\n    DbConnectionCallback closeCallback_{[](const DbConnectionPtr &) {}};\n    DbConnectionCallback okCallback_{[](const DbConnectionPtr &) {}};\n    std::function<void(const std::exception_ptr &)> exceptionCallback_;\n    bool isWorking_{false};\n\n    static std::map<std::string, std::string> parseConnString(\n        const std::string &);\n};\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/src/DbListener.cc",
    "content": "/**\n *\n *  @file DbListener.cc\n *  @author Nitromelon\n *\n *  Copyright 2022, An Tao.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/config.h>\n#include <drogon/orm/DbListener.h>\n#include <trantor/utils/Logger.h>\n#include <mutex>\n\n#if USE_POSTGRESQL\n#include \"postgresql_impl/PgListener.h\"\n#endif\n\nusing namespace drogon;\nusing namespace drogon::orm;\n\nDbListener::~DbListener() = default;\n\nstd::shared_ptr<DbListener> DbListener::newPgListener(\n    const std::string &connInfo,\n    trantor::EventLoop *loop)\n{\n#if USE_POSTGRESQL\n    std::shared_ptr<PgListener> pgListener =\n        std::make_shared<PgListener>(connInfo, loop);\n    pgListener->init();\n    return pgListener;\n#else\n    LOG_ERROR << \"Postgresql is not supported by current drogon build\";\n    return nullptr;\n#endif\n}\n"
  },
  {
    "path": "orm_lib/src/Exception.cc",
    "content": "/**\n *\n * Copyright (c) 2005-2017, Jeroen T. Vermeulen.\n *\n * See COPYING for copyright license.  If you did not receive a file called\n * COPYING with this source code, please notify the distributor of this mistake,\n * or contact the author.\n */\n// taken from libpqxx and modified\n\n/**\n *\n *  @file Exception.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/orm/Exception.h>\n\nusing namespace drogon::orm;\n\nFailure::Failure(const std::string &whatarg) : std::runtime_error(whatarg)\n{\n}\n\nBrokenConnection::BrokenConnection() : Failure(\"Connection to database failed\")\n{\n}\n\nBrokenConnection::BrokenConnection(const std::string &whatarg)\n    : Failure(whatarg)\n{\n}\n\nSqlError::SqlError(const std::string &whatarg,\n                   const std::string &Q,\n                   const char sqlstate[])\n    : Failure(whatarg),\n      query_(Q),\n      sqlState_(sqlstate ? sqlstate : \"\"),\n      errcode_(0),\n      extendedErrcode_(0)\n{\n}\n\nSqlError::SqlError(const std::string &whatarg,\n                   const std::string &Q,\n                   const int errcode,\n                   const int extendedErrcode)\n    : Failure(whatarg),\n      query_(Q),\n      sqlState_(\"\"),\n      errcode_(errcode),\n      extendedErrcode_(extendedErrcode)\n{\n}\n\nSqlError::~SqlError() noexcept\n{\n}\n\nconst std::string &SqlError::query() const noexcept\n{\n    return query_;\n}\n\nconst std::string &SqlError::sqlState() const noexcept\n{\n    return sqlState_;\n}\n\nint SqlError::errcode() const noexcept\n{\n    return errcode_;\n}\n\nint SqlError::extendedErrcode() const noexcept\n{\n    return extendedErrcode_;\n}\n\nInDoubtError::InDoubtError(const std::string &whatarg) : Failure(whatarg)\n{\n}\n\nTransactionRollback::TransactionRollback(const std::string &whatarg)\n    : Failure(whatarg)\n{\n}\n\nSerializationFailure::SerializationFailure(const std::string &whatarg)\n    : TransactionRollback(whatarg)\n{\n}\n\nStatementCompletionUnknown::StatementCompletionUnknown(\n    const std::string &whatarg)\n    : TransactionRollback(whatarg)\n{\n}\n\nDeadlockDetected::DeadlockDetected(const std::string &whatarg)\n    : TransactionRollback(whatarg)\n{\n}\n\nInternalError::InternalError(const std::string &whatarg)\n    : logic_error(\"drogon database internal error: \" + whatarg)\n{\n}\n\nTimeoutError::TimeoutError(const std::string &whatarg) : logic_error(whatarg)\n{\n}\n\nUsageError::UsageError(const std::string &whatarg) : logic_error(whatarg)\n{\n}\n\nArgumentError::ArgumentError(const std::string &whatarg)\n    : invalid_argument(whatarg)\n{\n}\n\nConversionError::ConversionError(const std::string &whatarg)\n    : domain_error(whatarg)\n{\n}\n\nRangeError::RangeError(const std::string &whatarg) : out_of_range(whatarg)\n{\n}\n"
  },
  {
    "path": "orm_lib/src/Field.cc",
    "content": "/**\n *\n *  Field.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/orm/Field.h>\n#include <drogon/utils/Utilities.h>\n#include <trantor/utils/Logger.h>\n#include <stdlib.h>\n\nusing namespace drogon::orm;\n\nField::Field(const Row &row, Row::SizeType columnNum) noexcept\n    : row_(Result::SizeType(row.index_)),\n      column_((long)columnNum),\n      result_(row.result_)\n{\n}\n\nconst char *Field::name() const\n{\n    return result_.columnName(column_);\n}\n\nbool Field::isNull() const\n{\n    return result_.isNull(row_, column_);\n}\n\ntemplate <>\nstd::string Field::as<std::string>() const\n{\n    if (result_.oid(column_) != 17)\n    {\n        auto data_ = result_.getValue(row_, column_);\n        auto dataLength_ = result_.getLength(row_, column_);\n        //    LOG_DEBUG << \"dataLength_=\" << dataLength_ << \" str=\" << data_;\n        return std::string(data_, dataLength_);\n    }\n    else\n    {\n        // Bytea type of PostgreSQL\n        auto sv = as<std::string_view>();\n        if (sv.length() < 2 || sv[0] != '\\\\' || sv[1] != 'x')\n            return std::string();\n        return utils::hexToBinaryString(sv.data() + 2, sv.length() - 2);\n    }\n}\n\ntemplate <>\nconst char *Field::as<const char *>() const\n{\n    auto data_ = result_.getValue(row_, column_);\n    return data_;\n}\n\ntemplate <>\nchar *Field::as<char *>() const\n{\n    auto data_ = result_.getValue(row_, column_);\n    return (char *)data_;\n}\n\ntemplate <>\nstd::vector<char> Field::as<std::vector<char>>() const\n{\n    if (result_.oid(column_) != 17)\n    {\n        char *first = (char *)result_.getValue(row_, column_);\n        char *last = first + result_.getLength(row_, column_);\n        return std::vector<char>(first, last);\n    }\n    else\n    {\n        // Bytea type of PostgreSQL\n        auto sv = as<std::string_view>();\n        if (sv.length() < 2 || sv[0] != '\\\\' || sv[1] != 'x')\n            return std::vector<char>();\n        return utils::hexToBinaryVector(sv.data() + 2, sv.length() - 2);\n    }\n}\n\nconst char *Field::c_str() const\n{\n    return as<const char *>();\n}\n\n// template <>\n// std::vector<short> Field::as<std::vector<short>>() const\n// {\n//     auto data_ = result_.getValue(row_, column_);\n//     auto dataLength_ = result_.getLength(row_, column_);\n//     LOG_DEBUG<<\"dataLength_=\"<<dataLength_;\n//     for(int i=0;i<dataLength_;i++)\n//     {\n//         LOG_DEBUG<<\"data[\"<<i<<\"]=\"<<(int)data_[i];\n//     }\n//     LOG_DEBUG<<data_;\n//     return std::vector<short>((short *)data_,(short *)(data_ + dataLength_));\n// }\n\n// template <>\n// std::vector<int32_t> Field::as<std::vector<int32_t>>() const\n// {\n//     auto data_ = result_.getValue(row_, column_);\n//     auto dataLength_ = result_.getLength(row_, column_);\n//     return std::vector<int32_t>((int32_t *)data_,(int32_t *)(data_ +\n//     dataLength_));\n// }\n\n// template <>\n// std::vector<int64_t> Field::as<std::vector<int64_t>>() const\n// {\n//     auto data_ = result_.getValue(row_, column_);\n//     auto dataLength_ = result_.getLength(row_, column_);\n//     return std::vector<int64_t>((int64_t *)data_,(int64_t *)(data_ +\n//     dataLength_));\n// }\n"
  },
  {
    "path": "orm_lib/src/RestfulController.cc",
    "content": "/**\n *\n *  RestfulController.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/orm/RestfulController.h>\n\nusing namespace drogon;\n\norm::Criteria RestfulController::makeCriteria(\n    const Json::Value &pJson) noexcept(false)\n{\n    if (!pJson.isArray())\n    {\n        throw std::runtime_error(\"Json format error\");\n    }\n    orm::Criteria ret;\n    for (auto &orJson : pJson)\n    {\n        if (!orJson.isArray())\n        {\n            throw std::runtime_error(\"Json format error\");\n        }\n        orm::Criteria orCriteria;\n        for (auto &andJson : orJson)\n        {\n            if (!andJson.isArray() || andJson.size() != 3)\n            {\n                throw std::runtime_error(\"Json format error\");\n            }\n            if (masquerading_)\n            {\n                Json::Value newJson(andJson);\n                auto iter = masqueradingMap_.find(newJson[0].asString());\n                if (iter != masqueradingMap_.end())\n                {\n                    newJson[0] = masqueradingVector_[iter->second];\n                    if (!orCriteria)\n                    {\n                        orCriteria = orm::Criteria(newJson);\n                    }\n                    else\n                    {\n                        orCriteria = orCriteria && orm::Criteria(newJson);\n                    }\n                }\n                else\n                {\n                    throw std::runtime_error(\"Json format error\");\n                }\n            }\n            else\n            {\n                if (!orCriteria)\n                {\n                    orCriteria = orm::Criteria(andJson);\n                }\n                else\n                {\n                    orCriteria = orCriteria && orm::Criteria(andJson);\n                }\n            }\n        }\n        if (!ret)\n        {\n            ret = std::move(orCriteria);\n        }\n        else\n        {\n            ret = ret || orCriteria;\n        }\n    }\n    return ret;\n}\n"
  },
  {
    "path": "orm_lib/src/Result.cc",
    "content": "/**\n *\n *  Result.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"ResultImpl.h\"\n#include <cassert>\n#include <drogon/orm/Result.h>\n#include <drogon/orm/ResultIterator.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Exception.h>\n\nusing namespace drogon::orm;\n\nResult::ConstIterator Result::begin() const noexcept\n{\n    return ConstIterator(*this, (SizeType)0);\n}\n\nResult::ConstIterator Result::cbegin() const noexcept\n{\n    return begin();\n}\n\nResult::ConstIterator Result::end() const noexcept\n{\n    return ConstIterator(*this, size());\n}\n\nResult::ConstIterator Result::cend() const noexcept\n{\n    return end();\n}\n\nResult::ConstReverseIterator Result::rbegin() const\n{\n    return ConstReverseResultIterator(end());\n}\n\nResult::ConstReverseIterator Result::crbegin() const\n{\n    return rbegin();\n}\n\nResult::ConstReverseIterator Result::rend() const\n{\n    return ConstReverseResultIterator(begin());\n}\n\nResult::ConstReverseIterator Result::crend() const\n{\n    return rend();\n}\n\nResult::ConstIterator Result::ConstReverseIterator::base() const noexcept\n{\n    iterator_type tmp(*this);\n    return ++tmp;\n}\n\nResult::Reference Result::front() const noexcept\n{\n    return Row(*this, 0);\n}\n\nResult::Reference Result::back() const noexcept\n{\n    return Row(*this, size() - 1);\n}\n\nResult::Reference Result::operator[](SizeType index) const noexcept\n{\n    assert(index < size());\n    return Row(*this, index);\n}\n\nResult::Reference Result::at(SizeType index) const\n{\n    if (index >= size())\n        throw RangeError(\"Result index is out of range\");\n    return operator[](index);\n}\n\nConstResultIterator ConstResultIterator::operator++(int)\n{\n    ConstResultIterator old(*this);\n    ++index_;\n    return old;\n}\n\nConstResultIterator ConstResultIterator::operator--(int)\n{\n    ConstResultIterator old(*this);\n    --index_;\n    return old;\n}\n\nConstReverseResultIterator ConstReverseResultIterator::operator++(int)\n{\n    ConstReverseResultIterator old(*this);\n    iterator_type::operator--();\n    return old;\n}\n\nConstReverseResultIterator ConstReverseResultIterator::operator--(int)\n{\n    ConstReverseResultIterator old(*this);\n    iterator_type::operator++();\n    return old;\n}\n\nResult::SizeType Result::size() const noexcept\n{\n    return resultPtr_->size();\n}\n\nvoid Result::swap(Result &other) noexcept\n{\n    resultPtr_.swap(other.resultPtr_);\n}\n\nResult::RowSizeType Result::columns() const noexcept\n{\n    return resultPtr_->columns();\n}\n\nconst char *Result::columnName(Result::RowSizeType number) const\n{\n    return resultPtr_->columnName(number);\n}\n\nResult::SizeType Result::affectedRows() const noexcept\n{\n    return resultPtr_->affectedRows();\n}\n\nResult::RowSizeType Result::columnNumber(const char colName[]) const\n{\n    return resultPtr_->columnNumber(colName);\n}\n\nconst char *Result::getValue(Result::SizeType row,\n                             Result::RowSizeType column) const\n{\n    return resultPtr_->getValue(row, column);\n}\n\nbool Result::isNull(Result::SizeType row, Result::RowSizeType column) const\n{\n    return resultPtr_->isNull(row, column);\n}\n\nResult::FieldSizeType Result::getLength(Result::SizeType row,\n                                        Result::RowSizeType column) const\n{\n    return resultPtr_->getLength(row, column);\n}\n\nunsigned long long Result::insertId() const noexcept\n{\n    return resultPtr_->insertId();\n}\n\nint Result::oid(RowSizeType column) const noexcept\n{\n    return resultPtr_->oid(column);\n}\n\nResult &Result::operator=(const Result &r) noexcept\n{\n    resultPtr_ = r.resultPtr_;\n    return *this;\n}\n\nResult &Result::operator=(Result &&r) noexcept\n{\n    resultPtr_ = std::move(r.resultPtr_);\n    return *this;\n}\n"
  },
  {
    "path": "orm_lib/src/ResultImpl.h",
    "content": "/**\n *\n *  ResultImpl.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/orm/Result.h>\n#include <trantor/utils/NonCopyable.h>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass ResultImpl : public trantor::NonCopyable\n{\n  public:\n    ResultImpl() = default;\n    using SizeType = Result::SizeType;\n    using RowSizeType = Result::RowSizeType;\n    using FieldSizeType = Result::FieldSizeType;\n    virtual SizeType size() const noexcept = 0;\n    virtual RowSizeType columns() const noexcept = 0;\n    virtual const char *columnName(RowSizeType Number) const = 0;\n    virtual SizeType affectedRows() const noexcept = 0;\n    virtual RowSizeType columnNumber(const char colName[]) const = 0;\n    virtual const char *getValue(SizeType row, RowSizeType column) const = 0;\n    virtual bool isNull(SizeType row, RowSizeType column) const = 0;\n    virtual FieldSizeType getLength(SizeType row, RowSizeType column) const = 0;\n\n    virtual unsigned long long insertId() const noexcept\n    {\n        return 0;\n    }\n\n    virtual int oid(RowSizeType column) const\n    {\n        (void)column;\n        return 0;\n    }\n\n    virtual ~ResultImpl()\n    {\n    }\n};\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/src/Row.cc",
    "content": "/**\n *\n *  @file Row.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <cassert>\n#include <drogon/orm/Exception.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/RowIterator.h>\n\nusing namespace drogon::orm;\n\nRow::Row(const Result &r, SizeType index) noexcept\n    : result_(r), index_(long(index)), end_(r.columns())\n{\n}\n\nRow::SizeType Row::size() const\n{\n    return end_;\n}\n\nRow::Reference Row::operator[](SizeType index) const noexcept\n{\n    assert(index < end_);\n    return Field(*this, index);\n}\n\nRow::Reference Row::operator[](int index) const noexcept\n{\n    assert(index >= 0);\n    auto i = static_cast<SizeType>(index);\n    assert(i < end_);\n    return Field(*this, i);\n}\n\nRow::Reference Row::operator[](const char columnName[]) const\n{\n    auto N = result_.columnNumber(columnName);\n    return Field(*this, N);\n}\n\nRow::Reference Row::operator[](const std::string &columnName) const\n{\n    return operator[](columnName.c_str());\n}\n\nRow::Reference Row::at(SizeType index) const\n{\n    if (index >= end_)\n        throw RangeError(\"Row index is out of range\");\n    return Field(*this, index);\n}\n\nRow::Reference Row::at(const char columnName[]) const\n{\n    auto N = result_.columnNumber(columnName);\n    return Field(*this, N);\n}\n\nRow::Reference Row::at(const std::string &columnName) const\n{\n    return at(columnName.c_str());\n}\n\nRow::ConstIterator Row::begin() const noexcept\n{\n    return ConstIterator(*this, 0);\n}\n\nRow::ConstIterator Row::cbegin() const noexcept\n{\n    return begin();\n}\n\nRow::ConstIterator Row::end() const noexcept\n{\n    return ConstIterator(*this, (Field::SizeType)size());\n}\n\nRow::ConstIterator Row::cend() const noexcept\n{\n    return end();\n}\n\nRow::ConstReverseIterator Row::rbegin() const\n{\n    return ConstReverseRowIterator(end());\n}\n\nRow::ConstReverseIterator Row::crbegin() const\n{\n    return rbegin();\n}\n\nRow::ConstReverseIterator Row::rend() const\n{\n    return ConstReverseRowIterator(begin());\n}\n\nRow::ConstReverseIterator Row::crend() const\n{\n    return rend();\n}\n\nRow::ConstIterator Row::ConstReverseIterator::base() const noexcept\n{\n    iterator_type tmp(*this);\n    return ++tmp;\n}\n\nConstRowIterator ConstRowIterator::operator++(int)\n{\n    ConstRowIterator old(*this);\n    ++column_;\n    return old;\n}\n\nConstRowIterator ConstRowIterator::operator--(int)\n{\n    ConstRowIterator old(*this);\n    --column_;\n    return old;\n}\n\nConstReverseRowIterator ConstReverseRowIterator::operator++(int)\n{\n    ConstReverseRowIterator old(*this);\n    iterator_type::operator--();\n    return old;\n}\n\nConstReverseRowIterator ConstReverseRowIterator::operator--(int)\n{\n    ConstReverseRowIterator old(*this);\n    iterator_type::operator++();\n    return old;\n}\n"
  },
  {
    "path": "orm_lib/src/SqlBinder.cc",
    "content": "/**\n *\n *  SqlBinder.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include <drogon/config.h>\n#include <drogon/orm/DbClient.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/utils/Utilities.h>\n#include <future>\n#include <regex>\n#if defined(__cpp_lib_format)\n#include <format>\n#endif\n#if USE_MYSQL\n#include <mysql.h>\n#endif\n\nusing namespace drogon::orm;\nusing namespace drogon::orm::internal;\n\nvoid SqlBinder::exec()\n{\n    execed_ = true;\n    if (mode_ == Mode::NonBlocking)\n    {\n        // nonblocking mode,default mode\n        // Retain shared_ptrs of parameters until we get the result;\n        client_.execSql(\n            sqlViewPtr_,\n            sqlViewLength_,\n            parametersNumber_,\n            std::move(parameters_),\n            std::move(lengths_),\n            std::move(formats_),\n            [holder = std::move(callbackHolder_),\n             objs = std::move(objs_),\n             sqlptr = std::move(sqlPtr_)](const Result &r) mutable {\n                objs.clear();\n                if (holder)\n                {\n                    holder->execCallback(r);\n                }\n            },\n            [exceptCb = std::move(exceptionCallback_),\n             exceptPtrCb = std::move(exceptionPtrCallback_),\n             isExceptPtr =\n                 isExceptionPtr_](const std::exception_ptr &exception) {\n                // LOG_DEBUG<<\"exp callback \"<<isExceptPtr;\n                if (!isExceptPtr)\n                {\n                    if (exceptCb)\n                    {\n                        try\n                        {\n                            std::rethrow_exception(exception);\n                        }\n                        catch (const DrogonDbException &e)\n                        {\n                            exceptCb(e);\n                        }\n                    }\n                }\n                else\n                {\n                    if (exceptPtrCb)\n                        exceptPtrCb(exception);\n                }\n            });\n    }\n    else\n    {\n        // blocking mode\n        std::shared_ptr<std::promise<Result>> pro(new std::promise<Result>);\n        auto f = pro->get_future();\n\n        client_.execSql(\n            sqlViewPtr_,\n            sqlViewLength_,\n            parametersNumber_,\n            std::move(parameters_),\n            std::move(lengths_),\n            std::move(formats_),\n            [pro](const Result &r) { pro->set_value(r); },\n            [pro](const std::exception_ptr &exception) {\n                try\n                {\n                    pro->set_exception(exception);\n                }\n                catch (...)\n                {\n                    assert(0);\n                }\n            });\n\n        try\n        {\n            const Result &v = f.get();\n            if (callbackHolder_)\n            {\n                callbackHolder_->execCallback(v);\n            }\n        }\n        catch (const DrogonDbException &exception)\n        {\n            if (!destructed_)\n            {\n                // throw exception\n                std::rethrow_exception(std::current_exception());\n            }\n            else\n            {\n                if (exceptionCallback_)\n                {\n                    exceptionCallback_(exception);\n                }\n            }\n        }\n    }\n}\n\nSqlBinder::~SqlBinder()\n{\n    destructed_ = true;\n    if (!execed_)\n    {\n        exec();\n    }\n}\n\nSqlBinder &SqlBinder::operator<<(const RawParameter &param)\n{\n    objs_.push_back(param.obj);\n    parameters_.push_back(param.parameter);\n    lengths_.push_back(param.length);\n    formats_.push_back(param.format);\n    ++parametersNumber_;\n    return *this;\n}\n\nSqlBinder &SqlBinder::operator<<(RawParameter &&param)\n{\n    objs_.push_back(std::move(param.obj));\n    parameters_.push_back(std::move(param.parameter));\n    lengths_.push_back(std::move(param.length));\n    formats_.push_back(std::move(param.format));\n    ++parametersNumber_;\n    return *this;\n}\n\nSqlBinder &SqlBinder::operator<<(const std::string_view &str)\n{\n    auto obj = std::make_shared<std::string>(str.data(), str.length());\n    parameters_.push_back(obj->data());\n    lengths_.push_back(static_cast<int>(obj->length()));\n    objs_.push_back(obj);\n    ++parametersNumber_;\n    if (type_ == ClientType::PostgreSQL)\n    {\n        formats_.push_back(0);\n    }\n    else if (type_ == ClientType::Mysql)\n    {\n        formats_.push_back(MySqlString);\n    }\n    else if (type_ == ClientType::Sqlite3)\n    {\n        formats_.push_back(Sqlite3TypeText);\n    }\n    return *this;\n}\n\nSqlBinder &SqlBinder::operator<<(const std::string &str)\n{\n    auto obj = std::make_shared<std::string>(str);\n    objs_.push_back(obj);\n    ++parametersNumber_;\n    parameters_.push_back(obj->data());\n    lengths_.push_back(static_cast<int>(obj->length()));\n    if (type_ == ClientType::PostgreSQL)\n    {\n        formats_.push_back(0);\n    }\n    else if (type_ == ClientType::Mysql)\n    {\n        formats_.push_back(MySqlString);\n    }\n    else if (type_ == ClientType::Sqlite3)\n    {\n        formats_.push_back(Sqlite3TypeText);\n    }\n    return *this;\n}\n\nSqlBinder &SqlBinder::operator<<(std::string &&str)\n{\n    std::shared_ptr<std::string> obj =\n        std::make_shared<std::string>(std::move(str));\n    objs_.push_back(obj);\n    ++parametersNumber_;\n    parameters_.push_back((char *)obj->c_str());\n    lengths_.push_back(static_cast<int>(obj->length()));\n    if (type_ == ClientType::PostgreSQL)\n    {\n        formats_.push_back(0);\n    }\n    else if (type_ == ClientType::Mysql)\n    {\n        formats_.push_back(MySqlString);\n    }\n    else if (type_ == ClientType::Sqlite3)\n    {\n        formats_.push_back(Sqlite3TypeText);\n    }\n    return *this;\n}\n\nSqlBinder &SqlBinder::operator<<(const std::vector<char> &v)\n{\n    std::shared_ptr<std::vector<char>> obj =\n        std::make_shared<std::vector<char>>(v);\n    objs_.push_back(obj);\n    ++parametersNumber_;\n    parameters_.push_back((char *)obj->data());\n    lengths_.push_back(static_cast<int>(obj->size()));\n    if (type_ == ClientType::PostgreSQL)\n    {\n        formats_.push_back(1);\n    }\n    else if (type_ == ClientType::Mysql)\n    {\n        formats_.push_back(MySqlString);\n    }\n    else if (type_ == ClientType::Sqlite3)\n    {\n        formats_.push_back(Sqlite3TypeBlob);\n    }\n    return *this;\n}\n\nSqlBinder &SqlBinder::operator<<(std::vector<char> &&v)\n{\n    std::shared_ptr<std::vector<char>> obj =\n        std::make_shared<std::vector<char>>(std::move(v));\n    objs_.push_back(obj);\n    ++parametersNumber_;\n    parameters_.push_back((char *)obj->data());\n    lengths_.push_back(static_cast<int>(obj->size()));\n    if (type_ == ClientType::PostgreSQL)\n    {\n        formats_.push_back(1);\n    }\n    else if (type_ == ClientType::Mysql)\n    {\n        formats_.push_back(MySqlString);\n    }\n    else if (type_ == ClientType::Sqlite3)\n    {\n        formats_.push_back(Sqlite3TypeBlob);\n    }\n    return *this;\n}\n\nSqlBinder &SqlBinder::operator<<(double f)\n{\n    if (type_ == ClientType::Sqlite3)\n    {\n        ++parametersNumber_;\n        auto obj = std::make_shared<double>(f);\n        objs_.push_back(obj);\n        formats_.push_back(Sqlite3TypeDouble);\n        lengths_.push_back(0);\n        parameters_.push_back((char *)(obj.get()));\n        return *this;\n    }\n\n#if defined(__cpp_lib_format)\n    return operator<<(std::format(\"{:.17g}\", f));\n#else\n    std::stringstream ss;\n    ss << std::setprecision(17) << f;\n    return operator<<(ss.str());\n#endif\n}\n\nSqlBinder &SqlBinder::operator<<(std::nullptr_t)\n{\n    ++parametersNumber_;\n    parameters_.push_back(nullptr);\n    lengths_.push_back(0);\n    if (type_ == ClientType::PostgreSQL)\n    {\n        formats_.push_back(0);\n    }\n    else if (type_ == ClientType::Mysql)\n    {\n        formats_.push_back(MySqlNull);\n    }\n    else if (type_ == ClientType::Sqlite3)\n    {\n        formats_.push_back(Sqlite3TypeNull);\n    }\n    return *this;\n}\n\nSqlBinder &SqlBinder::operator<<(DefaultValue dv)\n{\n    (void)dv;\n    if (type_ == ClientType::PostgreSQL)\n    {\n        std::regex r(\"\\\\$\" + std::to_string(parametersNumber_ + 1) + \"\\\\b\");\n        // initialize with empty, as the next line will make a copy anyway\n        if (!sqlPtr_)\n            sqlPtr_ = std::make_shared<std::string>();\n\n        *sqlPtr_ = std::regex_replace(sqlViewPtr_, r, \"default\");\n\n        // decrement all other $n parameters by 1\n        size_t i = parametersNumber_ + 2;\n        while ((sqlPtr_->find(\"$\" + std::to_string(i))) != std::string::npos)\n        {\n            r = \"\\\\$\" + std::to_string(i) + \"\\\\b\";\n            // use sed format to avoid $n regex group substitution,\n            // and use ->data() to compile in C++14 mode\n            *sqlPtr_ = std::regex_replace(sqlPtr_->data(),\n                                          r,\n                                          \"$\" + std::to_string(i - 1),\n                                          std::regex_constants::format_sed);\n            ++i;\n        }\n        sqlViewPtr_ = sqlPtr_->data();\n        sqlViewLength_ = sqlPtr_->length();\n    }\n    else if (type_ == ClientType::Mysql)\n    {\n        ++parametersNumber_;\n        parameters_.push_back(nullptr);\n        lengths_.push_back(0);\n        formats_.push_back(DrogonDefaultValue);\n    }\n    else if (type_ == ClientType::Sqlite3)\n    {\n        LOG_FATAL << \"default not supported in sqlite3\";\n        exit(1);\n    }\n    return *this;\n}\n\nint SqlBinder::getMysqlTypeBySize(size_t size)\n{\n#if USE_MYSQL\n    switch (size)\n    {\n        case 1:\n            return MySqlTiny;\n            break;\n        case 2:\n            return MySqlShort;\n            break;\n        case 4:\n            return MySqlLong;\n            break;\n        case 8:\n            return MySqlLongLong;\n            break;\n        default:\n            return 0;\n    }\n#else\n    static_cast<void>(size);\n    LOG_FATAL << \"Mysql is not supported!\";\n    exit(1);\n#endif\n}\n"
  },
  {
    "path": "orm_lib/src/TransactionImpl.cc",
    "content": "/**\n *\n *  TransactionImpl.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"TransactionImpl.h\"\n#include \"../../lib/src/TaskTimeoutFlag.h\"\n#include <string_view>\n#include <trantor/utils/Logger.h>\n\nusing namespace drogon::orm;\nusing namespace drogon;\n\nTransactionImpl::TransactionImpl(ClientType type,\n                                 const DbConnectionPtr &connPtr,\n                                 std::function<void(bool)> commitCallback,\n                                 std::function<void()> usedUpCallback)\n    : connectionPtr_(connPtr),\n      usedUpCallback_(std::move(usedUpCallback)),\n      loop_(connPtr->loop()),\n      commitCallback_(std::move(commitCallback))\n{\n    type_ = type;\n}\n\nTransactionImpl::~TransactionImpl()\n{\n    LOG_TRACE << \"Destruct\";\n    assert(sqlCmdBuffer_.empty());\n    if (!isCommitedOrRolledback_)\n    {\n        auto loop = connectionPtr_->loop();\n        loop->queueInLoop([conn = connectionPtr_,\n                           ucb = std::move(usedUpCallback_),\n                           commitCb = std::move(commitCallback_)]() {\n            conn->setIdleCallback([ucb = std::move(ucb)]() {\n                if (ucb)\n                    ucb();\n            });\n            conn->execSql(\n                \"commit\",\n                0,\n                {},\n                {},\n                {},\n                [commitCb](const Result &) {\n                    LOG_TRACE << \"Transaction committed!\";\n                    if (commitCb)\n                    {\n                        commitCb(true);\n                    }\n                },\n                [commitCb](const std::exception_ptr &ePtr) {\n                    try\n                    {\n                        std::rethrow_exception(ePtr);\n                    }\n                    catch (const DrogonDbException &e)\n                    {\n                        LOG_ERROR << \"Transaction submission failed:\"\n                                  << e.base().what();\n                        if (commitCb)\n                        {\n                            commitCb(false);\n                        }\n                    }\n                });\n        });\n    }\n    else\n    {\n        if (usedUpCallback_)\n        {\n            usedUpCallback_();\n        }\n    }\n}\n\nvoid TransactionImpl::execSqlInLoop(\n    std::string_view &&sql,\n    size_t paraNum,\n    std::vector<const char *> &&parameters,\n    std::vector<int> &&length,\n    std::vector<int> &&format,\n    ResultCallback &&rcb,\n    std::function<void(const std::exception_ptr &)> &&exceptCallback)\n{\n    loop_->assertInLoopThread();\n    if (!isCommitedOrRolledback_)\n    {\n        if (timeout_ > 0.0)\n        {\n            execSqlInLoopWithTimeout(std::move(sql),\n                                     paraNum,\n                                     std::move(parameters),\n                                     std::move(length),\n                                     std::move(format),\n                                     std::move(rcb),\n                                     std::move(exceptCallback));\n            return;\n        }\n        auto thisPtr = shared_from_this();\n        if (!isWorking_)\n        {\n            isWorking_ = true;\n            thisPtr_ = thisPtr;\n            connectionPtr_->execSql(std::move(sql),\n                                    paraNum,\n                                    std::move(parameters),\n                                    std::move(length),\n                                    std::move(format),\n                                    std::move(rcb),\n                                    [exceptCallback,\n                                     thisPtr](const std::exception_ptr &ePtr) {\n                                        thisPtr->rollback();\n                                        if (exceptCallback)\n                                            exceptCallback(ePtr);\n                                    });\n        }\n        else\n        {\n            // push sql cmd to buffer;\n            auto cmdPtr = std::make_shared<SqlCmd>();\n            cmdPtr->sql_ = std::move(sql);\n            cmdPtr->parametersNumber_ = paraNum;\n            cmdPtr->parameters_ = std::move(parameters);\n            cmdPtr->lengths_ = std::move(length);\n            cmdPtr->formats_ = std::move(format);\n            cmdPtr->callback_ = std::move(rcb);\n            cmdPtr->exceptionCallback_ = std::move(exceptCallback);\n            cmdPtr->thisPtr_ = thisPtr;\n            thisPtr->sqlCmdBuffer_.push_back(std::move(cmdPtr));\n        }\n    }\n    else\n    {\n        // The transaction has been rolled back;\n        auto exceptPtr = std::make_exception_ptr(\n            TransactionRollback(\"The transaction has been rolled back\"));\n        exceptCallback(exceptPtr);\n    }\n}\n\nvoid TransactionImpl::rollback()\n{\n    auto thisPtr = shared_from_this();\n\n    loop_->runInLoop([thisPtr]() {\n        if (thisPtr->isCommitedOrRolledback_)\n            return;\n        if (thisPtr->isWorking_)\n        {\n            // push sql cmd to buffer;\n            auto cmdPtr = std::make_shared<SqlCmd>();\n            cmdPtr->sql_ = \"rollback\";\n            cmdPtr->parametersNumber_ = 0;\n            cmdPtr->callback_ = [thisPtr](const Result &) {\n                LOG_DEBUG << \"Transaction roll back!\";\n                thisPtr->isCommitedOrRolledback_ = true;\n            };\n            cmdPtr->exceptionCallback_ = [thisPtr](const std::exception_ptr &) {\n                // clearupCb();\n                thisPtr->isCommitedOrRolledback_ = true;\n                LOG_ERROR << \"Transaction roll back error\";\n            };\n            cmdPtr->isRollbackCmd_ = true;\n            // Rollback cmd should be executed firstly, so we push it in front\n            // of the list\n            thisPtr->sqlCmdBuffer_.push_front(std::move(cmdPtr));\n            return;\n        }\n        thisPtr->isWorking_ = true;\n        thisPtr->thisPtr_ = thisPtr;\n        thisPtr->connectionPtr_->execSql(\n            \"rollback\",\n            0,\n            {},\n            {},\n            {},\n            [thisPtr](const Result &) {\n                LOG_TRACE << \"Transaction roll back!\";\n                thisPtr->isCommitedOrRolledback_ = true;\n                // clearupCb();\n            },\n            [thisPtr](const std::exception_ptr &) {\n                // clearupCb();\n                LOG_ERROR << \"Transaction roll back error\";\n                thisPtr->isCommitedOrRolledback_ = true;\n            });\n    });\n}\n\nvoid TransactionImpl::execNewTask()\n{\n    loop_->assertInLoopThread();\n    thisPtr_.reset();\n    assert(isWorking_);\n    if (!isCommitedOrRolledback_)\n    {\n        auto thisPtr = shared_from_this();\n        if (!sqlCmdBuffer_.empty())\n        {\n            auto cmd = std::move(sqlCmdBuffer_.front());\n            sqlCmdBuffer_.pop_front();\n            auto conn = connectionPtr_;\n            conn->execSql(\n                std::move(cmd->sql_),\n                cmd->parametersNumber_,\n                std::move(cmd->parameters_),\n                std::move(cmd->lengths_),\n                std::move(cmd->formats_),\n                [callback = std::move(cmd->callback_), cmd, thisPtr](\n                    const Result &r) {\n                    if (cmd->isRollbackCmd_)\n                    {\n                        thisPtr->isCommitedOrRolledback_ = true;\n                    }\n                    if (callback)\n                        callback(r);\n                },\n                [cmd, thisPtr](const std::exception_ptr &ePtr) {\n                    if (!cmd->isRollbackCmd_)\n                        thisPtr->rollback();\n                    else\n                    {\n                        thisPtr->isCommitedOrRolledback_ = true;\n                    }\n                    if (cmd->exceptionCallback_)\n                        cmd->exceptionCallback_(ePtr);\n                });\n            return;\n        }\n        isWorking_ = false;\n    }\n    else\n    {\n        isWorking_ = false;\n        if (!sqlCmdBuffer_.empty())\n        {\n            auto exceptPtr = std::make_exception_ptr(\n                TransactionRollback(\"The transaction has been rolled back\"));\n            for (auto const &cmd : sqlCmdBuffer_)\n            {\n                if (cmd->exceptionCallback_)\n                {\n                    cmd->exceptionCallback_(exceptPtr);\n                }\n            }\n            sqlCmdBuffer_.clear();\n        }\n        if (usedUpCallback_)\n        {\n            usedUpCallback_();\n            usedUpCallback_ = std::function<void()>();\n        }\n    }\n}\n\nvoid TransactionImpl::doBegin()\n{\n    loop_->queueInLoop([thisPtr = shared_from_this()]() {\n        std::weak_ptr<TransactionImpl> weakPtr = thisPtr;\n        thisPtr->connectionPtr_->setIdleCallback([weakPtr]() {\n            auto thisPtr = weakPtr.lock();\n            if (!thisPtr)\n                return;\n            thisPtr->execNewTask();\n        });\n        assert(!thisPtr->isWorking_);\n        assert(!thisPtr->isCommitedOrRolledback_);\n        thisPtr->isWorking_ = true;\n        thisPtr->thisPtr_ = thisPtr;\n        thisPtr->connectionPtr_->execSql(\n            \"begin\",\n            0,\n            {},\n            {},\n            {},\n            [](const Result &) { LOG_TRACE << \"Transaction begin!\"; },\n            [thisPtr](const std::exception_ptr &) {\n                LOG_ERROR << \"Error occurred in transaction begin\";\n                thisPtr->isCommitedOrRolledback_ = true;\n            });\n    });\n}\n\nvoid TransactionImpl::execSqlInLoopWithTimeout(\n    std::string_view &&sql,\n    size_t paraNum,\n    std::vector<const char *> &&parameters,\n    std::vector<int> &&length,\n    std::vector<int> &&format,\n    ResultCallback &&rcb,\n    std::function<void(const std::exception_ptr &)> &&ecb)\n{\n    auto thisPtr = shared_from_this();\n    std::weak_ptr<TransactionImpl> weakPtr = thisPtr;\n    auto commandPtr = std::make_shared<std::weak_ptr<SqlCmd>>();\n    auto ecpPtr =\n        std::make_shared<std::function<void(const std::exception_ptr &)>>(\n            std::move(ecb));\n    auto timeoutFlagPtr = std::make_shared<drogon::TaskTimeoutFlag>(\n        loop_,\n        std::chrono::duration<double>(timeout_),\n        [commandPtr, weakPtr, ecpPtr]() {\n            auto thisPtr = weakPtr.lock();\n            if (!thisPtr)\n                return;\n            auto cmdPtr = (*commandPtr).lock();\n            if (cmdPtr)\n            {\n                for (auto iter = thisPtr->sqlCmdBuffer_.begin();\n                     iter != thisPtr->sqlCmdBuffer_.end();\n                     ++iter)\n                {\n                    if (cmdPtr == *iter)\n                    {\n                        thisPtr->sqlCmdBuffer_.erase(iter);\n                        break;\n                    }\n                }\n            }\n            thisPtr->rollback();\n            if (*ecpPtr)\n            {\n                (*ecpPtr)(std::make_exception_ptr(\n                    TimeoutError(\"SQL execution timeout\")));\n            }\n        });\n    auto resultCallback = [rcb = std::move(rcb),\n                           timeoutFlagPtr](const drogon::orm::Result &result) {\n        if (timeoutFlagPtr->done())\n            return;\n        rcb(result);\n    };\n    if (!isWorking_)\n    {\n        isWorking_ = true;\n        thisPtr_ = thisPtr;\n        connectionPtr_->execSql(std::move(sql),\n                                paraNum,\n                                std::move(parameters),\n                                std::move(length),\n                                std::move(format),\n                                std::move(resultCallback),\n                                [ecpPtr, timeoutFlagPtr, thisPtr](\n                                    const std::exception_ptr &ePtr) {\n                                    thisPtr->rollback();\n                                    if (timeoutFlagPtr->done())\n                                        return;\n                                    if (*ecpPtr)\n                                    {\n                                        (*ecpPtr)(ePtr);\n                                    }\n                                });\n    }\n    else\n    {\n        // push sql cmd to buffer;\n        auto cmdPtr = std::make_shared<SqlCmd>();\n        cmdPtr->sql_ = std::move(sql);\n        cmdPtr->parametersNumber_ = paraNum;\n        cmdPtr->parameters_ = std::move(parameters);\n        cmdPtr->lengths_ = std::move(length);\n        cmdPtr->formats_ = std::move(format);\n        cmdPtr->callback_ = std::move(resultCallback);\n        cmdPtr->exceptionCallback_ =\n            [ecpPtr, timeoutFlagPtr](const std::exception_ptr &ePtr) {\n                if (timeoutFlagPtr->done())\n                    return;\n                if (*ecpPtr)\n                {\n                    (*ecpPtr)(ePtr);\n                }\n            };\n\n        cmdPtr->thisPtr_ = thisPtr;\n        thisPtr->sqlCmdBuffer_.push_back(cmdPtr);\n        *commandPtr = cmdPtr;\n    }\n    timeoutFlagPtr->runTimer();\n}\n"
  },
  {
    "path": "orm_lib/src/TransactionImpl.h",
    "content": "/**\n *\n *  TransactionImpl.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"DbConnection.h\"\n#include <drogon/orm/DbClient.h>\n#include <functional>\n#include <list>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass TransactionImpl : public Transaction,\n                        public std::enable_shared_from_this<TransactionImpl>\n{\n  public:\n    TransactionImpl(ClientType type,\n                    const DbConnectionPtr &connPtr,\n                    std::function<void(bool)> commitCallback,\n                    std::function<void()> usedUpCallback);\n    ~TransactionImpl() override;\n    void rollback() override;\n\n    void setCommitCallback(\n        const std::function<void(bool)> &commitCallback) override\n    {\n        commitCallback_ = commitCallback;\n    }\n\n    bool hasAvailableConnections() const noexcept override\n    {\n        return connectionPtr_->status() == ConnectStatus::Ok;\n    }\n\n    void setTimeout(double timeout) override\n    {\n        timeout_ = timeout;\n    }\n\n  private:\n    DbConnectionPtr connectionPtr_;\n\n    void execSql(const char *sql,\n                 size_t sqlLength,\n                 size_t paraNum,\n                 std::vector<const char *> &&parameters,\n                 std::vector<int> &&length,\n                 std::vector<int> &&format,\n                 ResultCallback &&rcb,\n                 std::function<void(const std::exception_ptr &)>\n                     &&exceptCallback) override\n    {\n        if (loop_->isInLoopThread())\n        {\n            execSqlInLoop(std::string_view{sql, sqlLength},\n                          paraNum,\n                          std::move(parameters),\n                          std::move(length),\n                          std::move(format),\n                          std::move(rcb),\n                          std::move(exceptCallback));\n        }\n        else\n        {\n            loop_->queueInLoop(\n                [thisPtr = shared_from_this(),\n                 sql = std::string_view{sql, sqlLength},\n                 paraNum,\n                 parameters = std::move(parameters),\n                 length = std::move(length),\n                 format = std::move(format),\n                 rcb = std::move(rcb),\n                 exceptCallback = std::move(exceptCallback)]() mutable {\n                    thisPtr->execSqlInLoop(std::move(sql),\n                                           paraNum,\n                                           std::move(parameters),\n                                           std::move(length),\n                                           std::move(format),\n                                           std::move(rcb),\n                                           std::move(exceptCallback));\n                });\n        }\n    }\n\n    void execSqlInLoop(\n        std::string_view &&sql,\n        size_t paraNum,\n        std::vector<const char *> &&parameters,\n        std::vector<int> &&length,\n        std::vector<int> &&format,\n        ResultCallback &&rcb,\n        std::function<void(const std::exception_ptr &)> &&exceptCallback);\n    void execSqlInLoopWithTimeout(\n        std::string_view &&sql,\n        size_t paraNum,\n        std::vector<const char *> &&parameters,\n        std::vector<int> &&length,\n        std::vector<int> &&format,\n        ResultCallback &&rcb,\n        std::function<void(const std::exception_ptr &)> &&exceptCallback);\n\n    std::shared_ptr<Transaction> newTransaction(\n        const std::function<void(bool)> &) noexcept(false) override\n    {\n        return shared_from_this();\n    }\n\n    void newTransactionAsync(\n        const std::function<void(const std::shared_ptr<Transaction> &)>\n            &callback) override\n    {\n        callback(shared_from_this());\n    }\n\n    std::function<void()> usedUpCallback_;\n    bool isCommitedOrRolledback_{false};\n    bool isWorking_{false};\n    void execNewTask();\n\n    struct SqlCmd\n    {\n        std::string_view sql_;\n        size_t parametersNumber_;\n        std::vector<const char *> parameters_;\n        std::vector<int> lengths_;\n        std::vector<int> formats_;\n        QueryCallback callback_;\n        ExceptPtrCallback exceptionCallback_;\n        bool isRollbackCmd_{false};\n        std::shared_ptr<TransactionImpl> thisPtr_;\n    };\n\n    using SqlCmdPtr = std::shared_ptr<SqlCmd>;\n    std::list<SqlCmdPtr> sqlCmdBuffer_;\n    //   std::mutex _bufferMutex;\n    friend class DbClientImpl;\n    friend class DbClientLockFree;\n    void doBegin();\n    trantor::EventLoop *loop_;\n    std::function<void(bool)> commitCallback_;\n    std::shared_ptr<TransactionImpl> thisPtr_;\n    double timeout_{-1.0};\n};\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/src/mysql_impl/MysqlConnection.cc",
    "content": "/**\n *\n *  @file MysqlConnection.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"MysqlConnection.h\"\n#include \"MysqlResultImpl.h\"\n#include <algorithm>\n#include <exception>\n#include <drogon/orm/DbTypes.h>\n#include <drogon/utils/Utilities.h>\n#include <string_view>\n#include <errmsg.h>\n#ifndef _WIN32\n#include <poll.h>\n#else\n#define POLLIN (1U << 0)\n#define POLLPRI (1U << 1)\n#define POLLOUT (1U << 2)\n#endif\n#include <regex>\n\nusing namespace drogon;\nusing namespace drogon::orm;\n\nnamespace drogon\n{\nnamespace orm\n{\nResult makeResult(std::shared_ptr<MYSQL_RES> &&r = nullptr,\n                  Result::SizeType affectedRows = 0,\n                  unsigned long long insertId = 0)\n{\n    return Result{std::make_shared<MysqlResultImpl>(std::move(r),\n                                                    affectedRows,\n                                                    insertId)};\n}\n\n}  // namespace orm\n}  // namespace drogon\n\nMysqlConnection::MysqlConnection(trantor::EventLoop *loop,\n                                 const std::string &connInfo)\n    : DbConnection(loop),\n      mysqlPtr_(std::shared_ptr<MYSQL>(new MYSQL, [](MYSQL *p) {\n          mysql_close(p);\n          delete p;\n      }))\n{\n    static MysqlEnv env;\n    static thread_local MysqlThreadEnv threadEnv;\n    mysql_init(mysqlPtr_.get());\n    mysql_options(mysqlPtr_.get(), MYSQL_OPT_NONBLOCK, nullptr);\n#ifdef HAS_MYSQL_OPTIONSV\n    mysql_optionsv(mysqlPtr_.get(), MYSQL_OPT_RECONNECT, &reconnect_);\n#endif\n    // Get the key and value\n    auto connParams = parseConnString(connInfo);\n    for (auto const &kv : connParams)\n    {\n        auto key = kv.first;\n        auto value = kv.second;\n\n        std::transform(key.begin(),\n                       key.end(),\n                       key.begin(),\n                       [](unsigned char c) { return tolower(c); });\n        // LOG_TRACE << key << \"=\" << value;\n        if (key == \"host\")\n        {\n            host_ = value;\n        }\n        else if (key == \"user\")\n        {\n            user_ = value;\n        }\n        else if (key == \"dbname\")\n        {\n            // LOG_DEBUG << \"database:[\" << value << \"]\";\n            dbname_ = value;\n        }\n        else if (key == \"port\")\n        {\n            port_ = value;\n        }\n        else if (key == \"password\")\n        {\n            passwd_ = value;\n        }\n        else if (key == \"client_encoding\")\n        {\n            characterSet_ = value;\n        }\n    }\n}\n\nvoid MysqlConnection::init()\n{\n    loop_->queueInLoop([this]() {\n        MYSQL *ret;\n        status_ = ConnectStatus::Connecting;\n        waitStatus_ =\n            mysql_real_connect_start(&ret,\n                                     mysqlPtr_.get(),\n                                     host_.empty() ? nullptr : host_.c_str(),\n                                     user_.empty() ? nullptr : user_.c_str(),\n                                     passwd_.empty() ? nullptr\n                                                     : passwd_.c_str(),\n                                     dbname_.empty() ? nullptr\n                                                     : dbname_.c_str(),\n                                     port_.empty() ? 3306 : atol(port_.c_str()),\n                                     nullptr,\n                                     0);\n        // LOG_DEBUG << ret;\n        auto fd = mysql_get_socket(mysqlPtr_.get());\n        if (fd < 0)\n        {\n            LOG_ERROR << \"Connection with MySQL could not be established\";\n            if (closeCallback_)\n            {\n                auto thisPtr = shared_from_this();\n                closeCallback_(thisPtr);\n            }\n            return;\n        }\n        channelPtr_ = std::make_unique<trantor::Channel>(loop_, fd);\n        channelPtr_->setEventCallback([this]() { handleEvent(); });\n        setChannel();\n    });\n}\n\nvoid MysqlConnection::setChannel()\n{\n    if ((waitStatus_ & MYSQL_WAIT_READ) || (waitStatus_ & MYSQL_WAIT_EXCEPT))\n    {\n        if (!channelPtr_->isReading())\n            channelPtr_->enableReading();\n    }\n    if (waitStatus_ & MYSQL_WAIT_WRITE)\n    {\n        if (!channelPtr_->isWriting())\n            channelPtr_->enableWriting();\n    }\n    else\n    {\n        if (channelPtr_->isWriting())\n            channelPtr_->disableWriting();\n    }\n    if (waitStatus_ & MYSQL_WAIT_TIMEOUT)\n    {\n        auto timeout = mysql_get_timeout_value(mysqlPtr_.get());\n        auto thisPtr = shared_from_this();\n        loop_->runAfter(timeout, [thisPtr]() { thisPtr->handleTimeout(); });\n    }\n}\n\nvoid MysqlConnection::handleClosed()\n{\n    loop_->assertInLoopThread();\n    if (status_ == ConnectStatus::Bad)\n        return;\n    status_ = ConnectStatus::Bad;\n    channelPtr_->disableAll();\n    channelPtr_->remove();\n    assert(closeCallback_);\n    auto thisPtr = shared_from_this();\n    closeCallback_(thisPtr);\n}\n\nvoid MysqlConnection::disconnect()\n{\n    auto thisPtr = shared_from_this();\n    std::promise<int> pro;\n    auto f = pro.get_future();\n    loop_->runInLoop([thisPtr, &pro]() {\n        thisPtr->status_ = ConnectStatus::Bad;\n        thisPtr->channelPtr_->disableAll();\n        thisPtr->channelPtr_->remove();\n        thisPtr->mysqlPtr_.reset();\n        pro.set_value(1);\n    });\n    f.get();\n}\n\nvoid MysqlConnection::handleTimeout()\n{\n    int status = 0;\n    status |= MYSQL_WAIT_TIMEOUT;\n    MYSQL *ret;\n    if (status_ == ConnectStatus::Connecting)\n    {\n        waitStatus_ = mysql_real_connect_cont(&ret, mysqlPtr_.get(), status);\n        if (waitStatus_ == 0)\n        {\n            auto errorNo = mysql_errno(mysqlPtr_.get());\n            if (!ret && errorNo)\n            {\n                LOG_ERROR << \"Error(\" << errorNo << \") \\\"\"\n                          << mysql_error(mysqlPtr_.get()) << \"\\\"\";\n                LOG_ERROR << \"Failed to mysql_real_connect()\";\n                handleClosed();\n                return;\n            }\n            // I don't think the programe can run to here.\n            if (characterSet_.empty())\n            {\n                status_ = ConnectStatus::Ok;\n                if (okCallback_)\n                {\n                    auto thisPtr = shared_from_this();\n                    okCallback_(thisPtr);\n                }\n            }\n            else\n            {\n                startSetCharacterSet();\n                return;\n            }\n        }\n        setChannel();\n    }\n    else if (status_ == ConnectStatus::SettingCharacterSet)\n    {\n        continueSetCharacterSet(status);\n    }\n    else if (status_ == ConnectStatus::Ok)\n    {\n    }\n}\n\nvoid MysqlConnection::handleCmd(int status)\n{\n    switch (execStatus_)\n    {\n        case ExecStatus::RealQuery:\n        {\n            int err = 0;\n            waitStatus_ = mysql_real_query_cont(&err, mysqlPtr_.get(), status);\n            LOG_TRACE << \"real_query:\" << waitStatus_;\n            if (waitStatus_ == 0)\n            {\n                if (err)\n                {\n                    execStatus_ = ExecStatus::None;\n                    LOG_ERROR << \"error:\" << err << \" status:\" << status;\n                    outputError();\n                    return;\n                }\n                startStoreResult(false);\n            }\n            setChannel();\n            break;\n        }\n        case ExecStatus::StoreResult:\n        {\n            MYSQL_RES *ret;\n            waitStatus_ =\n                mysql_store_result_cont(&ret, mysqlPtr_.get(), status);\n            LOG_TRACE << \"store_result:\" << waitStatus_;\n            if (waitStatus_ == 0)\n            {\n                if (!ret && mysql_errno(mysqlPtr_.get()))\n                {\n                    execStatus_ = ExecStatus::None;\n                    LOG_ERROR << \"error\";\n                    outputError();\n                    return;\n                }\n                getResult(ret);\n            }\n            setChannel();\n            break;\n        }\n        case ExecStatus::NextResult:\n        {\n            int err;\n            waitStatus_ = mysql_next_result_cont(&err, mysqlPtr_.get(), status);\n            if (waitStatus_ == 0)\n            {\n                if (err)\n                {\n                    execStatus_ = ExecStatus::None;\n                    LOG_ERROR << \"error:\" << err << \" status:\" << status;\n                    outputError();\n                    return;\n                }\n                startStoreResult(false);\n            }\n            setChannel();\n            break;\n        }\n        case ExecStatus::None:\n        {\n            // Connection closed!\n            if (waitStatus_ == 0)\n                handleClosed();\n            break;\n        }\n        default:\n            return;\n    }\n}\n\nvoid MysqlConnection::handleEvent()\n{\n    int status = 0;\n    auto revents = channelPtr_->revents();\n    if (revents & POLLIN)\n        status |= MYSQL_WAIT_READ;\n    if (revents & POLLOUT)\n        status |= MYSQL_WAIT_WRITE;\n    if (revents & POLLPRI)\n        status |= MYSQL_WAIT_EXCEPT;\n    status = (status & waitStatus_);\n    MYSQL *ret;\n    if (status_ == ConnectStatus::Connecting)\n    {\n        waitStatus_ = mysql_real_connect_cont(&ret, mysqlPtr_.get(), status);\n        if (waitStatus_ == 0)\n        {\n            auto errorNo = mysql_errno(mysqlPtr_.get());\n            if (!ret && errorNo)\n            {\n                LOG_ERROR << \"Error(\" << errorNo << \") \\\"\"\n                          << mysql_error(mysqlPtr_.get()) << \"\\\"\";\n                LOG_ERROR << \"Failed to mysql_real_connect()\";\n                handleClosed();\n                return;\n            }\n            if (characterSet_.empty())\n            {\n                status_ = ConnectStatus::Ok;\n                if (okCallback_)\n                {\n                    auto thisPtr = shared_from_this();\n                    okCallback_(thisPtr);\n                }\n            }\n            else\n            {\n                startSetCharacterSet();\n                return;\n            }\n        }\n        setChannel();\n    }\n    else if (status_ == ConnectStatus::Ok)\n    {\n        handleCmd(status);\n    }\n    else if (status_ == ConnectStatus::SettingCharacterSet)\n    {\n        continueSetCharacterSet(status);\n    }\n}\n\nvoid MysqlConnection::continueSetCharacterSet(int status)\n{\n    int err;\n    waitStatus_ = mysql_set_character_set_cont(&err, mysqlPtr_.get(), status);\n    if (waitStatus_ == 0)\n    {\n        if (err)\n        {\n            LOG_ERROR << \"Error(\" << err << \") \\\"\"\n                      << mysql_error(mysqlPtr_.get()) << \"\\\"\";\n            LOG_ERROR << \"Failed to mysql_set_character_set_cont()\";\n            handleClosed();\n            return;\n        }\n        status_ = ConnectStatus::Ok;\n        if (okCallback_)\n        {\n            auto thisPtr = shared_from_this();\n            okCallback_(thisPtr);\n        }\n    }\n    setChannel();\n}\n\nvoid MysqlConnection::startSetCharacterSet()\n{\n    int err;\n    waitStatus_ = mysql_set_character_set_start(&err,\n                                                mysqlPtr_.get(),\n                                                characterSet_.data());\n    if (waitStatus_ == 0)\n    {\n        if (err)\n        {\n            LOG_ERROR << \"Error(\" << err << \") \\\"\"\n                      << mysql_error(mysqlPtr_.get()) << \"\\\"\";\n            LOG_ERROR << \"Failed to mysql_set_character_set_start()\";\n            handleClosed();\n            return;\n        }\n        status_ = ConnectStatus::Ok;\n        if (okCallback_)\n        {\n            auto thisPtr = shared_from_this();\n            okCallback_(thisPtr);\n        }\n    }\n    else\n    {\n        status_ = ConnectStatus::SettingCharacterSet;\n    }\n    setChannel();\n}\n\nvoid MysqlConnection::execSqlInLoop(\n    std::string_view &&sql,\n    size_t paraNum,\n    std::vector<const char *> &&parameters,\n    std::vector<int> &&length,\n    std::vector<int> &&format,\n    ResultCallback &&rcb,\n    std::function<void(const std::exception_ptr &)> &&exceptCallback)\n{\n    LOG_TRACE << sql;\n    assert(paraNum == parameters.size());\n    assert(paraNum == length.size());\n    assert(paraNum == format.size());\n    assert(rcb);\n    assert(!isWorking_);\n    assert(!sql.empty());\n    if (status_ != ConnectStatus::Ok)\n    {\n        LOG_ERROR << \"Connection is not ready\";\n        auto exceptPtr =\n            std::make_exception_ptr(drogon::orm::BrokenConnection());\n        exceptCallback(exceptPtr);\n        return;\n    }\n    callback_ = std::move(rcb);\n    isWorking_ = true;\n    exceptionCallback_ = std::move(exceptCallback);\n    sql_.clear();\n    if (paraNum > 0)\n    {\n        std::string::size_type pos = 0;\n        std::string::size_type seekPos = std::string::npos;\n        for (size_t i = 0; i < paraNum; ++i)\n        {\n            seekPos = sql.find('?', pos);\n            if (seekPos == std::string::npos)\n            {\n                auto sub = sql.substr(pos);\n                sql_.append(sub.data(), sub.length());\n                pos = seekPos;\n                break;\n            }\n            else\n            {\n                auto sub = sql.substr(pos, seekPos - pos);\n                sql_.append(sub.data(), sub.length());\n                pos = seekPos + 1;\n                switch (format[i])\n                {\n                    case internal::MySqlTiny:\n                        sql_.append(std::to_string(*((char *)parameters[i])));\n                        break;\n                    case internal::MySqlShort:\n                        sql_.append(std::to_string(*((short *)parameters[i])));\n                        break;\n                    case internal::MySqlLong:\n                        sql_.append(\n                            std::to_string(*((int32_t *)parameters[i])));\n                        break;\n                    case internal::MySqlLongLong:\n                        sql_.append(\n                            std::to_string(*((int64_t *)parameters[i])));\n                        break;\n                    case internal::MySqlUTiny:\n                        sql_.append(\n                            std::to_string(*((unsigned char *)parameters[i])));\n                        break;\n                    case internal::MySqlUShort:\n                        sql_.append(\n                            std::to_string(*((unsigned short *)parameters[i])));\n                        break;\n                    case internal::MySqlULong:\n                        sql_.append(\n                            std::to_string(*((uint32_t *)parameters[i])));\n                        break;\n                    case internal::MySqlULongLong:\n                        sql_.append(\n                            std::to_string(*((uint64_t *)parameters[i])));\n                        break;\n                    case internal::MySqlNull:\n                        sql_.append(\"NULL\");\n                        break;\n                    case internal::MySqlString:\n                    {\n                        sql_.append(\"'\");\n                        std::string to(length[i] * 2, '\\0');\n                        auto len = mysql_real_escape_string(mysqlPtr_.get(),\n                                                            (char *)to.c_str(),\n                                                            parameters[i],\n                                                            length[i]);\n                        to.resize(len);\n                        sql_.append(to);\n                        sql_.append(\"'\");\n                    }\n                    break;\n                    case internal::DrogonDefaultValue:\n                        sql_.append(\"default\");\n                        break;\n                    default:\n                        LOG_FATAL\n                            << \"MySQL does not recognize the parameter type\";\n                        abort();\n                        break;\n                }\n            }\n        }\n        if (pos < sql.length())\n        {\n            auto sub = sql.substr(pos);\n            sql_.append(sub.data(), sub.length());\n        }\n    }\n    else\n    {\n        sql_ = std::string(sql.data(), sql.length());\n    }\n    startQuery();\n    setChannel();\n}\n\nvoid MysqlConnection::outputError()\n{\n    channelPtr_->disableAll();\n    auto errorNo = mysql_errno(mysqlPtr_.get());\n    LOG_ERROR << \"Error(\" << errorNo << \") [\" << mysql_sqlstate(mysqlPtr_.get())\n              << \"] \\\"\" << mysql_error(mysqlPtr_.get()) << \"\\\"\";\n    LOG_ERROR << \"sql:\" << sql_;\n    if (isWorking_)\n    {\n        // TODO: exception type\n        auto exceptPtr = std::make_exception_ptr(\n            SqlError(mysql_error(mysqlPtr_.get()), sql_, errorNo, 0));\n        exceptionCallback_(exceptPtr);\n        exceptionCallback_ = nullptr;\n\n        callback_ = nullptr;\n        isWorking_ = false;\n        if (errorNo != CR_SERVER_GONE_ERROR && errorNo != CR_SERVER_LOST)\n        {\n            idleCb_();\n        }\n    }\n    if (errorNo == CR_SERVER_GONE_ERROR || errorNo == CR_SERVER_LOST)\n    {\n        handleClosed();\n    }\n}\n\nvoid MysqlConnection::startQuery()\n{\n    int err;\n    // int mysql_real_query_start(int *ret, MYSQL *mysql, const char *q,\n    // unsigned long length)\n    waitStatus_ = mysql_real_query_start(&err,\n                                         mysqlPtr_.get(),\n                                         sql_.c_str(),\n                                         sql_.length());\n    LOG_TRACE << \"real_query:\" << waitStatus_;\n    execStatus_ = ExecStatus::RealQuery;\n    if (waitStatus_ == 0)\n    {\n        if (err)\n        {\n            LOG_ERROR << \"error\";\n            loop_->queueInLoop(\n                [thisPtr = shared_from_this()] { thisPtr->outputError(); });\n            return;\n        }\n        startStoreResult(true);\n    }\n}\n\nvoid MysqlConnection::startStoreResult(bool queueInLoop)\n{\n    MYSQL_RES *ret;\n    execStatus_ = ExecStatus::StoreResult;\n    waitStatus_ = mysql_store_result_start(&ret, mysqlPtr_.get());\n    LOG_TRACE << \"store_result:\" << waitStatus_;\n    if (waitStatus_ == 0)\n    {\n        execStatus_ = ExecStatus::None;\n        if (!ret && mysql_errno(mysqlPtr_.get()))\n        {\n            if (queueInLoop)\n            {\n                loop_->queueInLoop(\n                    [thisPtr = shared_from_this()] { thisPtr->outputError(); });\n            }\n            else\n            {\n                outputError();\n            }\n            return;\n        }\n        if (queueInLoop)\n        {\n            loop_->queueInLoop([thisPtr = shared_from_this(), ret] {\n                thisPtr->getResult(ret);\n            });\n        }\n        else\n        {\n            getResult(ret);\n        }\n    }\n}\n\nvoid MysqlConnection::getResult(MYSQL_RES *res)\n{\n    auto resultPtr = std::shared_ptr<MYSQL_RES>(res, [](MYSQL_RES *r) {\n        mysql_free_result(r);\n    });\n    auto Result = makeResult(std::move(resultPtr),\n                             mysql_affected_rows(mysqlPtr_.get()),\n                             mysql_insert_id(mysqlPtr_.get()));\n    if (isWorking_)\n    {\n        callback_(Result);\n        if (!mysql_more_results(mysqlPtr_.get()))\n        {\n            callback_ = nullptr;\n            exceptionCallback_ = nullptr;\n            isWorking_ = false;\n            idleCb_();\n        }\n        else\n        {\n            execStatus_ = ExecStatus::NextResult;\n            int err;\n            waitStatus_ = mysql_next_result_start(&err, mysqlPtr_.get());\n            if (waitStatus_ == 0)\n            {\n                if (err)\n                {\n                    execStatus_ = ExecStatus::None;\n                    LOG_ERROR << \"error:\" << err;\n                    outputError();\n                    return;\n                }\n                startStoreResult(false);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "orm_lib/src/mysql_impl/MysqlConnection.h",
    "content": "/**\n *\n *  MysqlConnection.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"../DbConnection.h\"\n#include <drogon/orm/DbClient.h>\n#include <trantor/net/EventLoop.h>\n#include <trantor/net/Channel.h>\n#include <trantor/utils/NonCopyable.h>\n#include <functional>\n#include <iostream>\n#include <memory>\n#include <mysql.h>\n#include <string>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass MysqlConnection;\nusing MysqlConnectionPtr = std::shared_ptr<MysqlConnection>;\n\nclass MysqlConnection : public DbConnection,\n                        public std::enable_shared_from_this<MysqlConnection>\n{\n  public:\n    MysqlConnection(trantor::EventLoop *loop, const std::string &connInfo);\n\n    void init() override;\n\n    ~MysqlConnection()\n    {\n    }\n\n    void execSql(std::string_view &&sql,\n                 size_t paraNum,\n                 std::vector<const char *> &&parameters,\n                 std::vector<int> &&length,\n                 std::vector<int> &&format,\n                 ResultCallback &&rcb,\n                 std::function<void(const std::exception_ptr &)>\n                     &&exceptCallback) override\n    {\n        if (loop_->isInLoopThread())\n        {\n            execSqlInLoop(std::move(sql),\n                          paraNum,\n                          std::move(parameters),\n                          std::move(length),\n                          std::move(format),\n                          std::move(rcb),\n                          std::move(exceptCallback));\n        }\n        else\n        {\n            auto thisPtr = shared_from_this();\n            loop_->queueInLoop(\n                [thisPtr,\n                 sql = std::move(sql),\n                 paraNum,\n                 parameters = std::move(parameters),\n                 length = std::move(length),\n                 format = std::move(format),\n                 rcb = std::move(rcb),\n                 exceptCallback = std::move(exceptCallback)]() mutable {\n                    thisPtr->execSqlInLoop(std::move(sql),\n                                           paraNum,\n                                           std::move(parameters),\n                                           std::move(length),\n                                           std::move(format),\n                                           std::move(rcb),\n                                           std::move(exceptCallback));\n                });\n        }\n    }\n\n    void batchSql(std::deque<std::shared_ptr<SqlCmd>> &&) override\n    {\n        LOG_FATAL << \"The mysql library does not support batch mode\";\n        exit(1);\n    }\n\n    void disconnect() override;\n\n  private:\n    class MysqlEnv\n    {\n      public:\n        MysqlEnv()\n        {\n            mysql_library_init(0, nullptr, nullptr);\n        }\n\n        ~MysqlEnv()\n        {\n            mysql_library_end();\n        }\n    };\n\n    class MysqlThreadEnv\n    {\n      public:\n        MysqlThreadEnv()\n        {\n            mysql_thread_init();\n        }\n\n        ~MysqlThreadEnv()\n        {\n            mysql_thread_end();\n        }\n    };\n\n    void execSqlInLoop(\n        std::string_view &&sql,\n        size_t paraNum,\n        std::vector<const char *> &&parameters,\n        std::vector<int> &&length,\n        std::vector<int> &&format,\n        ResultCallback &&rcb,\n        std::function<void(const std::exception_ptr &)> &&exceptCallback);\n    void startSetCharacterSet();\n    void continueSetCharacterSet(int status);\n    std::unique_ptr<trantor::Channel> channelPtr_;\n    std::shared_ptr<MYSQL> mysqlPtr_;\n    std::string characterSet_;\n    void handleTimeout();\n    void handleCmd(int status);\n    void handleClosed();\n    void handleEvent();\n    void setChannel();\n    void getResult(MYSQL_RES *res);\n    void startQuery();\n    void startStoreResult(bool queueInLoop);\n    int waitStatus_;\n    unsigned int reconnect_{1};\n    enum class ExecStatus\n    {\n        None = 0,\n        RealQuery,\n        StoreResult,\n        NextResult\n    };\n    ExecStatus execStatus_{ExecStatus::None};\n\n    void outputError();\n    std::string sql_;\n    std::string host_, user_, passwd_, dbname_, port_;\n};\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/src/mysql_impl/MysqlResultImpl.cc",
    "content": "/**\n *\n *  MysqlResultImpl.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"MysqlResultImpl.h\"\n#include <algorithm>\n#include <cassert>\n#include <drogon/orm/Exception.h>\n\nusing namespace drogon::orm;\n\nResult::SizeType MysqlResultImpl::size() const noexcept\n{\n    return rowsNumber_;\n}\n\nResult::RowSizeType MysqlResultImpl::columns() const noexcept\n{\n    return fieldsNumber_;\n}\n\nconst char *MysqlResultImpl::columnName(RowSizeType number) const\n{\n    assert(number < fieldsNumber_);\n    if (fieldArray_)\n        return fieldArray_[number].name;\n    return \"\";\n}\n\nResult::SizeType MysqlResultImpl::affectedRows() const noexcept\n{\n    return affectedRows_;\n}\n\nResult::RowSizeType MysqlResultImpl::columnNumber(const char colName[]) const\n{\n    if (!fieldsMapPtr_)\n        return -1;\n    std::string col(colName);\n    std::transform(col.begin(), col.end(), col.begin(), [](unsigned char c) {\n        return tolower(c);\n    });\n    if (fieldsMapPtr_->find(col) != fieldsMapPtr_->end())\n        return (*fieldsMapPtr_)[col];\n    throw RangeError(std::string(\"no column named \") + colName);\n}\n\nconst char *MysqlResultImpl::getValue(SizeType row, RowSizeType column) const\n{\n    if (rowsNumber_ == 0 || fieldsNumber_ == 0)\n        return NULL;\n    assert(row < rowsNumber_);\n    assert(column < fieldsNumber_);\n    return (*rowsPtr_)[row].first[column];\n}\n\nbool MysqlResultImpl::isNull(SizeType row, RowSizeType column) const\n{\n    return getValue(row, column) == NULL;\n}\n\nResult::FieldSizeType MysqlResultImpl::getLength(SizeType row,\n                                                 RowSizeType column) const\n{\n    if (rowsNumber_ == 0 || fieldsNumber_ == 0)\n        return 0;\n    assert(row < rowsNumber_);\n    assert(column < fieldsNumber_);\n    return (*rowsPtr_)[row].second[column];\n}\n\nunsigned long long MysqlResultImpl::insertId() const noexcept\n{\n    return insertId_;\n}\n"
  },
  {
    "path": "orm_lib/src/mysql_impl/MysqlResultImpl.h",
    "content": "/**\n *\n *  MysqlResultImpl.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n#pragma once\n\n#include \"../ResultImpl.h\"\n#include <trantor/utils/Logger.h>\n#include <algorithm>\n#include <memory>\n#include <mysql.h>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass MysqlResultImpl : public ResultImpl\n{\n  public:\n    MysqlResultImpl(std::shared_ptr<MYSQL_RES> r,\n                    SizeType affectedRows,\n                    unsigned long long insertId) noexcept\n        : result_(std::move(r)),\n          rowsNumber_(result_ ? mysql_num_rows(result_.get()) : 0),\n          fieldArray_(result_ ? mysql_fetch_fields(result_.get()) : nullptr),\n          fieldsNumber_(result_ ? mysql_num_fields(result_.get()) : 0),\n          affectedRows_(affectedRows),\n          insertId_(insertId)\n    {\n        if (fieldsNumber_ > 0)\n        {\n            fieldsMapPtr_ = std::make_shared<\n                std::unordered_map<std::string, RowSizeType>>();\n            for (RowSizeType i = 0; i < fieldsNumber_; ++i)\n            {\n                std::string fieldName = fieldArray_[i].name;\n                std::transform(fieldName.begin(),\n                               fieldName.end(),\n                               fieldName.begin(),\n                               [](unsigned char c) { return tolower(c); });\n                (*fieldsMapPtr_)[fieldName] = i;\n            }\n        }\n        if (size() > 0)\n        {\n            rowsPtr_ = std::make_shared<\n                std::vector<std::pair<char **, std::vector<unsigned long>>>>();\n            MYSQL_ROW row;\n            std::vector<unsigned long> vLens;\n            vLens.resize(fieldsNumber_);\n            while ((row = mysql_fetch_row(result_.get())) != NULL)\n            {\n                auto lengths = mysql_fetch_lengths(result_.get());\n                memcpy(vLens.data(),\n                       lengths,\n                       sizeof(unsigned long) * fieldsNumber_);\n                rowsPtr_->push_back(std::make_pair(row, vLens));\n            }\n        }\n    }\n\n    SizeType size() const noexcept override;\n    RowSizeType columns() const noexcept override;\n    const char *columnName(RowSizeType number) const override;\n    SizeType affectedRows() const noexcept override;\n    RowSizeType columnNumber(const char colName[]) const override;\n    const char *getValue(SizeType row, RowSizeType column) const override;\n    bool isNull(SizeType row, RowSizeType column) const override;\n    FieldSizeType getLength(SizeType row, RowSizeType column) const override;\n    unsigned long long insertId() const noexcept override;\n\n  private:\n    const std::shared_ptr<MYSQL_RES> result_;\n    const Result::SizeType rowsNumber_;\n    const MYSQL_FIELD *fieldArray_;\n    const Result::RowSizeType fieldsNumber_;\n    const SizeType affectedRows_;\n    const unsigned long long insertId_;\n    std::shared_ptr<std::unordered_map<std::string, RowSizeType>> fieldsMapPtr_;\n    std::shared_ptr<std::vector<std::pair<char **, std::vector<unsigned long>>>>\n        rowsPtr_;\n};\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/src/mysql_impl/test/CMakeLists.txt",
    "content": "link_libraries(${PROJECT_NAME})\n\nadd_executable(mysql_test1 test1.cc)\n\nset_property(TARGET mysql_test1 PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})\nset_property(TARGET mysql_test1 PROPERTY CXX_STANDARD_REQUIRED ON)\nset_property(TARGET mysql_test1 PROPERTY CXX_EXTENSIONS OFF)\n"
  },
  {
    "path": "orm_lib/src/mysql_impl/test/test1.cc",
    "content": "#include <drogon/orm/DbClient.h>\n#include <drogon/utils/Utilities.h>\n#include <trantor/utils/Logger.h>\n#include <fstream>\n#include <iostream>\n#include <thread>\n#include <chrono>\n\nusing namespace std::chrono_literals;\nusing namespace drogon::orm;\nusing namespace drogon;\n\nint main()\n{\n    trantor::Logger::setLogLevel(trantor::Logger::kTrace);\n    auto clientPtr = DbClient::newMysqlClient(\n        \"host= 127.0.0.1    port  =3306 dbname= test user = root  \", 1);\n    std::this_thread::sleep_for(1s);\n    // for (int i = 0; i < 10; ++i)\n    // {\n    //     std::string str = formattedString(\"insert into users\n    //     (user_id,user_name,org_name) values('%d','antao','default')\",\n    //     i);\n    //     *clientPtr << str >> [](const Result &r) {\n    //         std::cout << \"insert rows:\" << r.affectedRows() << std::endl;\n    //     } >> [](const DrogonDbException &e) {\n    //         std::cerr << e.base().what() << std::endl;\n    //     };\n    // }\n    LOG_TRACE << \"begin\";\n    *clientPtr << \"select * from users where id!=139 order by id\"\n               << Mode::Blocking >>\n        [](const Result &r) {\n            std::cout << \"rows:\" << r.size() << std::endl;\n            std::cout << \"column num:\" << r.columns() << std::endl;\n\n            for (auto const &row : r)\n            {\n                std::cout << \"id=\" << row[\"id\"].as<int>() << std::endl;\n                std::cout << \"id=\" << row[\"id\"].as<std::string>() << std::endl;\n            }\n            // for (auto row : r)\n            // {\n            //     for (auto f : row)\n            //     {\n            //         std::cout << f.name() << \":\" << (f.isNull() ? \"NULL\" :\n            //         f.as<std::string>()) << std::endl;\n            //     }\n            // }\n        } >>\n        [](const DrogonDbException &e) {\n            std::cerr << e.base().what() << std::endl;\n        };\n    LOG_TRACE << \"end\";\n    LOG_TRACE << \"begin\";\n    *clientPtr << \"select * from users where id=? and user_id=? order by id\"\n               << 139 << \"233\" << Mode::Blocking >>\n        [](const Result &r) {\n            std::cout << \"rows:\" << r.size() << std::endl;\n            std::cout << \"column num:\" << r.columns() << std::endl;\n            for (auto const &row : r)\n            {\n                std::cout << \"user_id=\" << row[\"user_id\"].as<std::string>()\n                          << \" id=\" << row[\"id\"].as<std::string>();\n                std::cout << \" time=\" << row[\"time\"].as<std::string>()\n                          << std::endl;\n            }\n        } >>\n        [](const DrogonDbException &e) {\n            std::cerr << e.base().what() << std::endl;\n        };\n    *clientPtr << \"update users set time=? where id>?\" << trantor::Date::date()\n               << 1000 << Mode::Blocking >>\n        [](const Result &r) {\n            std::cout << \"update \" << r.affectedRows() << \" rows\" << std::endl;\n        } >>\n        [](const DrogonDbException &e) {\n            std::cerr << e.base().what() << std::endl;\n        };\n\n    // std::ifstream infile(\"Makefile\", std::ifstream::binary);\n    // std::streambuf *pbuf = infile.rdbuf();\n    // std::streamsize filesize = pbuf->pubseekoff(0, infile.end);\n    // pbuf->pubseekoff(0, infile.beg); // rewind\n    // std::string str;\n    // str.resize(filesize);\n    // pbuf->sgetn(&str[0], filesize);\n\n    {\n        auto trans = clientPtr->newTransaction([](bool ret) {\n            if (ret)\n            {\n                std::cout << \"committed!!!!!!\" << std::endl;\n            }\n        });\n        *trans << \"update users set file=? where id != ?\"\n               << \"hehaha\" << 1000 >>\n            [](const Result &r) {\n                std::cout << \"hahaha update \" << r.affectedRows() << \" rows\"\n                          << std::endl;\n                // trans->rollback();\n            } >>\n            [](const DrogonDbException &e) {\n                std::cerr << e.base().what() << std::endl;\n            };\n    }\n    LOG_DEBUG << \"out of transaction block\";\n    *clientPtr << \"select * from users where id=1000\" >> [](const Result &r) {\n        std::cout << \"file:\" << r[0][\"file\"].as<std::string>() << std::endl;\n    } >> [](const DrogonDbException &e) {\n        std::cerr << e.base().what() << std::endl;\n    };\n\n    *clientPtr << \"select * from users limit ? offset ?\" << 2 << 2 >>\n        [](const Result &r) {\n            std::cout << \"select \" << r.size() << \" records\" << std::endl;\n            for (auto row : r)\n            {\n                std::cout << \"is admin:\" << row[\"admin\"].as<bool>() << \"(\"\n                          << row[\"admin\"].as<std::string>() << \")\" << std::endl;\n            }\n        } >>\n        [](const DrogonDbException &e) {\n            std::cerr << e.base().what() << std::endl;\n        };\n    LOG_TRACE << \"end\";\n    getchar();\n}\n"
  },
  {
    "path": "orm_lib/src/postgresql_impl/PgBatchConnection.cc",
    "content": "/**\n *\n *  PgConnection.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"PgConnection.h\"\n#include \"PostgreSQLResultImpl.h\"\n#include <drogon/orm/Exception.h>\n#include <drogon/utils/Utilities.h>\n#include <trantor/utils/Logger.h>\n#include <exception>\n#include <memory>\n#include <algorithm>\n#include <stdio.h>\n\nusing namespace drogon::orm;\n\nnamespace drogon\n{\nnamespace orm\n{\nstatic const unsigned int maxBatchCount = 256;\n\nResult makeResult(std::shared_ptr<PGresult> &&r = nullptr)\n{\n    return Result(std::make_shared<PostgreSQLResultImpl>(std::move(r)));\n}\n\nbool checkSql(const std::string_view &sql_)\n{\n    if (sql_.length() > 1024)\n        return true;\n    std::string sql{sql_.data(), sql_.length()};\n    std::transform(sql.begin(), sql.end(), sql.begin(), [](unsigned char c) {\n        return tolower(c);\n    });\n    return (sql.find(\"update\") != std::string::npos ||\n            sql.find(\"into\") != std::string::npos ||\n            sql.find(\"delete\") != std::string::npos ||\n            sql.find(\"drop\") != std::string::npos ||\n            sql.find(\"truncate\") != std::string::npos ||\n            sql.find(\"lock\") != std::string::npos ||\n            sql.find(\"create\") != std::string::npos ||\n            sql.find(\"call\") != std::string::npos ||\n            sql.find(\"alter\") != std::string::npos);\n}\n\n}  // namespace orm\n}  // namespace drogon\n\nint PgConnection::flush()\n{\n    auto ret = PQflush(connectionPtr_.get());\n    if (ret == 1)\n    {\n        if (!channel_.isWriting())\n        {\n            channel_.enableWriting();\n        }\n    }\n    else if (ret == 0)\n    {\n        if (channel_.isWriting())\n        {\n            channel_.disableWriting();\n        }\n    }\n    return ret;\n}\n\nPgConnection::PgConnection(trantor::EventLoop *loop,\n                           const std::string &connInfo,\n                           bool autoBatch)\n    : DbConnection(loop),\n      autoBatch_(autoBatch),\n      connectionPtr_(\n          std::shared_ptr<PGconn>(PQconnectStart(connInfo.c_str()),\n                                  [](PGconn *conn) { PQfinish(conn); })),\n      channel_(loop, PQsocket(connectionPtr_.get()))\n{\n    if (channel_.fd() < 0)\n    {\n        LOG_ERROR << \"Failed to create Postgres connection\";\n    }\n}\n\nvoid PgConnection::init()\n{\n    if (channel_.fd() < 0)\n    {\n        LOG_ERROR << \"Connection with Postgres could not be established\";\n\n        if (closeCallback_)\n        {\n            auto thisPtr = shared_from_this();\n            closeCallback_(thisPtr);\n        }\n        return;\n    }\n\n    PQsetnonblocking(connectionPtr_.get(), 1);\n    channel_.setReadCallback([this]() {\n        if (status_ == ConnectStatus::Bad)\n        {\n            return;\n        }\n        if (status_ != ConnectStatus::Ok)\n        {\n            pgPoll();\n        }\n        else\n        {\n            handleRead();\n        }\n    });\n    channel_.setWriteCallback([this]() {\n        if (status_ == ConnectStatus::Ok)\n        {\n            auto ret = PQflush(connectionPtr_.get());\n            if (ret == 0)\n            {\n                sendBatchedSql();\n                return;\n            }\n            else if (ret < 0)\n            {\n                channel_.disableWriting();\n                LOG_ERROR << \"PQflush error:\"\n                          << PQerrorMessage(connectionPtr_.get());\n                return;\n            }\n        }\n        else\n        {\n            pgPoll();\n        }\n    });\n    channel_.setCloseCallback([this]() { handleClosed(); });\n    channel_.setErrorCallback([this]() { handleClosed(); });\n    channel_.enableReading();\n    channel_.enableWriting();\n}\n\nvoid PgConnection::handleClosed()\n{\n    loop_->assertInLoopThread();\n    if (status_ == ConnectStatus::Bad)\n        return;\n    status_ = ConnectStatus::Bad;\n    channel_.disableAll();\n    channel_.remove();\n    assert(closeCallback_);\n    auto thisPtr = shared_from_this();\n    closeCallback_(thisPtr);\n}\n\nvoid PgConnection::disconnect()\n{\n    std::promise<int> pro;\n    auto f = pro.get_future();\n    auto thisPtr = shared_from_this();\n    loop_->runInLoop([thisPtr, &pro]() {\n        thisPtr->status_ = ConnectStatus::Bad;\n        if (thisPtr->channel_.fd() >= 0)\n        {\n            thisPtr->channel_.disableAll();\n            thisPtr->channel_.remove();\n        }\n        thisPtr->connectionPtr_.reset();\n        pro.set_value(1);\n    });\n    f.get();\n}\n\nvoid PgConnection::pgPoll()\n{\n    loop_->assertInLoopThread();\n    auto connStatus = PQconnectPoll(connectionPtr_.get());\n    switch (connStatus)\n    {\n        case PGRES_POLLING_FAILED:\n            LOG_ERROR << \"!!!Pg connection failed: \"\n                      << PQerrorMessage(connectionPtr_.get());\n            if (status_ == ConnectStatus::None)\n            {\n                handleClosed();\n            }\n            break;\n        case PGRES_POLLING_WRITING:\n            if (!channel_.isWriting())\n                channel_.enableWriting();\n            break;\n        case PGRES_POLLING_READING:\n            if (!channel_.isReading())\n                channel_.enableReading();\n            if (channel_.isWriting())\n                channel_.disableWriting();\n            break;\n\n        case PGRES_POLLING_OK:\n            if (status_ != ConnectStatus::Ok)\n            {\n                status_ = ConnectStatus::Ok;\n                if (!PQenterPipelineMode(connectionPtr_.get()))\n                {\n                    handleClosed();\n                    return;\n                }\n                assert(okCallback_);\n                okCallback_(shared_from_this());\n            }\n            if (!channel_.isReading())\n                channel_.enableReading();\n            if (channel_.isWriting())\n                channel_.disableWriting();\n            break;\n        case PGRES_POLLING_ACTIVE:\n            // unused!\n            break;\n        default:\n            break;\n    }\n}\n\nvoid PgConnection::execSqlInLoop(\n    std::string_view &&sql,\n    size_t paraNum,\n    std::vector<const char *> &&parameters,\n    std::vector<int> &&length,\n    std::vector<int> &&format,\n    ResultCallback &&rcb,\n    std::function<void(const std::exception_ptr &)> &&exceptCallback)\n{\n    LOG_TRACE << sql;\n    if (status_ != ConnectStatus::Ok)\n    {\n        LOG_ERROR << \"Connection is not ready\";\n        auto exceptPtr =\n            std::make_exception_ptr(drogon::orm::BrokenConnection());\n        exceptCallback(exceptPtr);\n        return;\n    }\n    isWorking_ = true;\n    batchSqlCommands_.emplace_back(\n        std::make_shared<SqlCmd>(std::move(sql),\n                                 paraNum,\n                                 std::move(parameters),\n                                 std::move(length),\n                                 std::move(format),\n                                 std::move(rcb),\n                                 std::move(exceptCallback)));\n    if (batchSqlCommands_.size() == 1 && !channel_.isWriting())\n    {\n        loop_->queueInLoop(\n            [thisPtr = shared_from_this()]() { thisPtr->sendBatchedSql(); });\n    }\n}\n\nint PgConnection::sendBatchEnd()\n{\n    if (!PQpipelineSync(connectionPtr_.get()))\n    {\n        isWorking_ = false;\n        handleFatalError(true);\n        handleClosed();\n        return 0;\n    }\n    return 1;\n}\n\nvoid PgConnection::sendBatchedSql()\n{\n    if (isWorking_)\n    {\n        if (sendBatchEnd_)\n        {\n            sendBatchEnd_ = false;\n            if (!sendBatchEnd())\n            {\n                return;\n            }\n        }\n        if (batchSqlCommands_.empty())\n        {\n            if (channel_.isWriting())\n                channel_.disableWriting();\n            return;\n        }\n    }\n    isWorking_ = true;\n    while (!batchSqlCommands_.empty())\n    {\n        auto &cmd = batchSqlCommands_.front();\n        std::string statName;\n        if (cmd->preparingStatement_.empty())\n        {\n            auto iter = preparedStatementsMap_.find(cmd->sql_);\n            if (iter == preparedStatementsMap_.end())\n            {\n                statName = newStmtName();\n                if (PQsendPrepare(connectionPtr_.get(),\n                                  statName.c_str(),\n                                  cmd->sql_.data(),\n                                  cmd->parametersNumber_,\n                                  NULL) == 0)\n                {\n                    LOG_ERROR << \"send query error: \"\n                              << PQerrorMessage(connectionPtr_.get());\n\n                    isWorking_ = false;\n                    handleFatalError(true);\n                    handleClosed();\n                    return;\n                }\n                cmd->preparingStatement_ = statName;\n                if (autoBatch_)\n                {\n                    cmd->isChanging_ = checkSql(cmd->sql_);\n                }\n                if (flush())\n                {\n                    return;\n                }\n            }\n            else\n            {\n                statName = iter->second.first;\n                if (autoBatch_)\n                {\n                    cmd->isChanging_ = iter->second.second;\n                }\n            }\n        }\n        else\n        {\n            statName = cmd->preparingStatement_;\n        }\n        if (autoBatch_)\n        {\n            if (batchSqlCommands_.size() == 1 || cmd->sql_.length() > 1024 ||\n                batchCount_ > maxBatchCount)\n            {\n                sendBatchEnd_ = true;\n                batchCount_ = 0;\n            }\n            else if (cmd->isChanging_)\n            {\n                sendBatchEnd_ = true;\n                batchCount_ = 0;\n            }\n            ++batchCount_;\n        }\n        if (PQsendQueryPrepared(connectionPtr_.get(),\n                                statName.c_str(),\n                                cmd->parametersNumber_,\n                                cmd->parameters_.data(),\n                                cmd->lengths_.data(),\n                                cmd->formats_.data(),\n                                0) == 0)\n        {\n            isWorking_ = false;\n            handleFatalError(true);\n            handleClosed();\n            return;\n        }\n\n        batchCommandsForWaitingResults_.push_back(std::move(cmd));\n        batchSqlCommands_.pop_front();\n        if (!autoBatch_)\n        {\n            if (!sendBatchEnd())\n            {\n                return;\n            }\n        }\n        else\n        {\n            if (flush())\n            {\n                return;\n            }\n        }\n        if (autoBatch_ && sendBatchEnd_)\n        {\n            sendBatchEnd_ = false;\n            if (!sendBatchEnd())\n            {\n                return;\n            }\n            if (flush())\n            {\n                return;\n            }\n        }\n        else\n        {\n            if (flush())\n            {\n                return;\n            }\n        }\n    }\n}\n\nvoid PgConnection::handleRead()\n{\n    loop_->assertInLoopThread();\n    std::shared_ptr<PGresult> res;\n\n    if (!PQconsumeInput(connectionPtr_.get()))\n    {\n        LOG_ERROR << \"Failed to consume pg input:\"\n                  << PQerrorMessage(connectionPtr_.get());\n        if (isWorking_)\n        {\n            isWorking_ = false;\n            handleFatalError(true);\n        }\n        handleClosed();\n        return;\n    }\n    if (PQisBusy(connectionPtr_.get()))\n    {\n        // need read more data from socket;\n        return;\n    }\n    // assert((!batchCommandsForWaitingResults_.empty() ||\n    //         !batchSqlCommands_.empty()));\n\n    while (!PQisBusy(connectionPtr_.get()))\n    {\n        // TODO: should optimize order of checking\n        // Check notification\n        std::shared_ptr<PGnotify> notify;\n        while (\n            (notify =\n                 std::shared_ptr<PGnotify>(PQnotifies(connectionPtr_.get()),\n                                           [](PGnotify *p) { PQfreemem(p); })))\n        {\n            messageCallback_({notify->relname}, {notify->extra});\n        }\n\n        // Check query result\n        res = std::shared_ptr<PGresult>(PQgetResult(connectionPtr_.get()),\n                                        [](PGresult *p) { PQclear(p); });\n        if (!res)\n        {\n            /*\n             * No more results currtently available.\n             */\n            if (!PQsendFlushRequest(connectionPtr_.get()))\n            {\n                LOG_ERROR << \"Failed to PQsendFlushRequest:\"\n                          << PQerrorMessage(connectionPtr_.get());\n                return;\n            }\n            res = std::shared_ptr<PGresult>(PQgetResult(connectionPtr_.get()),\n                                            [](PGresult *p) { PQclear(p); });\n            if (!res)\n            {\n                return;\n            }\n        }\n        auto type = PQresultStatus(res.get());\n        if (type == PGRES_BAD_RESPONSE || type == PGRES_FATAL_ERROR ||\n            type == PGRES_PIPELINE_ABORTED)\n        {\n            handleFatalError(false, type == PGRES_PIPELINE_ABORTED);\n            continue;\n        }\n        if (type == PGRES_PIPELINE_SYNC)\n        {\n            if (batchCommandsForWaitingResults_.empty() &&\n                batchSqlCommands_.empty())\n            {\n                isWorking_ = false;\n                idleCb_();\n                return;\n            }\n            continue;\n        }\n        if (!batchCommandsForWaitingResults_.empty())\n        {\n            auto &cmd = batchCommandsForWaitingResults_.front();\n            if (!cmd->preparingStatement_.empty())\n            {\n                auto r = preparedStatements_.insert(\n                    std::string{cmd->sql_.data(), cmd->sql_.length()});\n                preparedStatementsMap_[std::string_view{r.first->c_str(),\n                                                        r.first->length()}] = {\n                    std::move(cmd->preparingStatement_), cmd->isChanging_};\n                cmd->preparingStatement_.clear();\n                continue;\n            }\n            auto r = makeResult(std::move(res));\n            cmd->callback_(r);\n            batchCommandsForWaitingResults_.pop_front();\n            continue;\n        }\n        assert(!batchSqlCommands_.empty());\n        assert(!batchSqlCommands_.front()->preparingStatement_.empty());\n        auto &cmd = batchSqlCommands_.front();\n        if (!cmd->preparingStatement_.empty())\n        {\n            auto r = preparedStatements_.insert(\n                std::string{cmd->sql_.data(), cmd->sql_.length()});\n            preparedStatementsMap_[std::string_view{r.first->c_str(),\n                                                    r.first->length()}] = {\n                std::move(cmd->preparingStatement_), cmd->isChanging_};\n            cmd->preparingStatement_.clear();\n            continue;\n        }\n    }\n}\n\nvoid PgConnection::doAfterPreparing()\n{\n}\n\nvoid PgConnection::handleFatalError(bool clearAll, bool isAbortPipeline)\n{\n    std::string errmsg =\n        isAbortPipeline\n            ? \"Command didn't run because of an abort earlier in a pipeline\"\n            : PQerrorMessage(connectionPtr_.get());\n    LOG_ERROR << errmsg;\n    auto exceptPtr = std::make_exception_ptr(Failure(errmsg));\n    if (clearAll)\n    {\n        for (auto &cmd : batchCommandsForWaitingResults_)\n        {\n            if (cmd->exceptionCallback_)\n            {\n                cmd->exceptionCallback_(exceptPtr);\n            }\n        }\n        for (auto &cmd : batchSqlCommands_)\n        {\n            if (cmd->exceptionCallback_)\n            {\n                cmd->exceptionCallback_(exceptPtr);\n            }\n        }\n        batchCommandsForWaitingResults_.clear();\n        batchSqlCommands_.clear();\n    }\n    else\n    {\n        if (!batchSqlCommands_.empty() &&\n            !batchSqlCommands_.front()->preparingStatement_.empty())\n        {\n            if (batchSqlCommands_.front()->exceptionCallback_)\n            {\n                batchSqlCommands_.front()->exceptionCallback_(exceptPtr);\n            }\n            batchSqlCommands_.pop_front();\n        }\n        else if (!batchCommandsForWaitingResults_.empty())\n        {\n            auto &cmd = batchCommandsForWaitingResults_.front();\n            if (cmd->exceptionCallback_)\n            {\n                cmd->exceptionCallback_(exceptPtr);\n            }\n            batchCommandsForWaitingResults_.pop_front();\n        }\n        else\n        {\n            // PQsendPrepare failed, error message has already been reported\n            // Ignore PQsendQueryPrepared failure\n        }\n    }\n}\n\nvoid PgConnection::batchSql(std::deque<std::shared_ptr<SqlCmd>> &&sqlCommands)\n{\n    loop_->assertInLoopThread();\n    batchSqlCommands_ = std::move(sqlCommands);\n    sendBatchedSql();\n}\n"
  },
  {
    "path": "orm_lib/src/postgresql_impl/PgConnection.cc",
    "content": "/**\n *\n *  @file PgConnection.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"PgConnection.h\"\n#include \"PostgreSQLResultImpl.h\"\n#include <drogon/orm/Exception.h>\n#include <drogon/utils/Utilities.h>\n#include <string_view>\n#include <trantor/utils/Logger.h>\n#include <memory>\n#include <stdio.h>\n\nusing namespace drogon;\nusing namespace drogon::orm;\n\nnamespace drogon\n{\nnamespace orm\n{\nResult makeResult(std::shared_ptr<PGresult> &&r = nullptr)\n{\n    return Result(std::make_shared<PostgreSQLResultImpl>(std::move(r)));\n}\n\n}  // namespace orm\n}  // namespace drogon\n\nint PgConnection::flush()\n{\n    auto ret = PQflush(connectionPtr_.get());\n    if (ret == 1)\n    {\n        if (!channel_.isWriting())\n        {\n            channel_.enableWriting();\n        }\n    }\n    else if (ret == 0)\n    {\n        if (channel_.isWriting())\n        {\n            channel_.disableWriting();\n        }\n    }\n    return ret;\n}\n\nPgConnection::PgConnection(trantor::EventLoop *loop,\n                           const std::string &connInfo,\n                           bool)\n    : DbConnection(loop),\n      connectionPtr_(\n          std::shared_ptr<PGconn>(PQconnectStart(connInfo.c_str()),\n                                  [](PGconn *conn) { PQfinish(conn); })),\n      channel_(loop, PQsocket(connectionPtr_.get()))\n{\n    if (channel_.fd() < 0)\n    {\n        LOG_ERROR << \"Failed to create Postgres connection\";\n    }\n}\n\nvoid PgConnection::init()\n{\n    if (channel_.fd() < 0)\n    {\n        LOG_ERROR << \"Connection with Postgres could not be established\";\n        if (closeCallback_)\n        {\n            auto thisPtr = shared_from_this();\n            closeCallback_(thisPtr);\n        }\n        return;\n    }\n\n    PQsetnonblocking(connectionPtr_.get(), 1);\n    channel_.setReadCallback([this]() {\n        if (status_ == ConnectStatus::Bad)\n        {\n            return;\n        }\n        if (status_ != ConnectStatus::Ok)\n        {\n            pgPoll();\n        }\n        else\n        {\n            handleRead();\n        }\n    });\n    channel_.setWriteCallback([this]() {\n        if (status_ == ConnectStatus::Ok)\n        {\n            auto ret = PQflush(connectionPtr_.get());\n            if (ret == 0)\n            {\n                channel_.disableWriting();\n                return;\n            }\n            else if (ret < 0)\n            {\n                channel_.disableWriting();\n                LOG_ERROR << \"PQflush error:\"\n                          << PQerrorMessage(connectionPtr_.get());\n                return;\n            }\n        }\n        else\n        {\n            pgPoll();\n        }\n    });\n    channel_.setCloseCallback([this]() { handleClosed(); });\n    channel_.setErrorCallback([this]() { handleClosed(); });\n    channel_.enableReading();\n    channel_.enableWriting();\n}\n\nvoid PgConnection::handleClosed()\n{\n    loop_->assertInLoopThread();\n    if (status_ == ConnectStatus::Bad)\n        return;\n    status_ = ConnectStatus::Bad;\n\n    if (isWorking_)\n    {\n        // Connection was closed unexpectedly while isWorking_ was true.\n        isWorking_ = false;\n        handleFatalError();\n        callback_ = nullptr;\n    }\n\n    channel_.disableAll();\n    channel_.remove();\n    assert(closeCallback_);\n    auto thisPtr = shared_from_this();\n    closeCallback_(thisPtr);\n}\n\nvoid PgConnection::disconnect()\n{\n    std::promise<int> pro;\n    auto f = pro.get_future();\n    auto thisPtr = shared_from_this();\n    loop_->runInLoop([thisPtr, &pro]() {\n        thisPtr->status_ = ConnectStatus::Bad;\n        if (thisPtr->channel_.fd() >= 0)\n        {\n            thisPtr->channel_.disableAll();\n            thisPtr->channel_.remove();\n        }\n        thisPtr->connectionPtr_.reset();\n        pro.set_value(1);\n    });\n    f.get();\n}\n\nvoid PgConnection::pgPoll()\n{\n    loop_->assertInLoopThread();\n    auto connStatus = PQconnectPoll(connectionPtr_.get());\n\n    switch (connStatus)\n    {\n        case PGRES_POLLING_FAILED:\n            LOG_ERROR << \"!!!Pg connection failed: \"\n                      << PQerrorMessage(connectionPtr_.get());\n            if (status_ == ConnectStatus::None)\n            {\n                handleClosed();\n            }\n            break;\n        case PGRES_POLLING_WRITING:\n            if (!channel_.isWriting())\n                channel_.enableWriting();\n            break;\n        case PGRES_POLLING_READING:\n            if (!channel_.isReading())\n                channel_.enableReading();\n            if (channel_.isWriting())\n                channel_.disableWriting();\n            break;\n\n        case PGRES_POLLING_OK:\n            if (status_ != ConnectStatus::Ok)\n            {\n                status_ = ConnectStatus::Ok;\n                assert(okCallback_);\n                okCallback_(shared_from_this());\n            }\n            if (!channel_.isReading())\n                channel_.enableReading();\n            if (channel_.isWriting())\n                channel_.disableWriting();\n            break;\n        case PGRES_POLLING_ACTIVE:\n            // unused!\n            break;\n        default:\n            break;\n    }\n}\n\nvoid PgConnection::execSqlInLoop(\n    std::string_view &&sql,\n    size_t paraNum,\n    std::vector<const char *> &&parameters,\n    std::vector<int> &&length,\n    std::vector<int> &&format,\n    ResultCallback &&rcb,\n    std::function<void(const std::exception_ptr &)> &&exceptCallback)\n{\n    LOG_TRACE << sql;\n    loop_->assertInLoopThread();\n    assert(paraNum == parameters.size());\n    assert(paraNum == length.size());\n    assert(paraNum == format.size());\n    assert(rcb);\n    assert(!isWorking_);\n    assert(!sql.empty());\n    if (status_ != ConnectStatus::Ok)\n    {\n        LOG_ERROR << \"Connection is not ready\";\n        auto exceptPtr =\n            std::make_exception_ptr(drogon::orm::BrokenConnection());\n        exceptCallback(exceptPtr);\n        return;\n    }\n    sql_ = std::move(sql);\n    callback_ = std::move(rcb);\n    isWorking_ = true;\n    exceptionCallback_ = std::move(exceptCallback);\n    if (paraNum == 0)\n    {\n        isPreparingStatement_ = false;\n        if (PQsendQuery(connectionPtr_.get(), sql_.data()) == 0)\n        {\n            LOG_ERROR << \"send query error: \"\n                      << PQerrorMessage(connectionPtr_.get());\n            if (isWorking_)\n            {\n                isWorking_ = false;\n                isPreparingStatement_ = false;\n                handleFatalError();\n                callback_ = nullptr;\n                idleCb_();\n            }\n            return;\n        }\n        flush();\n    }\n    else\n    {\n        auto iter = preparedStatementsMap_.find(sql_);\n        if (iter != preparedStatementsMap_.end())\n        {\n            isPreparingStatement_ = false;\n            if (PQsendQueryPrepared(connectionPtr_.get(),\n                                    iter->second.c_str(),\n                                    static_cast<int>(paraNum),\n                                    parameters.data(),\n                                    length.data(),\n                                    format.data(),\n                                    0) == 0)\n            {\n                LOG_ERROR << \"send query error: \"\n                          << PQerrorMessage(connectionPtr_.get());\n                if (isWorking_)\n                {\n                    isWorking_ = false;\n                    isPreparingStatement_ = false;\n                    handleFatalError();\n                    callback_ = nullptr;\n                    idleCb_();\n                }\n                return;\n            }\n        }\n        else\n        {\n            isPreparingStatement_ = true;\n            statementName_ = newStmtName();\n            if (PQsendPrepare(connectionPtr_.get(),\n                              statementName_.c_str(),\n                              sql_.data(),\n                              static_cast<int>(paraNum),\n                              nullptr) == 0)\n            {\n                LOG_ERROR << \"send query error: \"\n                          << PQerrorMessage(connectionPtr_.get());\n                if (isWorking_)\n                {\n                    isWorking_ = false;\n                    handleFatalError();\n                    callback_ = nullptr;\n                    idleCb_();\n                }\n                return;\n            }\n            parametersNumber_ = static_cast<int>(paraNum);\n            parameters_ = std::move(parameters);\n            lengths_ = std::move(length);\n            formats_ = std::move(format);\n        }\n        flush();\n    }\n}\n\nvoid PgConnection::handleRead()\n{\n    loop_->assertInLoopThread();\n    std::shared_ptr<PGresult> res;\n\n    if (!PQconsumeInput(connectionPtr_.get()))\n    {\n        LOG_ERROR << \"Failed to consume pg input:\"\n                  << PQerrorMessage(connectionPtr_.get());\n        if (isWorking_)\n        {\n            isWorking_ = false;\n            handleFatalError();\n            callback_ = nullptr;\n        }\n        handleClosed();\n        return;\n    }\n    if (PQisBusy(connectionPtr_.get()))\n    {\n        // need read more data from socket;\n        return;\n    }\n    while ((res = std::shared_ptr<PGresult>(PQgetResult(connectionPtr_.get()),\n                                            [](PGresult *p) { PQclear(p); })))\n    {\n        auto type = PQresultStatus(res.get());\n        if (type == PGRES_BAD_RESPONSE || type == PGRES_FATAL_ERROR)\n        {\n            LOG_WARN << PQerrorMessage(connectionPtr_.get());\n            if (isWorking_)\n            {\n                handleFatalError();\n                callback_ = nullptr;\n            }\n        }\n        else\n        {\n            if (isWorking_)\n            {\n                if (!isPreparingStatement_)\n                {\n                    auto r = makeResult(std::move(res));\n                    callback_(r);\n                    callback_ = nullptr;\n                    exceptionCallback_ = nullptr;\n                }\n            }\n        }\n    }\n    if (isWorking_)\n    {\n        if (isPreparingStatement_ && callback_)\n        {\n            doAfterPreparing();\n        }\n        else\n        {\n            isWorking_ = false;\n            isPreparingStatement_ = false;\n            idleCb_();\n        }\n    }\n\n    // Check notification\n    std::shared_ptr<PGnotify> notify;\n    while (\n        (notify = std::shared_ptr<PGnotify>(PQnotifies(connectionPtr_.get()),\n                                            [](PGnotify *p) { PQfreemem(p); })))\n    {\n        messageCallback_({notify->relname}, {notify->extra});\n    }\n}\n\nvoid PgConnection::doAfterPreparing()\n{\n    isPreparingStatement_ = false;\n    auto r = preparedStatements_.insert(std::string{sql_});\n    preparedStatementsMap_[std::string_view{r.first->data(),\n                                            r.first->length()}] =\n        statementName_;\n    if (PQsendQueryPrepared(connectionPtr_.get(),\n                            statementName_.c_str(),\n                            parametersNumber_,\n                            parameters_.data(),\n                            lengths_.data(),\n                            formats_.data(),\n                            0) == 0)\n    {\n        LOG_ERROR << \"send query error: \"\n                  << PQerrorMessage(connectionPtr_.get());\n        if (isWorking_)\n        {\n            isWorking_ = false;\n            handleFatalError();\n            callback_ = nullptr;\n            idleCb_();\n        }\n        return;\n    }\n    flush();\n}\n\nvoid PgConnection::handleFatalError()\n{\n    if (exceptionCallback_)\n    {\n        auto exceptPtr = std::make_exception_ptr(\n            Failure(PQerrorMessage(connectionPtr_.get())));\n        exceptionCallback_(exceptPtr);\n    }\n\n    exceptionCallback_ = nullptr;\n}\n\nvoid PgConnection::batchSql(std::deque<std::shared_ptr<SqlCmd>> &&)\n{\n    assert(false);\n}\n"
  },
  {
    "path": "orm_lib/src/postgresql_impl/PgConnection.h",
    "content": "/**\n *\n *  PgConnection.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"../DbConnection.h\"\n#include <drogon/orm/DbClient.h>\n#include <trantor/net/EventLoop.h>\n#include <trantor/net/Channel.h>\n#include <trantor/utils/NonCopyable.h>\n#include <libpq-fe.h>\n#include <unordered_map>\n#include <memory>\n#include <string>\n#include <functional>\n#include <iostream>\n#include <list>\n#include <set>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass PgConnection;\nusing PgConnectionPtr = std::shared_ptr<PgConnection>;\n\nclass PgConnection : public DbConnection,\n                     public std::enable_shared_from_this<PgConnection>\n{\n  public:\n    using MessageCallback =\n        std::function<void(const std::string &, const std::string &)>;\n    PgConnection(trantor::EventLoop *loop,\n                 const std::string &connInfo,\n                 bool autoBatch);\n\n    void init() override;\n\n    void execSql(std::string_view &&sql,\n                 size_t paraNum,\n                 std::vector<const char *> &&parameters,\n                 std::vector<int> &&length,\n                 std::vector<int> &&format,\n                 ResultCallback &&rcb,\n                 std::function<void(const std::exception_ptr &)>\n                     &&exceptCallback) override\n    {\n        if (loop_->isInLoopThread())\n        {\n            execSqlInLoop(std::move(sql),\n                          paraNum,\n                          std::move(parameters),\n                          std::move(length),\n                          std::move(format),\n                          std::move(rcb),\n                          std::move(exceptCallback));\n        }\n        else\n        {\n            auto thisPtr = shared_from_this();\n            loop_->queueInLoop(\n                [thisPtr,\n                 sql = std::move(sql),\n                 paraNum,\n                 parameters = std::move(parameters),\n                 length = std::move(length),\n                 format = std::move(format),\n                 rcb = std::move(rcb),\n                 exceptCallback = std::move(exceptCallback)]() mutable {\n                    thisPtr->execSqlInLoop(std::move(sql),\n                                           paraNum,\n                                           std::move(parameters),\n                                           std::move(length),\n                                           std::move(format),\n                                           std::move(rcb),\n                                           std::move(exceptCallback));\n                });\n        }\n    }\n\n    void batchSql(std::deque<std::shared_ptr<SqlCmd>> &&sqlCommands) override;\n\n    void disconnect() override;\n\n    const std::shared_ptr<PGconn> &pgConn() const\n    {\n        return connectionPtr_;\n    }\n\n    void setMessageCallback(MessageCallback cb)\n    {\n        messageCallback_ = std::move(cb);\n    }\n\n  private:\n    std::shared_ptr<PGconn> connectionPtr_;\n    trantor::Channel channel_;\n    bool isPreparingStatement_{false};\n    size_t preparedStatementsID_{0};\n\n    std::string newStmtName()\n    {\n        loop_->assertInLoopThread();\n        return std::to_string(++preparedStatementsID_);\n    }\n\n    void handleRead();\n    void pgPoll();\n    void handleClosed();\n\n    void execSqlInLoop(\n        std::string_view &&sql,\n        size_t paraNum,\n        std::vector<const char *> &&parameters,\n        std::vector<int> &&length,\n        std::vector<int> &&format,\n        ResultCallback &&rcb,\n        std::function<void(const std::exception_ptr &)> &&exceptCallback);\n    void doAfterPreparing();\n    std::string statementName_;\n    int parametersNumber_{0};\n    std::vector<const char *> parameters_;\n    std::vector<int> lengths_;\n    std::vector<int> formats_;\n    int flush();\n    void handleFatalError();\n    std::set<std::string> preparedStatements_;\n    std::string_view sql_;\n#if LIBPQ_SUPPORTS_BATCH_MODE\n    void handleFatalError(bool clearAll, bool isAbortPipeline = false);\n    std::list<std::shared_ptr<SqlCmd>> batchCommandsForWaitingResults_;\n    std::deque<std::shared_ptr<SqlCmd>> batchSqlCommands_;\n    void sendBatchedSql();\n    int sendBatchEnd();\n    bool sendBatchEnd_{false};\n    bool autoBatch_{false};\n    unsigned int batchCount_{0};\n    std::unordered_map<std::string_view, std::pair<std::string, bool>>\n        preparedStatementsMap_;\n#else\n    std::unordered_map<std::string_view, std::string> preparedStatementsMap_;\n#endif\n\n    MessageCallback messageCallback_;\n};\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/src/postgresql_impl/PgListener.cc",
    "content": "/**\n *\n *  @file PgListener.cc\n *  @author Nitromelon\n *\n *  Copyright 2022, An Tao.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"PgListener.h\"\n#include \"PgConnection.h\"\n\nusing namespace drogon;\nusing namespace drogon::orm;\n\n#define MAX_UNLISTEN_RETRY 3\n#define MAX_LISTEN_RETRY 10\n\nPgListener::PgListener(std::string connInfo, trantor::EventLoop *loop)\n    : connectionInfo_(std::move(connInfo)), loop_(loop)\n{\n    if (!loop)\n    {\n        threadPtr_ = std::make_unique<trantor::EventLoopThread>();\n        threadPtr_->run();\n        loop_ = threadPtr_->getLoop();\n    }\n}\n\nPgListener::~PgListener()\n{\n    if (conn_)\n    {\n        conn_->disconnect();\n        conn_ = nullptr;\n    }\n}\n\nvoid PgListener::init() noexcept\n{\n    // shared_from_this() can not be called in constructor\n    std::weak_ptr<PgListener> weakThis = shared_from_this();\n    loop_->queueInLoop([weakThis]() {\n        auto thisPtr = weakThis.lock();\n        if (!thisPtr)\n        {\n            return;\n        }\n        thisPtr->connHolder_ = thisPtr->newConnection();\n    });\n}\n\nvoid PgListener::listen(\n    const std::string &channel,\n    std::function<void(std::string, std::string)> messageCallback) noexcept\n{\n    if (loop_->isInLoopThread())\n    {\n        listenChannels_[channel].push_back(std::move(messageCallback));\n        listenInLoop(channel, true);\n    }\n    else\n    {\n        std::weak_ptr<PgListener> weakThis = shared_from_this();\n        loop_->queueInLoop(\n            [weakThis, channel, cb = std::move(messageCallback)]() mutable {\n                auto thisPtr = weakThis.lock();\n                if (!thisPtr)\n                {\n                    return;\n                }\n                thisPtr->listenChannels_[channel].push_back(std::move(cb));\n                thisPtr->listenInLoop(channel, true);\n            });\n    }\n}\n\nvoid PgListener::unlisten(const std::string &channel) noexcept\n{\n    if (loop_->isInLoopThread())\n    {\n        listenChannels_.erase(channel);\n        listenInLoop(channel, false);\n    }\n    else\n    {\n        std::weak_ptr<PgListener> weakThis = shared_from_this();\n        loop_->queueInLoop([weakThis, channel]() {\n            auto thisPtr = weakThis.lock();\n            if (!thisPtr)\n            {\n                return;\n            }\n            thisPtr->listenChannels_.erase(channel);\n            thisPtr->listenInLoop(channel, false);\n        });\n    }\n}\n\nvoid PgListener::onMessage(const std::string &channel,\n                           const std::string &message) const noexcept\n{\n    loop_->assertInLoopThread();\n\n    auto iter = listenChannels_.find(channel);\n    if (iter == listenChannels_.end())\n    {\n        return;\n    }\n    for (auto &cb : iter->second)\n    {\n        cb(channel, message);\n    }\n}\n\nvoid PgListener::listenAll() noexcept\n{\n    loop_->assertInLoopThread();\n\n    listenTasks_.clear();\n    for (auto &item : listenChannels_)\n    {\n        listenTasks_.emplace_back(true, item.first);\n    }\n    listenNext();\n}\n\nvoid PgListener::listenNext() noexcept\n{\n    loop_->assertInLoopThread();\n\n    if (listenTasks_.empty())\n    {\n        return;\n    }\n    auto [listen, channel] = listenTasks_.front();\n    listenTasks_.pop_front();\n    listenInLoop(channel, listen);\n}\n\nvoid PgListener::listenInLoop(const std::string &channel,\n                              bool listen,\n                              std::shared_ptr<unsigned int> retryCnt)\n{\n    loop_->assertInLoopThread();\n    if (!retryCnt)\n        retryCnt = std::make_shared<unsigned int>(0);\n    if (conn_ && !conn_->isWorking())\n    {\n        auto pgConn = std::dynamic_pointer_cast<PgConnection>(conn_);\n        std::string escapedChannel =\n            escapeIdentifier(pgConn, channel.c_str(), channel.size());\n        if (escapedChannel.empty())\n        {\n            LOG_ERROR << \"Failed to escape pg identifier, stop listen\";\n            // TODO: report\n            return;\n        }\n\n        // Because DbConnection::execSql() takes std::string_view as parameter,\n        // sql must be hold until query finish.\n        auto sql = std::make_shared<std::string>(\n            (listen ? \"LISTEN \" : \"UNLISTEN \") + escapedChannel);\n        std::weak_ptr<PgListener> weakThis = shared_from_this();\n        conn_->execSql(\n            *sql,\n            0,\n            {},\n            {},\n            {},\n            [listen, channel, sql](const Result &r) {\n                if (listen)\n                {\n                    LOG_TRACE << \"Listen channel \" << channel;\n                }\n                else\n                {\n                    LOG_TRACE << \"Unlisten channel \" << channel;\n                }\n            },\n            [listen, channel, weakThis, sql, retryCnt, loop = loop_](\n                const std::exception_ptr &exception) {\n                try\n                {\n                    std::rethrow_exception(exception);\n                }\n                catch (const DrogonDbException &ex)\n                {\n                    ++(*retryCnt);\n                    if (listen)\n                    {\n                        LOG_ERROR << \"Failed to listen channel \" << channel\n                                  << \", error: \" << ex.base().what();\n                        if (*retryCnt > MAX_LISTEN_RETRY)\n                        {\n                            LOG_ERROR << \"Failed to listen channel \" << channel\n                                      << \" after max attempt. Stop trying.\";\n                            // TODO: report\n                            return;\n                        }\n                    }\n                    else\n                    {\n                        LOG_ERROR << \"Failed to unlisten channel \" << channel\n                                  << \", error: \" << ex.base().what();\n                        if (*retryCnt > MAX_UNLISTEN_RETRY)\n                        {\n                            LOG_ERROR << \"Failed to unlisten channel \"\n                                      << channel\n                                      << \" after max attempt. Stop trying.\";\n                            // TODO: report?\n                            return;\n                        }\n                    }\n                    auto delay = (*retryCnt) < 5 ? (*retryCnt * 2) : 10;\n                    loop->runAfter(delay, [=]() {\n                        auto thisPtr = weakThis.lock();\n                        if (thisPtr)\n                        {\n                            thisPtr->listenInLoop(channel, listen, retryCnt);\n                        }\n                    });\n                }\n            });\n        return;\n    }\n\n    if (listenTasks_.size() > 20000)\n    {\n        LOG_WARN << \"Too many queries in listen buffer. Stop listen channel \"\n                 << channel;\n        // TODO: report\n        return;\n    }\n\n    LOG_TRACE << \"Add to task queue, channel \" << channel;\n    listenTasks_.emplace_back(listen, channel);\n}\n\nPgConnectionPtr PgListener::newConnection(\n    std::shared_ptr<unsigned int> retryCnt)\n{\n    PgConnectionPtr connPtr =\n        std::make_shared<PgConnection>(loop_, connectionInfo_, false);\n    std::weak_ptr<PgListener> weakPtr = shared_from_this();\n    if (!retryCnt)\n        retryCnt = std::make_shared<unsigned int>(0);\n    connPtr->setCloseCallback(\n        [weakPtr, retryCnt](const DbConnectionPtr &closeConnPtr) {\n            auto thisPtr = weakPtr.lock();\n            if (!thisPtr)\n                return;\n            // Erase the connection\n            if (closeConnPtr == thisPtr->conn_)\n            {\n                thisPtr->conn_.reset();\n            }\n            if (closeConnPtr == thisPtr->connHolder_)\n            {\n                thisPtr->connHolder_.reset();\n            }\n            // Reconnect after delay\n            ++(*retryCnt);\n            unsigned int delay = (*retryCnt) < 5 ? (*retryCnt * 2) : 10;\n            thisPtr->loop_->runAfter(delay, [weakPtr, closeConnPtr, retryCnt] {\n                auto thisPtr = weakPtr.lock();\n                if (!thisPtr)\n                    return;\n                assert(!thisPtr->connHolder_);\n                thisPtr->connHolder_ = thisPtr->newConnection(retryCnt);\n            });\n        });\n    connPtr->setOkCallback(\n        [weakPtr, retryCnt](const DbConnectionPtr &okConnPtr) {\n            LOG_TRACE << \"connected after \" << *retryCnt << \" tries\";\n            (*retryCnt) = 0;\n            auto thisPtr = weakPtr.lock();\n            if (!thisPtr)\n                return;\n            assert(!thisPtr->conn_);\n            assert(thisPtr->connHolder_ == okConnPtr);\n            thisPtr->conn_ = okConnPtr;\n            thisPtr->listenAll();\n        });\n    connPtr->setIdleCallback([weakPtr]() {\n        auto thisPtr = weakPtr.lock();\n        if (!thisPtr)\n            return;\n        thisPtr->listenNext();\n    });\n\n    connPtr->setMessageCallback(\n        [weakPtr](const std::string &channel, const std::string &message) {\n            auto thisPtr = weakPtr.lock();\n            if (thisPtr)\n            {\n                thisPtr->onMessage(channel, message);\n            }\n        });\n    connPtr->init();\n    return connPtr;\n}\n\nstd::string PgListener::escapeIdentifier(const PgConnectionPtr &conn,\n                                         const char *str,\n                                         size_t length)\n{\n    auto res = std::unique_ptr<char, std::function<void(char *)>>(\n        PQescapeIdentifier(conn->pgConn().get(), str, length), [](char *res) {\n            if (res)\n            {\n                PQfreemem(res);\n            }\n        });\n    if (!res)\n    {\n        LOG_ERROR << \"Error when escaping identifier [\"\n                  << std::string(str, length) << \"]. \"\n                  << PQerrorMessage(conn->pgConn().get());\n        return {};\n    }\n    return std::string{res.get()};\n}\n"
  },
  {
    "path": "orm_lib/src/postgresql_impl/PgListener.h",
    "content": "/**\n *\n *  @file PgListener.h\n *  @author Nitromelon\n *\n *  Copyright 2022, An Tao.  All rights reserved.\n *  https://github.com/drogonframework/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include <drogon/orm/DbListener.h>\n#include <trantor/net/EventLoopThread.h>\n#include <deque>\n#include <mutex>\n#include <string>\n#include <unordered_map>\n#include \"./PgConnection.h\"\n\nnamespace drogon\n{\nnamespace orm\n{\nclass PgListener : public DbListener,\n                   public std::enable_shared_from_this<PgListener>\n{\n  public:\n    PgListener(std::string connInfo, trantor::EventLoop *loop);\n    ~PgListener() override;\n    void init() noexcept;\n\n    trantor::EventLoop *loop() const\n    {\n        return loop_;\n    }\n\n    void listen(const std::string &channel,\n                MessageCallback messageCallback) noexcept override;\n    void unlisten(const std::string &channel) noexcept override;\n\n    // methods below should be called in loop\n\n    void onMessage(const std::string &channel,\n                   const std::string &message) const noexcept;\n    void listenAll() noexcept;\n    void listenNext() noexcept;\n\n  private:\n    /// Escapes a string for use as an SQL identifier, such as a table, column,\n    /// or function name. This is useful when a user-supplied identifier might\n    /// contain special characters that would otherwise not be interpreted as\n    /// part of the identifier by the SQL parser, or when the identifier might\n    /// contain upper case characters whose case should be preserved.\n    /**\n     * @param str: c-style string to escape. A terminating zero byte is not\n     * required, and should not be counted in length(If a terminating zero byte\n     * is found before length bytes are processed, PQescapeIdentifier stops at\n     * the zero; the behavior is thus rather like strncpy).\n     * @param length: length of the c-style string\n     * @return:  The return string has all special characters replaced so that\n     * it will be properly processed as an SQL identifier. A terminating zero\n     * byte is also added. The return string will also be surrounded by double\n     * quotes.\n     */\n    static std::string escapeIdentifier(const PgConnectionPtr &conn,\n                                        const char *str,\n                                        size_t length);\n\n    void listenInLoop(const std::string &channel,\n                      bool listen,\n                      std::shared_ptr<unsigned int> = nullptr);\n\n    PgConnectionPtr newConnection(std::shared_ptr<unsigned int> = nullptr);\n\n    std::string connectionInfo_;\n    std::unique_ptr<trantor::EventLoopThread> threadPtr_;\n    trantor::EventLoop *loop_;\n    DbConnectionPtr connHolder_;\n    DbConnectionPtr conn_;\n    std::deque<std::pair<bool, std::string>> listenTasks_;\n    std::unordered_map<std::string, std::vector<MessageCallback>>\n        listenChannels_;\n};\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/src/postgresql_impl/PostgreSQLResultImpl.cc",
    "content": "/**\n *\n *  PostgreSQLResultImpl.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"PostgreSQLResultImpl.h\"\n#include <cassert>\n#include <drogon/orm/Exception.h>\n\nusing namespace drogon::orm;\n\nResult::SizeType PostgreSQLResultImpl::size() const noexcept\n{\n    return result_ ? PQntuples(result_.get()) : 0;\n}\n\nResult::RowSizeType PostgreSQLResultImpl::columns() const noexcept\n{\n    auto ptr = const_cast<PGresult *>(result_.get());\n    return ptr ? Result::RowSizeType(PQnfields(ptr)) : 0;\n}\n\nconst char *PostgreSQLResultImpl::columnName(RowSizeType number) const\n{\n    auto ptr = const_cast<PGresult *>(result_.get());\n    if (ptr)\n    {\n        auto N = PQfname(ptr, int(number));\n        assert(N);\n        return N;\n    }\n    throw \"nullptr result\";  // The program will never execute here\n}\n\nResult::SizeType PostgreSQLResultImpl::affectedRows() const noexcept\n{\n    char *str = PQcmdTuples(result_.get());\n    if (str == nullptr || str[0] == '\\0')\n        return 0;\n    return atol(str);\n}\n\nResult::RowSizeType PostgreSQLResultImpl::columnNumber(\n    const char colName[]) const\n{\n    auto ptr = const_cast<PGresult *>(result_.get());\n    if (ptr)\n    {\n        auto N = PQfnumber(ptr, colName);\n        if (N == -1)\n            throw RangeError(std::string(\"there is no column named \") +\n                             colName);\n        return N;\n    }\n    throw \"nullptr result\";  // The program will never execute here\n}\n\nconst char *PostgreSQLResultImpl::getValue(SizeType row,\n                                           RowSizeType column) const\n{\n    return PQgetvalue(result_.get(), int(row), int(column));\n}\n\nbool PostgreSQLResultImpl::isNull(SizeType row, RowSizeType column) const\n{\n    return PQgetisnull(result_.get(), int(row), int(column)) != 0;\n}\n\nResult::FieldSizeType PostgreSQLResultImpl::getLength(SizeType row,\n                                                      RowSizeType column) const\n{\n    return PQgetlength(result_.get(), int(row), int(column));\n}\n\nint PostgreSQLResultImpl::oid(RowSizeType column) const\n{\n    return PQftype(result_.get(), (int)column);\n}\n"
  },
  {
    "path": "orm_lib/src/postgresql_impl/PostgreSQLResultImpl.h",
    "content": "/**\n *\n *  PostgreSQLResultImpl.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"../ResultImpl.h\"\n\n#include <libpq-fe.h>\n#include <memory>\n#include <string>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass PostgreSQLResultImpl : public ResultImpl\n{\n  public:\n    explicit PostgreSQLResultImpl(std::shared_ptr<PGresult> r) noexcept\n        : result_(std::move(r))\n    {\n    }\n\n    SizeType size() const noexcept override;\n    RowSizeType columns() const noexcept override;\n    const char *columnName(RowSizeType number) const override;\n    SizeType affectedRows() const noexcept override;\n    RowSizeType columnNumber(const char colName[]) const override;\n    const char *getValue(SizeType row, RowSizeType column) const override;\n    bool isNull(SizeType row, RowSizeType column) const override;\n    FieldSizeType getLength(SizeType row, RowSizeType column) const override;\n    int oid(RowSizeType column) const override;\n\n  private:\n    std::shared_ptr<PGresult> result_;\n};\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/src/postgresql_impl/test/CMakeLists.txt",
    "content": "link_libraries(${PROJECT_NAME})\n\nadd_executable(pg_test1 test1.cc)\nadd_executable(pg_test2 test2.cc)\n\nset_property(TARGET pg_test1 pg_test2\n             PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})\nset_property(TARGET pg_test1 pg_test2 PROPERTY CXX_STANDARD_REQUIRED ON)\nset_property(TARGET pg_test1 pg_test2 PROPERTY CXX_EXTENSIONS OFF)\n"
  },
  {
    "path": "orm_lib/src/postgresql_impl/test/test1.cc",
    "content": "#include <drogon/orm/DbClient.h>\n#include <iostream>\n#include <trantor/utils/Logger.h>\n#include <thread>\n#include <chrono>\nusing namespace std::chrono_literals;\nusing namespace drogon::orm;\n\nint main()\n{\n    trantor::Logger::setLogLevel(trantor::Logger::kTrace);\n    auto clientPtr =\n        DbClient::newPgClient(\"host=127.0.0.1 port=5432 dbname=test user=antao\",\n                              3);\n    LOG_DEBUG << \"start!\";\n    std::this_thread::sleep_for(1s);\n    *clientPtr << \"update group_users set join_date=$1,relationship=$2 where \"\n                  \"g_uuid=420040 and u_uuid=2\"\n               << nullptr << nullptr << Mode::Blocking >>\n        [](const Result &r) {\n            std::cout << \"update \" << r.affectedRows() << \" lines\" << std::endl;\n        } >>\n        [](const DrogonDbException &e) {\n            std::cerr << e.base().what() << std::endl;\n        };\n    try\n    {\n        auto r =\n            clientPtr->execSqlSync(\"select * from users where user_uuid=$1;\",\n                                   1);\n        for (auto const &row : r)\n        {\n            for (auto const &f : row)\n            {\n                std::cout << f.name() << \" : \" << std::endl;\n            }\n        }\n    }\n    catch (const drogon::orm::DrogonDbException &e)\n    {\n        LOG_DEBUG << \"catch:\" << e.base().what();\n    }\n\n    // client << \"select count(*) from users\" >> [](const drogon::orm::Result\n    // &r)\n    // {\n    //     for (auto row : r)\n    //     {\n    //         for (auto f : row)\n    //         {\n    //             std::cout << f.name() << \" : \" << f.as<int>() << std::endl;\n    //         }\n    //     }\n    // } >> [](const drogon::orm::DrogonDbException &e) {\n    //     LOG_DEBUG << \"except callback:\" << e.base().what();\n    // };\n\n    // client << \"select * from users limit 5\" >> [](const drogon::orm::Result\n    // &r)\n    // {\n    //     for (auto row : r)\n    //     {\n    //         for (auto f : row)\n    //         {\n    //             std::cout << f.name() << \" : \" << f.as<int>() << std::endl;\n    //         }\n    //     }\n    // } >> [](const drogon::orm::DrogonDbException &e) {\n    //     LOG_DEBUG << \"except callback:\" << e.base().what();\n    // };\n\n    // client << \"select user_id,user_uuid from users where user_uuid=$1\"\n    // << 2\n    // >> [](bool isNull, const std::string &id, uint64_t uuid) {\n    //     if (!isNull)\n    //         std::cout << \"id is \" << id << \"(\" << uuid << \")\" << std::endl;\n    //     else\n    //         std::cout << \"no more!\" << std::endl;\n    // } >> [](const drogon::orm::DrogonDbException &e) {\n    //     LOG_DEBUG << \"except callback:\" << e.base().what();\n    // };\n\n    // client.execSqlAsync(\"\",\n    //                     [](const drogon::orm::Result &r) {},\n    //                     [](const drogon::orm::DrogonDbException &e) {\n    //                         LOG_DEBUG << \"async blocking except callback:\" <<\n    //                         e.base().what();\n    //                     },\n    //                     true);\n    auto f = clientPtr->execSqlAsyncFuture(\n        \"select * from users where user_uuid > $1 limit $2 offset $3\",\n        100,\n        (size_t)2,\n        (size_t)2);\n    try\n    {\n        auto r = f.get();\n        for (auto const &row : r)\n        {\n            std::cout << \"user_id:\" << row[\"user_id\"].as<std::string>()\n                      << std::endl;\n        }\n    }\n    catch (const drogon::orm::DrogonDbException &e)\n    {\n        LOG_DEBUG << \"future exception:\" << e.base().what();\n    }\n    // client << \"\\\\d users\"\n    // >>[](const Result &r)\n    // {\n    //     std::cout<<\"got a result!\"<<std::endl;\n    // }\n    // >>[](const DrogonDbException &e)\n    // {\n    //     std::cout<< e.base().what()<<std::endl;\n    // };\n    clientPtr->execSqlAsync(\n        \"select * from users where user_uuid=$1;\",\n        [](const drogon::orm::Result &r) {\n            LOG_DEBUG << \"row count:\" << r.size();\n        },\n        [](const drogon::orm::DrogonDbException &e) {\n            LOG_DEBUG << \"async nonblocking except callback:\"\n                      << e.base().what();\n        },\n        1);\n    clientPtr->execSqlAsync(\n        \"select * from users where org_name=$1\",\n        [](const Result &r) {\n            std::cout << r.size() << \" rows selected!\" << std::endl;\n            int i = 0;\n            for (auto row : r)\n            {\n                std::cout << i++ << \": user name is \"\n                          << row[\"user_name\"].as<std::string>() << std::endl;\n                std::cout << \"admin column is \"\n                          << (row[\"admin\"].isNull() ? \"null\" : \"not null\")\n                          << std::endl;\n                std::cout << \"bool value of admin column is :\"\n                          << row[\"admin\"].as<bool>() << std::endl;\n                std::cout << \"string value of admin column is :\"\n                          << row[\"admin\"].as<std::string>() << std::endl;\n            }\n        },\n        [](const DrogonDbException &e) {\n            std::cerr << \"error:\" << e.base().what() << std::endl;\n        },\n        \"default\");\n    *clientPtr << \"select t2,t9 from ttt where t7=2\" >> [](const Result &r) {\n        std::cout << r.size() << \" rows selected!\" << std::endl;\n        for (auto row : r)\n        {\n            std::cout << row[\"t9\"].as<std::string>() << std::endl;\n            std::cout << row[\"t2\"].as<int>() << std::endl;\n        }\n    } >> [](const DrogonDbException &e) {\n        std::cerr << \"error:\" << e.base().what() << std::endl;\n    };\n\n    *clientPtr << \"select t1.*, t2.* from users t1 left join groups t2 on \"\n                  \"t1.talkgroup_uuid=t2.g_uuid\" >>\n        [](const Result &r) {\n            std::cout << r.size() << \" rows selected!\" << std::endl;\n            if (r.size() == 0)\n                return;\n            for (auto f : r[0])\n            {\n                std::cout << f.name() << std::endl;\n            }\n        } >>\n        [](const DrogonDbException &e) {\n            std::cerr << \"error:\" << e.base().what() << std::endl;\n        };\n    getchar();\n}\n"
  },
  {
    "path": "orm_lib/src/postgresql_impl/test/test2.cc",
    "content": "#include <drogon/orm/DbClient.h>\n#include <drogon/orm/Mapper.h>\n\n#include <iostream>\n#include <string>\n#include <trantor/utils/Logger.h>\n#include <thread>\n#include <chrono>\n\nusing namespace std::chrono_literals;\nusing namespace drogon::orm;\n\nclass User\n{\n  public:\n    static const std::string primaryKeyName;\n    static const bool hasPrimaryKey;\n    static const std::string tableName;\n\n    using PrimaryKeyType = int;\n\n    explicit User(const Row &r)\n        : userId_(r[\"user_id\"].as<std::string>()),\n          userName_(r[\"user_name\"].as<std::string>())\n    {\n    }\n\n    std::string userId_;\n    std::string userName_;\n\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from users where user_uuid = $1\";\n        return sql;\n    }\n};\n\nconst std::string User::primaryKeyName = \"user_uuid\";\nconst bool User::hasPrimaryKey = true;\nconst std::string User::tableName = \"users\";\n\nint main()\n{\n    trantor::Logger::setLogLevel(trantor::Logger::kTrace);\n    auto client =\n        DbClient::newPgClient(\"host=127.0.0.1 port=5432 dbname=test user=antao\",\n                              1);\n    std::this_thread::sleep_for(1s);\n    LOG_DEBUG << \"start!\";\n    {\n        auto trans = client->newTransaction([](bool committed) {\n            std::cout << \"The transaction submission \"\n                      << (committed ? \"succeeded\" : \"failed\") << std::endl;\n        });\n        *trans << \"delete from users where user_uuid=201\" >>\n            [trans](const Result &r) {\n                std::cout << \"delete \" << r.affectedRows() << \"user!!!!!\"\n                          << std::endl;\n                trans->rollback();\n            } >>\n            [](const DrogonDbException &e) {\n                std::cout << e.base().what() << std::endl;\n            };\n\n        *trans << \"delete from users where user_uuid=201\" >>\n            [](const Result &r) {\n                std::cout << \"delete \" << r.affectedRows() << \"user!!!!!\"\n                          << std::endl;\n            } >>\n            [](const DrogonDbException &e) {\n                std::cout << e.base().what() << std::endl;\n            };\n    }\n\n    Mapper<User> mapper(client);\n    auto U = mapper.findByPrimaryKey(2);\n    std::cout << \"id=\" << U.userId_ << std::endl;\n    std::cout << \"name=\" << U.userName_ << std::endl;\n    *client << \"select * from array_test\" >>\n        [](bool isNull,\n           const std::vector<std::shared_ptr<int>> &a,\n           const std::string &b,\n           int c) {\n            if (!isNull)\n            {\n                std::cout << \"a.len=\" << a.size() << std::endl;\n                for (size_t i = 0; i < a.size(); ++i)\n                {\n                    std::cout << \"a[\" << i << \"]=\" << *a[i] << \" \";\n                }\n                std::cout << std::endl;\n                std::cout << \"b=\" << b << \" b.len=\" << b.length() << std::endl;\n                std::cout << \"c=\" << c << std::endl;\n            }\n        } >>\n        [](const DrogonDbException &e) {\n            std::cout << e.base().what() << std::endl;\n        };\n    for (int i = 0; i < 100; ++i)\n        mapper.findByPrimaryKey(\n            2,\n            [](const User &u) { std::cout << \"get a user by pk\" << std::endl; },\n            [](const DrogonDbException &e) { throw std::exception(); });\n    getchar();\n}\n"
  },
  {
    "path": "orm_lib/src/sqlite3_impl/Sqlite3Connection.cc",
    "content": "/**\n *\n *  @file Sqlite3Connection.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"Sqlite3Connection.h\"\n#include \"Sqlite3ResultImpl.h\"\n#include <drogon/orm/Exception.h>\n#include <drogon/utils/Utilities.h>\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <cctype>\n#include <exception>\n#include <mutex>\n#include <regex>\n\nusing namespace drogon;\nusing namespace drogon::orm;\n\nnamespace\n{\n\n}\n\nstd::once_flag Sqlite3Connection::once_;\n\nvoid Sqlite3Connection::onError(\n    const std::string_view &sql,\n    const std::function<void(const std::exception_ptr &)> &exceptCallback,\n    const int &extendedErrcode)\n{\n    int errcode = extendedErrcode & 0xFF;  // low 8 bit\n#define ORM_ERR_CASE(code, type)                                    \\\n    case code:                                                      \\\n    {                                                               \\\n        auto exceptPtr = std::make_exception_ptr(                   \\\n            drogon::orm::type(sqlite3_errmsg(connectionPtr_.get()), \\\n                              std::string{sql},                     \\\n                              errcode,                              \\\n                              extendedErrcode));                    \\\n        exceptCallback(exceptPtr);                                  \\\n        return;                                                     \\\n    };\n    switch (extendedErrcode)\n    {\n        ORM_ERR_CASE(SQLITE_CONSTRAINT_NOTNULL, NotNullViolation)\n        ORM_ERR_CASE(SQLITE_CONSTRAINT_FOREIGNKEY, ForeignKeyViolation)\n        ORM_ERR_CASE(SQLITE_CONSTRAINT_PRIMARYKEY, UniqueViolation)\n        ORM_ERR_CASE(SQLITE_CONSTRAINT_UNIQUE, UniqueViolation)\n        ORM_ERR_CASE(SQLITE_CONSTRAINT_CHECK, CheckViolation)\n    }\n    switch (errcode)\n    {\n        ORM_ERR_CASE(SQLITE_MISMATCH, DataException)\n        ORM_ERR_CASE(SQLITE_CONSTRAINT, IntegrityConstraintViolation)\n        ORM_ERR_CASE(SQLITE_PERM, InsufficientPrivilege)\n        ORM_ERR_CASE(SQLITE_AUTH, InsufficientPrivilege)\n        ORM_ERR_CASE(SQLITE_NOMEM, OutOfMemory)\n        ORM_ERR_CASE(SQLITE_FULL, DiskFull)\n    }\n#undef ORM_ERR_CASE\n\n    auto exceptPtr =\n        std::make_exception_ptr(SqlError(sqlite3_errmsg(connectionPtr_.get()),\n                                         std::string{sql},\n                                         errcode,\n                                         extendedErrcode));\n    exceptCallback(exceptPtr);\n}\n\nSqlite3Connection::Sqlite3Connection(\n    trantor::EventLoop *loop,\n    const std::string &connInfo,\n    const std::shared_ptr<SharedMutex> &sharedMutex)\n    : DbConnection(loop), sharedMutexPtr_(sharedMutex), connInfo_(connInfo)\n{\n}\n\nvoid Sqlite3Connection::init()\n{\n    loopThread_.run();\n    loop_ = loopThread_.getLoop();\n    std::call_once(once_, []() {\n        auto ret = sqlite3_config(SQLITE_CONFIG_MULTITHREAD);\n        if (ret != SQLITE_OK)\n        {\n            LOG_FATAL << \"SQLITE_CONFIG_MULTITHREAD is not supported!\";\n        }\n    });\n    // Get the key and value\n    auto connParams = parseConnString(connInfo_);\n    std::string filename;\n    for (auto const &kv : connParams)\n    {\n        auto key = kv.first;\n        auto value = kv.second;\n        std::transform(key.begin(),\n                       key.end(),\n                       key.begin(),\n                       [](unsigned char c) { return tolower(c); });\n        if (key == \"filename\")\n        {\n            filename = value;\n        }\n    }\n    loop_->runInLoop([this, filename = std::move(filename)]() {\n        sqlite3 *tmp = nullptr;\n        auto ret = sqlite3_open(filename.data(), &tmp);\n        connectionPtr_ = std::shared_ptr<sqlite3>(tmp, [](sqlite3 *ptr) {\n            sqlite3_close(ptr);\n        });\n        auto thisPtr = shared_from_this();\n        if (ret != SQLITE_OK)\n        {\n            LOG_FATAL << sqlite3_errmsg(connectionPtr_.get());\n            closeCallback_(thisPtr);\n        }\n        else\n        {\n            sqlite3_extended_result_codes(tmp, true);\n            status_ = ConnectStatus::Ok;\n            okCallback_(thisPtr);\n        }\n    });\n}\n\nvoid Sqlite3Connection::execSql(\n    std::string_view &&sql,\n    size_t paraNum,\n    std::vector<const char *> &&parameters,\n    std::vector<int> &&length,\n    std::vector<int> &&format,\n    ResultCallback &&rcb,\n    std::function<void(const std::exception_ptr &)> &&exceptCallback)\n{\n    auto thisPtr = shared_from_this();\n    loopThread_.getLoop()->queueInLoop(\n        [thisPtr,\n         sql = std::move(sql),\n         paraNum,\n         parameters = std::move(parameters),\n         length = std::move(length),\n         format = std::move(format),\n         rcb = std::move(rcb),\n         exceptCallback = std::move(exceptCallback)]() mutable {\n            thisPtr->execSqlInQueue(\n                sql, paraNum, parameters, length, format, rcb, exceptCallback);\n        });\n}\n\nvoid Sqlite3Connection::execSqlInQueue(\n    const std::string_view &sql,\n    size_t paraNum,\n    const std::vector<const char *> &parameters,\n    const std::vector<int> &length,\n    const std::vector<int> &format,\n    const ResultCallback &rcb,\n    const std::function<void(const std::exception_ptr &)> &exceptCallback)\n{\n    LOG_TRACE << \"sql:\" << sql;\n    if (status_ != ConnectStatus::Ok)\n    {\n        LOG_ERROR << \"Connection is not ready\";\n        auto exceptPtr =\n            std::make_exception_ptr(drogon::orm::BrokenConnection());\n        exceptCallback(exceptPtr);\n        return;\n    }\n    std::shared_ptr<sqlite3_stmt> stmtPtr;\n    bool newStmt = false;\n    if (paraNum > 0)\n    {\n        auto iter = stmtsMap_.find(sql);\n        if (iter != stmtsMap_.end())\n        {\n            stmtPtr = iter->second;\n        }\n    }\n    if (!stmtPtr)\n    {\n        sqlite3_stmt *stmt = nullptr;\n        newStmt = true;\n        const char *remaining;\n        auto ret = sqlite3_prepare_v2(\n            connectionPtr_.get(), sql.data(), -1, &stmt, &remaining);\n        stmtPtr = stmt ? std::shared_ptr<sqlite3_stmt>(stmt,\n                                                       [](sqlite3_stmt *p) {\n                                                           sqlite3_finalize(p);\n                                                       })\n                       : nullptr;\n        if (ret != SQLITE_OK || !stmtPtr)\n        {\n            int ext_ret = sqlite3_extended_errcode(connectionPtr_.get());\n            onError(sql, exceptCallback, ext_ret);\n            idleCb_();\n            return;\n        }\n        if (!std::all_of(remaining, sql.data() + sql.size(), [](char ch) {\n                return std::isspace(static_cast<unsigned char>(ch));\n            }))\n        {\n            auto exceptPtr = std::make_exception_ptr(SqlError(\n                \"Multiple semicolon separated statements are unsupported\",\n                std::string{sql}));\n            exceptCallback(exceptPtr);\n            idleCb_();\n            return;\n        }\n    }\n    assert(stmtPtr);\n    auto stmt = stmtPtr.get();\n    for (int i = 0; i < (int)parameters.size(); ++i)\n    {\n        int bindRet{SQLITE_OK};\n        switch (format[i])\n        {\n            case Sqlite3TypeChar:\n                bindRet = sqlite3_bind_int(stmt, i + 1, *(char *)parameters[i]);\n                break;\n            case Sqlite3TypeShort:\n                bindRet =\n                    sqlite3_bind_int(stmt, i + 1, *(short *)parameters[i]);\n                break;\n            case Sqlite3TypeInt:\n                bindRet =\n                    sqlite3_bind_int(stmt, i + 1, *(int32_t *)parameters[i]);\n                break;\n            case Sqlite3TypeInt64:\n                bindRet =\n                    sqlite3_bind_int64(stmt, i + 1, *(int64_t *)parameters[i]);\n                break;\n            case Sqlite3TypeDouble:\n                bindRet =\n                    sqlite3_bind_double(stmt, i + 1, *(double *)parameters[i]);\n                break;\n            case Sqlite3TypeText:\n                bindRet = sqlite3_bind_text(\n                    stmt, i + 1, parameters[i], -1, SQLITE_STATIC);\n                break;\n            case Sqlite3TypeBlob:\n                bindRet = sqlite3_bind_blob(\n                    stmt, i + 1, parameters[i], length[i], SQLITE_STATIC);\n                break;\n            case Sqlite3TypeNull:\n                bindRet = sqlite3_bind_null(stmt, i + 1);\n                break;\n            default:\n                LOG_FATAL << \"SQLite does not recognize the parameter type\";\n                abort();\n        }\n        if (bindRet != SQLITE_OK)\n        {\n            int eret = sqlite3_extended_errcode(connectionPtr_.get());\n            onError(sql, exceptCallback, eret);\n            sqlite3_reset(stmt);\n            idleCb_();\n            return;\n        }\n    }\n    int r, er;\n    int columnNum = sqlite3_column_count(stmt);\n    auto resultPtr = std::make_shared<Sqlite3ResultImpl>();\n    for (int i = 0; i < columnNum; ++i)\n    {\n        auto name = std::string(sqlite3_column_name(stmt, i));\n        std::transform(name.begin(),\n                       name.end(),\n                       name.begin(),\n                       [](unsigned char c) { return tolower(c); });\n        LOG_TRACE << \"column name:\" << name;\n        resultPtr->columnNames_.push_back(name);\n        resultPtr->columnNamesMap_.insert({name, i});\n    }\n\n    if (sqlite3_stmt_readonly(stmt))\n    {\n        // Readonly, hold read lock;\n        std::shared_lock<SharedMutex> lock(*sharedMutexPtr_);\n        r = stmtStep(stmt, resultPtr, columnNum);\n        if (r != SQLITE_DONE)\n        {\n            er = sqlite3_extended_errcode(connectionPtr_.get());\n        }\n        sqlite3_reset(stmt);\n    }\n    else\n    {\n        // Hold write lock\n        std::unique_lock<SharedMutex> lock(*sharedMutexPtr_);\n        r = stmtStep(stmt, resultPtr, columnNum);\n        if (r == SQLITE_DONE)\n        {\n            resultPtr->affectedRows_ = sqlite3_changes(connectionPtr_.get());\n            resultPtr->insertId_ =\n                sqlite3_last_insert_rowid(connectionPtr_.get());\n        }\n        else\n        {\n            er = sqlite3_extended_errcode(connectionPtr_.get());\n        }\n        sqlite3_reset(stmt);\n    }\n\n    if (r != SQLITE_DONE)\n    {\n        onError(sql, exceptCallback, er);\n        sqlite3_reset(stmt);\n        idleCb_();\n        return;\n    }\n    if (paraNum > 0 && newStmt)\n    {\n        auto r = stmts_.insert(std::string{sql});\n        stmtsMap_[std::string_view{r.first->data(), r.first->length()}] =\n            stmtPtr;\n    }\n    rcb(Result(std::move(resultPtr)));\n    idleCb_();\n}\n\nint Sqlite3Connection::stmtStep(\n    sqlite3_stmt *stmt,\n    const std::shared_ptr<Sqlite3ResultImpl> &resultPtr,\n    int columnNum)\n{\n    int r;\n    while ((r = sqlite3_step(stmt)) == SQLITE_ROW)\n    {\n        std::vector<std::shared_ptr<std::string>> row;\n        for (int i = 0; i < columnNum; ++i)\n        {\n            switch (sqlite3_column_type(stmt, i))\n            {\n                case SQLITE_INTEGER:\n                    row.push_back(std::make_shared<std::string>(\n                        std::to_string(sqlite3_column_int64(stmt, i))));\n                    break;\n                case SQLITE_FLOAT:\n                    row.push_back(std::make_shared<std::string>(\n                        std::to_string(sqlite3_column_double(stmt, i))));\n                    break;\n                case SQLITE_TEXT:\n                    row.push_back(std::make_shared<std::string>(\n                        (const char *)sqlite3_column_text(stmt, i),\n                        (size_t)sqlite3_column_bytes(stmt, i)));\n                    break;\n                case SQLITE_BLOB:\n                {\n                    const char *buf =\n                        (const char *)sqlite3_column_blob(stmt, i);\n                    size_t len = sqlite3_column_bytes(stmt, i);\n                    row.push_back(buf ? std::make_shared<std::string>(buf, len)\n                                      : std::make_shared<std::string>());\n                }\n                break;\n                case SQLITE_NULL:\n                    row.push_back(nullptr);\n                    break;\n            }\n        }\n        resultPtr->result_.push_back(std::move(row));\n    }\n    return r;\n}\n\nvoid Sqlite3Connection::disconnect()\n{\n    std::promise<int> pro;\n    auto f = pro.get_future();\n    auto thisPtr = shared_from_this();\n    std::weak_ptr<Sqlite3Connection> weakPtr = thisPtr;\n    loopThread_.getLoop()->runInLoop([weakPtr, &pro]() {\n        {\n            auto thisPtr = weakPtr.lock();\n            if (!thisPtr)\n                return;\n            thisPtr->status_ = ConnectStatus::Bad;\n            thisPtr->connectionPtr_.reset();\n        }\n        pro.set_value(1);\n    });\n    f.get();\n}\n"
  },
  {
    "path": "orm_lib/src/sqlite3_impl/Sqlite3Connection.h",
    "content": "/**\n *\n *  @file Sqlite3Connection.h\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"../DbConnection.h\"\n#include \"Sqlite3ResultImpl.h\"\n#include <drogon/orm/DbClient.h>\n#include <trantor/net/EventLoopThread.h>\n#include <trantor/utils/NonCopyable.h>\n#include <trantor/utils/SerialTaskQueue.h>\n#include <sqlite3.h>\n#include <functional>\n#include <iostream>\n#include <memory>\n#include <shared_mutex>\n#include <string>\n#include <thread>\n#include <set>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass Sqlite3Connection;\nusing Sqlite3ConnectionPtr = std::shared_ptr<Sqlite3Connection>;\n\nclass Sqlite3Connection : public DbConnection,\n                          public std::enable_shared_from_this<Sqlite3Connection>\n{\n  public:\n    Sqlite3Connection(trantor::EventLoop *loop,\n                      const std::string &connInfo,\n                      const std::shared_ptr<SharedMutex> &sharedMutex);\n\n    void init() override;\n\n    void execSql(std::string_view &&sql,\n                 size_t paraNum,\n                 std::vector<const char *> &&parameters,\n                 std::vector<int> &&length,\n                 std::vector<int> &&format,\n                 ResultCallback &&rcb,\n                 std::function<void(const std::exception_ptr &)>\n                     &&exceptCallback) override;\n\n    void batchSql(std::deque<std::shared_ptr<SqlCmd>> &&) override\n    {\n        LOG_FATAL << \"The mysql library does not support batch mode\";\n        exit(1);\n    }\n\n    void disconnect() override;\n\n  private:\n    static std::once_flag once_;\n    void execSqlInQueue(\n        const std::string_view &sql,\n        size_t paraNum,\n        const std::vector<const char *> &parameters,\n        const std::vector<int> &length,\n        const std::vector<int> &format,\n        const ResultCallback &rcb,\n        const std::function<void(const std::exception_ptr &)> &exceptCallback);\n    void onError(\n        const std::string_view &sql,\n        const std::function<void(const std::exception_ptr &)> &exceptCallback,\n        const int &extendedErrcode);\n    int stmtStep(sqlite3_stmt *stmt,\n                 const std::shared_ptr<Sqlite3ResultImpl> &resultPtr,\n                 int columnNum);\n    trantor::EventLoopThread loopThread_;\n    std::shared_ptr<sqlite3> connectionPtr_;\n    std::shared_ptr<SharedMutex> sharedMutexPtr_;\n    std::unordered_map<std::string_view, std::shared_ptr<sqlite3_stmt>>\n        stmtsMap_;\n    std::set<std::string> stmts_;\n    std::string connInfo_;\n};\n\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/src/sqlite3_impl/Sqlite3ResultImpl.cc",
    "content": "/**\n *\n *  Sqlite3ResultImpl.cc\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#include \"Sqlite3ResultImpl.h\"\n#include <algorithm>\n#include <cassert>\n#include <drogon/orm/Exception.h>\n\nusing namespace drogon::orm;\n\nResult::SizeType Sqlite3ResultImpl::size() const noexcept\n{\n    return result_.size();\n}\n\nResult::RowSizeType Sqlite3ResultImpl::columns() const noexcept\n{\n    return result_.empty() ? 0 : result_[0].size();\n}\n\nconst char *Sqlite3ResultImpl::columnName(RowSizeType number) const\n{\n    assert(number < columnNames_.size());\n    return columnNames_[number].c_str();\n}\n\nResult::SizeType Sqlite3ResultImpl::affectedRows() const noexcept\n{\n    return affectedRows_;\n}\n\nResult::RowSizeType Sqlite3ResultImpl::columnNumber(const char colName[]) const\n{\n    auto name = std::string(colName);\n    std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) {\n        return tolower(c);\n    });\n    auto iter = columnNamesMap_.find(name);\n    if (iter != columnNamesMap_.end())\n    {\n        return iter->second;\n    }\n    throw RangeError(std::string(\"there is no column named \") + colName);\n}\n\nconst char *Sqlite3ResultImpl::getValue(SizeType row, RowSizeType column) const\n{\n    auto col = result_[row][column];\n    return col ? col->c_str() : nullptr;\n}\n\nbool Sqlite3ResultImpl::isNull(SizeType row, RowSizeType column) const\n{\n    return !result_[row][column];\n}\n\nResult::FieldSizeType Sqlite3ResultImpl::getLength(SizeType row,\n                                                   RowSizeType column) const\n{\n    auto col = result_[row][column];\n    return col ? col->length() : 0;\n}\n\nunsigned long long Sqlite3ResultImpl::insertId() const noexcept\n{\n    return insertId_;\n}\n"
  },
  {
    "path": "orm_lib/src/sqlite3_impl/Sqlite3ResultImpl.h",
    "content": "/**\n *\n *  Sqlite3ResultImpl.h\n *  An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  https://github.com/an-tao/drogon\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n */\n\n#pragma once\n\n#include \"../ResultImpl.h\"\n\n#include <sqlite3.h>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass Sqlite3ResultImpl : public ResultImpl\n{\n  public:\n    Sqlite3ResultImpl() = default;\n    SizeType size() const noexcept override;\n    RowSizeType columns() const noexcept override;\n    const char *columnName(RowSizeType number) const override;\n    SizeType affectedRows() const noexcept override;\n    RowSizeType columnNumber(const char colName[]) const override;\n    const char *getValue(SizeType row, RowSizeType column) const override;\n    bool isNull(SizeType row, RowSizeType column) const override;\n    FieldSizeType getLength(SizeType row, RowSizeType column) const override;\n    unsigned long long insertId() const noexcept override;\n\n  private:\n    friend class Sqlite3Connection;\n    std::vector<std::vector<std::shared_ptr<std::string>>> result_;\n    std::string query_;\n    std::vector<std::string> columnNames_;\n    std::unordered_map<std::string, size_t> columnNamesMap_;\n    size_t affectedRows_{0};\n    size_t insertId_{0};\n};\n}  // namespace orm\n}  // namespace drogon\n"
  },
  {
    "path": "orm_lib/src/sqlite3_impl/test/CMakeLists.txt",
    "content": "link_libraries(${PROJECT_NAME})\n\nadd_executable(sqlite3_test1 test1.cc Groups.cc)\n\nset_property(TARGET sqlite3_test1 PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})\nset_property(TARGET sqlite3_test1 PROPERTY CXX_STANDARD_REQUIRED ON)\nset_property(TARGET sqlite3_test1 PROPERTY CXX_EXTENSIONS OFF)\n"
  },
  {
    "path": "orm_lib/src/sqlite3_impl/test/Groups.cc",
    "content": "/**\n *\n *  Groups.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Groups.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon_model::sqlite3;\n\nconst std::string Groups::Cols::_group_id = \"group_id\";\nconst std::string Groups::Cols::_group_name = \"group_name\";\nconst std::string Groups::Cols::_creater_id = \"creater_id\";\nconst std::string Groups::Cols::_create_time = \"create_time\";\nconst std::string Groups::Cols::_inviting = \"inviting\";\nconst std::string Groups::Cols::_inviting_user_id = \"inviting_user_id\";\nconst std::string Groups::Cols::_avatar_id = \"avatar_id\";\nconst std::string Groups::Cols::_uuu = \"uuu\";\nconst std::string Groups::Cols::_text = \"text\";\nconst std::string Groups::Cols::_avatar = \"avatar\";\nconst std::string Groups::Cols::_is_default = \"is_default\";\nconst std::string Groups::primaryKeyName = \"group_id\";\nconst bool Groups::hasPrimaryKey = true;\nconst std::string Groups::tableName = \"groups\";\n\nconst std::vector<typename Groups::MetaData> Groups::metaData_ = {\n    {\"group_id\", \"uint64_t\", \"integer\", 8, 1, 1, 0},\n    {\"group_name\", \"std::string\", \"text\", 0, 0, 0, 0},\n    {\"creater_id\", \"uint64_t\", \"integer\", 8, 0, 0, 0},\n    {\"create_time\", \"std::string\", \"text\", 0, 0, 0, 0},\n    {\"inviting\", \"uint64_t\", \"integer\", 8, 0, 0, 0},\n    {\"inviting_user_id\", \"uint64_t\", \"integer\", 8, 0, 0, 0},\n    {\"avatar_id\", \"std::string\", \"text\", 0, 0, 0, 0},\n    {\"uuu\", \"double\", \"double\", 8, 0, 0, 0},\n    {\"text\", \"std::string\", \"varchar(255)\", 0, 0, 0, 0},\n    {\"avatar\", \"std::vector<char>\", \"blob\", 0, 0, 0, 0},\n    {\"is_default\", \"bool\", \"bool\", 1, 0, 0, 0}};\n\nconst std::string &Groups::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nGroups::Groups(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"group_id\"].isNull())\n        {\n            groupId_ = std::make_shared<uint64_t>(r[\"group_id\"].as<uint64_t>());\n        }\n        if (!r[\"group_name\"].isNull())\n        {\n            groupName_ = std::make_shared<std::string>(\n                r[\"group_name\"].as<std::string>());\n        }\n        if (!r[\"creater_id\"].isNull())\n        {\n            createrId_ =\n                std::make_shared<uint64_t>(r[\"creater_id\"].as<uint64_t>());\n        }\n        if (!r[\"create_time\"].isNull())\n        {\n            createTime_ = std::make_shared<std::string>(\n                r[\"create_time\"].as<std::string>());\n        }\n        if (!r[\"inviting\"].isNull())\n        {\n            inviting_ =\n                std::make_shared<uint64_t>(r[\"inviting\"].as<uint64_t>());\n        }\n        if (!r[\"inviting_user_id\"].isNull())\n        {\n            invitingUserId_ = std::make_shared<uint64_t>(\n                r[\"inviting_user_id\"].as<uint64_t>());\n        }\n        if (!r[\"avatar_id\"].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(r[\"avatar_id\"].as<std::string>());\n        }\n        if (!r[\"uuu\"].isNull())\n        {\n            uuu_ = std::make_shared<double>(r[\"uuu\"].as<double>());\n        }\n        if (!r[\"text\"].isNull())\n        {\n            text_ = std::make_shared<std::string>(r[\"text\"].as<std::string>());\n        }\n        if (!r[\"avatar\"].isNull())\n        {\n            avatar_ = std::make_shared<std::vector<char>>(\n                r[\"avatar\"].as<std::vector<char>>());\n        }\n        if (!r[\"is_default\"].isNull())\n        {\n            isDefault_ = std::make_shared<bool>(r[\"is_default\"].as<bool>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 11 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            groupId_ = std::make_shared<uint64_t>(r[index].as<uint64_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            groupName_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 2;\n        if (!r[index].isNull())\n        {\n            createrId_ = std::make_shared<uint64_t>(r[index].as<uint64_t>());\n        }\n        index = offset + 3;\n        if (!r[index].isNull())\n        {\n            createTime_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 4;\n        if (!r[index].isNull())\n        {\n            inviting_ = std::make_shared<uint64_t>(r[index].as<uint64_t>());\n        }\n        index = offset + 5;\n        if (!r[index].isNull())\n        {\n            invitingUserId_ =\n                std::make_shared<uint64_t>(r[index].as<uint64_t>());\n        }\n        index = offset + 6;\n        if (!r[index].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 7;\n        if (!r[index].isNull())\n        {\n            uuu_ = std::make_shared<double>(r[index].as<double>());\n        }\n        index = offset + 8;\n        if (!r[index].isNull())\n        {\n            text_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 9;\n        if (!r[index].isNull())\n        {\n            avatar_ = std::make_shared<std::vector<char>>(\n                r[index].as<std::vector<char>>());\n        }\n        index = offset + 10;\n        if (!r[index].isNull())\n        {\n            isDefault_ = std::make_shared<bool>(r[index].as<bool>());\n        }\n    }\n}\n\nGroups::Groups(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 11)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            groupId_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[pMasqueradingVector[0]].asUInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            groupName_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            createrId_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[pMasqueradingVector[2]].asUInt64());\n        }\n    }\n    if (!pMasqueradingVector[3].empty() &&\n        pJson.isMember(pMasqueradingVector[3]))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[pMasqueradingVector[3]].isNull())\n        {\n            createTime_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[3]].asString());\n        }\n    }\n    if (!pMasqueradingVector[4].empty() &&\n        pJson.isMember(pMasqueradingVector[4]))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[pMasqueradingVector[4]].isNull())\n        {\n            inviting_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[pMasqueradingVector[4]].asUInt64());\n        }\n    }\n    if (!pMasqueradingVector[5].empty() &&\n        pJson.isMember(pMasqueradingVector[5]))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[pMasqueradingVector[5]].isNull())\n        {\n            invitingUserId_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[pMasqueradingVector[5]].asUInt64());\n        }\n    }\n    if (!pMasqueradingVector[6].empty() &&\n        pJson.isMember(pMasqueradingVector[6]))\n    {\n        dirtyFlag_[6] = true;\n        if (!pJson[pMasqueradingVector[6]].isNull())\n        {\n            avatarId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[6]].asString());\n        }\n    }\n    if (!pMasqueradingVector[7].empty() &&\n        pJson.isMember(pMasqueradingVector[7]))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[pMasqueradingVector[7]].isNull())\n        {\n            uuu_ = std::make_shared<double>(\n                pJson[pMasqueradingVector[7]].asDouble());\n        }\n    }\n    if (!pMasqueradingVector[8].empty() &&\n        pJson.isMember(pMasqueradingVector[8]))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[pMasqueradingVector[8]].isNull())\n        {\n            text_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[8]].asString());\n        }\n    }\n    if (!pMasqueradingVector[9].empty() &&\n        pJson.isMember(pMasqueradingVector[9]))\n    {\n        dirtyFlag_[9] = true;\n        if (!pJson[pMasqueradingVector[9]].isNull())\n        {\n            auto str = pJson[pMasqueradingVector[9]].asString();\n            avatar_ = std::make_shared<std::vector<char>>(\n                drogon::utils::base64DecodeToVector(str));\n        }\n    }\n    if (!pMasqueradingVector[10].empty() &&\n        pJson.isMember(pMasqueradingVector[10]))\n    {\n        dirtyFlag_[10] = true;\n        if (!pJson[pMasqueradingVector[10]].isNull())\n        {\n            isDefault_ =\n                std::make_shared<bool>(pJson[pMasqueradingVector[10]].asBool());\n        }\n    }\n}\n\nGroups::Groups(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"group_id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"group_id\"].isNull())\n        {\n            groupId_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[\"group_id\"].asUInt64());\n        }\n    }\n    if (pJson.isMember(\"group_name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"group_name\"].isNull())\n        {\n            groupName_ =\n                std::make_shared<std::string>(pJson[\"group_name\"].asString());\n        }\n    }\n    if (pJson.isMember(\"creater_id\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"creater_id\"].isNull())\n        {\n            createrId_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[\"creater_id\"].asUInt64());\n        }\n    }\n    if (pJson.isMember(\"create_time\"))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[\"create_time\"].isNull())\n        {\n            createTime_ =\n                std::make_shared<std::string>(pJson[\"create_time\"].asString());\n        }\n    }\n    if (pJson.isMember(\"inviting\"))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[\"inviting\"].isNull())\n        {\n            inviting_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[\"inviting\"].asUInt64());\n        }\n    }\n    if (pJson.isMember(\"inviting_user_id\"))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[\"inviting_user_id\"].isNull())\n        {\n            invitingUserId_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[\"inviting_user_id\"].asUInt64());\n        }\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        dirtyFlag_[6] = true;\n        if (!pJson[\"avatar_id\"].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(pJson[\"avatar_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"uuu\"))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[\"uuu\"].isNull())\n        {\n            uuu_ = std::make_shared<double>(pJson[\"uuu\"].asDouble());\n        }\n    }\n    if (pJson.isMember(\"text\"))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[\"text\"].isNull())\n        {\n            text_ = std::make_shared<std::string>(pJson[\"text\"].asString());\n        }\n    }\n    if (pJson.isMember(\"avatar\"))\n    {\n        dirtyFlag_[9] = true;\n        if (!pJson[\"avatar\"].isNull())\n        {\n            auto str = pJson[\"avatar\"].asString();\n            avatar_ = std::make_shared<std::vector<char>>(\n                drogon::utils::base64DecodeToVector(str));\n        }\n    }\n    if (pJson.isMember(\"is_default\"))\n    {\n        dirtyFlag_[10] = true;\n        if (!pJson[\"is_default\"].isNull())\n        {\n            isDefault_ = std::make_shared<bool>(pJson[\"is_default\"].asBool());\n        }\n    }\n}\n\nvoid Groups::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 11)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            groupId_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[pMasqueradingVector[0]].asUInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            groupName_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            createrId_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[pMasqueradingVector[2]].asUInt64());\n        }\n    }\n    if (!pMasqueradingVector[3].empty() &&\n        pJson.isMember(pMasqueradingVector[3]))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[pMasqueradingVector[3]].isNull())\n        {\n            createTime_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[3]].asString());\n        }\n    }\n    if (!pMasqueradingVector[4].empty() &&\n        pJson.isMember(pMasqueradingVector[4]))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[pMasqueradingVector[4]].isNull())\n        {\n            inviting_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[pMasqueradingVector[4]].asUInt64());\n        }\n    }\n    if (!pMasqueradingVector[5].empty() &&\n        pJson.isMember(pMasqueradingVector[5]))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[pMasqueradingVector[5]].isNull())\n        {\n            invitingUserId_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[pMasqueradingVector[5]].asUInt64());\n        }\n    }\n    if (!pMasqueradingVector[6].empty() &&\n        pJson.isMember(pMasqueradingVector[6]))\n    {\n        dirtyFlag_[6] = true;\n        if (!pJson[pMasqueradingVector[6]].isNull())\n        {\n            avatarId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[6]].asString());\n        }\n    }\n    if (!pMasqueradingVector[7].empty() &&\n        pJson.isMember(pMasqueradingVector[7]))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[pMasqueradingVector[7]].isNull())\n        {\n            uuu_ = std::make_shared<double>(\n                pJson[pMasqueradingVector[7]].asDouble());\n        }\n    }\n    if (!pMasqueradingVector[8].empty() &&\n        pJson.isMember(pMasqueradingVector[8]))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[pMasqueradingVector[8]].isNull())\n        {\n            text_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[8]].asString());\n        }\n    }\n    if (!pMasqueradingVector[9].empty() &&\n        pJson.isMember(pMasqueradingVector[9]))\n    {\n        dirtyFlag_[9] = true;\n        if (!pJson[pMasqueradingVector[9]].isNull())\n        {\n            auto str = pJson[pMasqueradingVector[9]].asString();\n            avatar_ = std::make_shared<std::vector<char>>(\n                drogon::utils::base64DecodeToVector(str));\n        }\n    }\n    if (!pMasqueradingVector[10].empty() &&\n        pJson.isMember(pMasqueradingVector[10]))\n    {\n        dirtyFlag_[10] = true;\n        if (!pJson[pMasqueradingVector[10]].isNull())\n        {\n            isDefault_ =\n                std::make_shared<bool>(pJson[pMasqueradingVector[10]].asBool());\n        }\n    }\n}\n\nvoid Groups::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"group_id\"))\n    {\n        if (!pJson[\"group_id\"].isNull())\n        {\n            groupId_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[\"group_id\"].asUInt64());\n        }\n    }\n    if (pJson.isMember(\"group_name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"group_name\"].isNull())\n        {\n            groupName_ =\n                std::make_shared<std::string>(pJson[\"group_name\"].asString());\n        }\n    }\n    if (pJson.isMember(\"creater_id\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"creater_id\"].isNull())\n        {\n            createrId_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[\"creater_id\"].asUInt64());\n        }\n    }\n    if (pJson.isMember(\"create_time\"))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[\"create_time\"].isNull())\n        {\n            createTime_ =\n                std::make_shared<std::string>(pJson[\"create_time\"].asString());\n        }\n    }\n    if (pJson.isMember(\"inviting\"))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[\"inviting\"].isNull())\n        {\n            inviting_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[\"inviting\"].asUInt64());\n        }\n    }\n    if (pJson.isMember(\"inviting_user_id\"))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[\"inviting_user_id\"].isNull())\n        {\n            invitingUserId_ = std::make_shared<uint64_t>(\n                (uint64_t)pJson[\"inviting_user_id\"].asUInt64());\n        }\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        dirtyFlag_[6] = true;\n        if (!pJson[\"avatar_id\"].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(pJson[\"avatar_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"uuu\"))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[\"uuu\"].isNull())\n        {\n            uuu_ = std::make_shared<double>(pJson[\"uuu\"].asDouble());\n        }\n    }\n    if (pJson.isMember(\"text\"))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[\"text\"].isNull())\n        {\n            text_ = std::make_shared<std::string>(pJson[\"text\"].asString());\n        }\n    }\n    if (pJson.isMember(\"avatar\"))\n    {\n        dirtyFlag_[9] = true;\n        if (!pJson[\"avatar\"].isNull())\n        {\n            auto str = pJson[\"avatar\"].asString();\n            avatar_ = std::make_shared<std::vector<char>>(\n                drogon::utils::base64DecodeToVector(str));\n        }\n    }\n    if (pJson.isMember(\"is_default\"))\n    {\n        dirtyFlag_[10] = true;\n        if (!pJson[\"is_default\"].isNull())\n        {\n            isDefault_ = std::make_shared<bool>(pJson[\"is_default\"].asBool());\n        }\n    }\n}\n\nconst uint64_t &Groups::getValueOfGroupId() const noexcept\n{\n    static const uint64_t defaultValue = uint64_t();\n    if (groupId_)\n        return *groupId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<uint64_t> &Groups::getGroupId() const noexcept\n{\n    return groupId_;\n}\n\nconst typename Groups::PrimaryKeyType &Groups::getPrimaryKey() const\n{\n    assert(groupId_);\n    return *groupId_;\n}\n\nconst std::string &Groups::getValueOfGroupName() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (groupName_)\n        return *groupName_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Groups::getGroupName() const noexcept\n{\n    return groupName_;\n}\n\nvoid Groups::setGroupName(const std::string &pGroupName) noexcept\n{\n    groupName_ = std::make_shared<std::string>(pGroupName);\n    dirtyFlag_[1] = true;\n}\n\nvoid Groups::setGroupName(std::string &&pGroupName) noexcept\n{\n    groupName_ = std::make_shared<std::string>(std::move(pGroupName));\n    dirtyFlag_[1] = true;\n}\n\nvoid Groups::setGroupNameToNull() noexcept\n{\n    groupName_.reset();\n    dirtyFlag_[1] = true;\n}\n\nconst uint64_t &Groups::getValueOfCreaterId() const noexcept\n{\n    static const uint64_t defaultValue = uint64_t();\n    if (createrId_)\n        return *createrId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<uint64_t> &Groups::getCreaterId() const noexcept\n{\n    return createrId_;\n}\n\nvoid Groups::setCreaterId(const uint64_t &pCreaterId) noexcept\n{\n    createrId_ = std::make_shared<uint64_t>(pCreaterId);\n    dirtyFlag_[2] = true;\n}\n\nvoid Groups::setCreaterIdToNull() noexcept\n{\n    createrId_.reset();\n    dirtyFlag_[2] = true;\n}\n\nconst std::string &Groups::getValueOfCreateTime() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (createTime_)\n        return *createTime_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Groups::getCreateTime() const noexcept\n{\n    return createTime_;\n}\n\nvoid Groups::setCreateTime(const std::string &pCreateTime) noexcept\n{\n    createTime_ = std::make_shared<std::string>(pCreateTime);\n    dirtyFlag_[3] = true;\n}\n\nvoid Groups::setCreateTime(std::string &&pCreateTime) noexcept\n{\n    createTime_ = std::make_shared<std::string>(std::move(pCreateTime));\n    dirtyFlag_[3] = true;\n}\n\nvoid Groups::setCreateTimeToNull() noexcept\n{\n    createTime_.reset();\n    dirtyFlag_[3] = true;\n}\n\nconst uint64_t &Groups::getValueOfInviting() const noexcept\n{\n    static const uint64_t defaultValue = uint64_t();\n    if (inviting_)\n        return *inviting_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<uint64_t> &Groups::getInviting() const noexcept\n{\n    return inviting_;\n}\n\nvoid Groups::setInviting(const uint64_t &pInviting) noexcept\n{\n    inviting_ = std::make_shared<uint64_t>(pInviting);\n    dirtyFlag_[4] = true;\n}\n\nvoid Groups::setInvitingToNull() noexcept\n{\n    inviting_.reset();\n    dirtyFlag_[4] = true;\n}\n\nconst uint64_t &Groups::getValueOfInvitingUserId() const noexcept\n{\n    static const uint64_t defaultValue = uint64_t();\n    if (invitingUserId_)\n        return *invitingUserId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<uint64_t> &Groups::getInvitingUserId() const noexcept\n{\n    return invitingUserId_;\n}\n\nvoid Groups::setInvitingUserId(const uint64_t &pInvitingUserId) noexcept\n{\n    invitingUserId_ = std::make_shared<uint64_t>(pInvitingUserId);\n    dirtyFlag_[5] = true;\n}\n\nvoid Groups::setInvitingUserIdToNull() noexcept\n{\n    invitingUserId_.reset();\n    dirtyFlag_[5] = true;\n}\n\nconst std::string &Groups::getValueOfAvatarId() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (avatarId_)\n        return *avatarId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Groups::getAvatarId() const noexcept\n{\n    return avatarId_;\n}\n\nvoid Groups::setAvatarId(const std::string &pAvatarId) noexcept\n{\n    avatarId_ = std::make_shared<std::string>(pAvatarId);\n    dirtyFlag_[6] = true;\n}\n\nvoid Groups::setAvatarId(std::string &&pAvatarId) noexcept\n{\n    avatarId_ = std::make_shared<std::string>(std::move(pAvatarId));\n    dirtyFlag_[6] = true;\n}\n\nvoid Groups::setAvatarIdToNull() noexcept\n{\n    avatarId_.reset();\n    dirtyFlag_[6] = true;\n}\n\nconst double &Groups::getValueOfUuu() const noexcept\n{\n    static const double defaultValue = double();\n    if (uuu_)\n        return *uuu_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<double> &Groups::getUuu() const noexcept\n{\n    return uuu_;\n}\n\nvoid Groups::setUuu(const double &pUuu) noexcept\n{\n    uuu_ = std::make_shared<double>(pUuu);\n    dirtyFlag_[7] = true;\n}\n\nvoid Groups::setUuuToNull() noexcept\n{\n    uuu_.reset();\n    dirtyFlag_[7] = true;\n}\n\nconst std::string &Groups::getValueOfText() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (text_)\n        return *text_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Groups::getText() const noexcept\n{\n    return text_;\n}\n\nvoid Groups::setText(const std::string &pText) noexcept\n{\n    text_ = std::make_shared<std::string>(pText);\n    dirtyFlag_[8] = true;\n}\n\nvoid Groups::setText(std::string &&pText) noexcept\n{\n    text_ = std::make_shared<std::string>(std::move(pText));\n    dirtyFlag_[8] = true;\n}\n\nvoid Groups::setTextToNull() noexcept\n{\n    text_.reset();\n    dirtyFlag_[8] = true;\n}\n\nconst std::vector<char> &Groups::getValueOfAvatar() const noexcept\n{\n    static const std::vector<char> defaultValue = std::vector<char>();\n    if (avatar_)\n        return *avatar_;\n    return defaultValue;\n}\n\nstd::string Groups::getValueOfAvatarAsString() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (avatar_)\n        return std::string(avatar_->data(), avatar_->size());\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::vector<char>> &Groups::getAvatar() const noexcept\n{\n    return avatar_;\n}\n\nvoid Groups::setAvatar(const std::vector<char> &pAvatar) noexcept\n{\n    avatar_ = std::make_shared<std::vector<char>>(pAvatar);\n    dirtyFlag_[9] = true;\n}\n\nvoid Groups::setAvatar(const std::string &pAvatar) noexcept\n{\n    avatar_ =\n        std::make_shared<std::vector<char>>(pAvatar.c_str(),\n                                            pAvatar.c_str() + pAvatar.length());\n    dirtyFlag_[9] = true;\n}\n\nvoid Groups::setAvatarToNull() noexcept\n{\n    avatar_.reset();\n    dirtyFlag_[9] = true;\n}\n\nconst bool &Groups::getValueOfIsDefault() const noexcept\n{\n    static const bool defaultValue = bool();\n    if (isDefault_)\n        return *isDefault_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<bool> &Groups::getIsDefault() const noexcept\n{\n    return isDefault_;\n}\n\nvoid Groups::setIsDefault(const bool &pIsDefault) noexcept\n{\n    isDefault_ = std::make_shared<bool>(pIsDefault);\n    dirtyFlag_[10] = true;\n}\n\nvoid Groups::setIsDefaultToNull() noexcept\n{\n    isDefault_.reset();\n    dirtyFlag_[10] = true;\n}\n\nvoid Groups::updateId(const uint64_t id)\n{\n    groupId_ = std::make_shared<uint64_t>(id);\n}\n\nconst std::vector<std::string> &Groups::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"group_name\",\n                                                    \"creater_id\",\n                                                    \"create_time\",\n                                                    \"inviting\",\n                                                    \"inviting_user_id\",\n                                                    \"avatar_id\",\n                                                    \"uuu\",\n                                                    \"text\",\n                                                    \"avatar\",\n                                                    \"is_default\"};\n    return inCols;\n}\n\nvoid Groups::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getGroupName())\n        {\n            binder << getValueOfGroupName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getCreaterId())\n        {\n            binder << getValueOfCreaterId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[3])\n    {\n        if (getCreateTime())\n        {\n            binder << getValueOfCreateTime();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[4])\n    {\n        if (getInviting())\n        {\n            binder << getValueOfInviting();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[5])\n    {\n        if (getInvitingUserId())\n        {\n            binder << getValueOfInvitingUserId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[6])\n    {\n        if (getAvatarId())\n        {\n            binder << getValueOfAvatarId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[7])\n    {\n        if (getUuu())\n        {\n            binder << getValueOfUuu();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[8])\n    {\n        if (getText())\n        {\n            binder << getValueOfText();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[9])\n    {\n        if (getAvatar())\n        {\n            binder << getValueOfAvatar();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[10])\n    {\n        if (getIsDefault())\n        {\n            binder << getValueOfIsDefault();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Groups::updateColumns() const\n{\n    std::vector<std::string> ret;\n    for (size_t i = 0; i < sizeof(dirtyFlag_); i++)\n    {\n        if (dirtyFlag_[i])\n        {\n            ret.push_back(getColumnName(i));\n        }\n    }\n    return ret;\n}\n\nvoid Groups::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getGroupName())\n        {\n            binder << getValueOfGroupName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getCreaterId())\n        {\n            binder << getValueOfCreaterId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[3])\n    {\n        if (getCreateTime())\n        {\n            binder << getValueOfCreateTime();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[4])\n    {\n        if (getInviting())\n        {\n            binder << getValueOfInviting();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[5])\n    {\n        if (getInvitingUserId())\n        {\n            binder << getValueOfInvitingUserId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[6])\n    {\n        if (getAvatarId())\n        {\n            binder << getValueOfAvatarId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[7])\n    {\n        if (getUuu())\n        {\n            binder << getValueOfUuu();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[8])\n    {\n        if (getText())\n        {\n            binder << getValueOfText();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[9])\n    {\n        if (getAvatar())\n        {\n            binder << getValueOfAvatar();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[10])\n    {\n        if (getIsDefault())\n        {\n            binder << getValueOfIsDefault();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Groups::toJson() const\n{\n    Json::Value ret;\n    if (getGroupId())\n    {\n        ret[\"group_id\"] = (Json::UInt64)getValueOfGroupId();\n    }\n    else\n    {\n        ret[\"group_id\"] = Json::Value();\n    }\n    if (getGroupName())\n    {\n        ret[\"group_name\"] = getValueOfGroupName();\n    }\n    else\n    {\n        ret[\"group_name\"] = Json::Value();\n    }\n    if (getCreaterId())\n    {\n        ret[\"creater_id\"] = (Json::UInt64)getValueOfCreaterId();\n    }\n    else\n    {\n        ret[\"creater_id\"] = Json::Value();\n    }\n    if (getCreateTime())\n    {\n        ret[\"create_time\"] = getValueOfCreateTime();\n    }\n    else\n    {\n        ret[\"create_time\"] = Json::Value();\n    }\n    if (getInviting())\n    {\n        ret[\"inviting\"] = (Json::UInt64)getValueOfInviting();\n    }\n    else\n    {\n        ret[\"inviting\"] = Json::Value();\n    }\n    if (getInvitingUserId())\n    {\n        ret[\"inviting_user_id\"] = (Json::UInt64)getValueOfInvitingUserId();\n    }\n    else\n    {\n        ret[\"inviting_user_id\"] = Json::Value();\n    }\n    if (getAvatarId())\n    {\n        ret[\"avatar_id\"] = getValueOfAvatarId();\n    }\n    else\n    {\n        ret[\"avatar_id\"] = Json::Value();\n    }\n    if (getUuu())\n    {\n        ret[\"uuu\"] = getValueOfUuu();\n    }\n    else\n    {\n        ret[\"uuu\"] = Json::Value();\n    }\n    if (getText())\n    {\n        ret[\"text\"] = getValueOfText();\n    }\n    else\n    {\n        ret[\"text\"] = Json::Value();\n    }\n    if (getAvatar())\n    {\n        ret[\"avatar\"] = drogon::utils::base64Encode(\n            (const unsigned char *)getAvatar()->data(), getAvatar()->size());\n    }\n    else\n    {\n        ret[\"avatar\"] = Json::Value();\n    }\n    if (getIsDefault())\n    {\n        ret[\"is_default\"] = getValueOfIsDefault();\n    }\n    else\n    {\n        ret[\"is_default\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Groups::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 11)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getGroupId())\n            {\n                ret[pMasqueradingVector[0]] = (Json::UInt64)getValueOfGroupId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getGroupName())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfGroupName();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (getCreaterId())\n            {\n                ret[pMasqueradingVector[2]] =\n                    (Json::UInt64)getValueOfCreaterId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[2]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[3].empty())\n        {\n            if (getCreateTime())\n            {\n                ret[pMasqueradingVector[3]] = getValueOfCreateTime();\n            }\n            else\n            {\n                ret[pMasqueradingVector[3]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[4].empty())\n        {\n            if (getInviting())\n            {\n                ret[pMasqueradingVector[4]] =\n                    (Json::UInt64)getValueOfInviting();\n            }\n            else\n            {\n                ret[pMasqueradingVector[4]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[5].empty())\n        {\n            if (getInvitingUserId())\n            {\n                ret[pMasqueradingVector[5]] =\n                    (Json::UInt64)getValueOfInvitingUserId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[5]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[6].empty())\n        {\n            if (getAvatarId())\n            {\n                ret[pMasqueradingVector[6]] = getValueOfAvatarId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[6]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[7].empty())\n        {\n            if (getUuu())\n            {\n                ret[pMasqueradingVector[7]] = getValueOfUuu();\n            }\n            else\n            {\n                ret[pMasqueradingVector[7]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[8].empty())\n        {\n            if (getText())\n            {\n                ret[pMasqueradingVector[8]] = getValueOfText();\n            }\n            else\n            {\n                ret[pMasqueradingVector[8]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[9].empty())\n        {\n            if (getAvatar())\n            {\n                ret[pMasqueradingVector[9]] = drogon::utils::base64Encode(\n                    (const unsigned char *)getAvatar()->data(),\n                    getAvatar()->size());\n            }\n            else\n            {\n                ret[pMasqueradingVector[9]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[10].empty())\n        {\n            if (getIsDefault())\n            {\n                ret[pMasqueradingVector[10]] = getValueOfIsDefault();\n            }\n            else\n            {\n                ret[pMasqueradingVector[10]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getGroupId())\n    {\n        ret[\"group_id\"] = (Json::UInt64)getValueOfGroupId();\n    }\n    else\n    {\n        ret[\"group_id\"] = Json::Value();\n    }\n    if (getGroupName())\n    {\n        ret[\"group_name\"] = getValueOfGroupName();\n    }\n    else\n    {\n        ret[\"group_name\"] = Json::Value();\n    }\n    if (getCreaterId())\n    {\n        ret[\"creater_id\"] = (Json::UInt64)getValueOfCreaterId();\n    }\n    else\n    {\n        ret[\"creater_id\"] = Json::Value();\n    }\n    if (getCreateTime())\n    {\n        ret[\"create_time\"] = getValueOfCreateTime();\n    }\n    else\n    {\n        ret[\"create_time\"] = Json::Value();\n    }\n    if (getInviting())\n    {\n        ret[\"inviting\"] = (Json::UInt64)getValueOfInviting();\n    }\n    else\n    {\n        ret[\"inviting\"] = Json::Value();\n    }\n    if (getInvitingUserId())\n    {\n        ret[\"inviting_user_id\"] = (Json::UInt64)getValueOfInvitingUserId();\n    }\n    else\n    {\n        ret[\"inviting_user_id\"] = Json::Value();\n    }\n    if (getAvatarId())\n    {\n        ret[\"avatar_id\"] = getValueOfAvatarId();\n    }\n    else\n    {\n        ret[\"avatar_id\"] = Json::Value();\n    }\n    if (getUuu())\n    {\n        ret[\"uuu\"] = getValueOfUuu();\n    }\n    else\n    {\n        ret[\"uuu\"] = Json::Value();\n    }\n    if (getText())\n    {\n        ret[\"text\"] = getValueOfText();\n    }\n    else\n    {\n        ret[\"text\"] = Json::Value();\n    }\n    if (getAvatar())\n    {\n        ret[\"avatar\"] = drogon::utils::base64Encode(\n            (const unsigned char *)getAvatar()->data(), getAvatar()->size());\n    }\n    else\n    {\n        ret[\"avatar\"] = Json::Value();\n    }\n    if (getIsDefault())\n    {\n        ret[\"is_default\"] = getValueOfIsDefault();\n    }\n    else\n    {\n        ret[\"is_default\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Groups::validateJsonForCreation(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"group_id\"))\n    {\n        if (!validJsonOfField(0, \"group_id\", pJson[\"group_id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"group_name\"))\n    {\n        if (!validJsonOfField(1, \"group_name\", pJson[\"group_name\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"creater_id\"))\n    {\n        if (!validJsonOfField(2, \"creater_id\", pJson[\"creater_id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"create_time\"))\n    {\n        if (!validJsonOfField(\n                3, \"create_time\", pJson[\"create_time\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"inviting\"))\n    {\n        if (!validJsonOfField(4, \"inviting\", pJson[\"inviting\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"inviting_user_id\"))\n    {\n        if (!validJsonOfField(\n                5, \"inviting_user_id\", pJson[\"inviting_user_id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        if (!validJsonOfField(6, \"avatar_id\", pJson[\"avatar_id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"uuu\"))\n    {\n        if (!validJsonOfField(7, \"uuu\", pJson[\"uuu\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"text\"))\n    {\n        if (!validJsonOfField(8, \"text\", pJson[\"text\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"avatar\"))\n    {\n        if (!validJsonOfField(9, \"avatar\", pJson[\"avatar\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"is_default\"))\n    {\n        if (!validJsonOfField(10, \"is_default\", pJson[\"is_default\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Groups::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 11)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    if (!pMasqueradingVector[0].empty())\n    {\n        if (pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  true))\n                return false;\n        }\n    }\n    if (!pMasqueradingVector[1].empty())\n    {\n        if (pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  true))\n                return false;\n        }\n    }\n    if (!pMasqueradingVector[2].empty())\n    {\n        if (pJson.isMember(pMasqueradingVector[2]))\n        {\n            if (!validJsonOfField(2,\n                                  pMasqueradingVector[2],\n                                  pJson[pMasqueradingVector[2]],\n                                  err,\n                                  true))\n                return false;\n        }\n    }\n    if (!pMasqueradingVector[3].empty())\n    {\n        if (pJson.isMember(pMasqueradingVector[3]))\n        {\n            if (!validJsonOfField(3,\n                                  pMasqueradingVector[3],\n                                  pJson[pMasqueradingVector[3]],\n                                  err,\n                                  true))\n                return false;\n        }\n    }\n    if (!pMasqueradingVector[4].empty())\n    {\n        if (pJson.isMember(pMasqueradingVector[4]))\n        {\n            if (!validJsonOfField(4,\n                                  pMasqueradingVector[4],\n                                  pJson[pMasqueradingVector[4]],\n                                  err,\n                                  true))\n                return false;\n        }\n    }\n    if (!pMasqueradingVector[5].empty())\n    {\n        if (pJson.isMember(pMasqueradingVector[5]))\n        {\n            if (!validJsonOfField(5,\n                                  pMasqueradingVector[5],\n                                  pJson[pMasqueradingVector[5]],\n                                  err,\n                                  true))\n                return false;\n        }\n    }\n    if (!pMasqueradingVector[6].empty())\n    {\n        if (pJson.isMember(pMasqueradingVector[6]))\n        {\n            if (!validJsonOfField(6,\n                                  pMasqueradingVector[6],\n                                  pJson[pMasqueradingVector[6]],\n                                  err,\n                                  true))\n                return false;\n        }\n    }\n    if (!pMasqueradingVector[7].empty())\n    {\n        if (pJson.isMember(pMasqueradingVector[7]))\n        {\n            if (!validJsonOfField(7,\n                                  pMasqueradingVector[7],\n                                  pJson[pMasqueradingVector[7]],\n                                  err,\n                                  true))\n                return false;\n        }\n    }\n    if (!pMasqueradingVector[8].empty())\n    {\n        if (pJson.isMember(pMasqueradingVector[8]))\n        {\n            if (!validJsonOfField(8,\n                                  pMasqueradingVector[8],\n                                  pJson[pMasqueradingVector[8]],\n                                  err,\n                                  true))\n                return false;\n        }\n    }\n    if (!pMasqueradingVector[9].empty())\n    {\n        if (pJson.isMember(pMasqueradingVector[9]))\n        {\n            if (!validJsonOfField(9,\n                                  pMasqueradingVector[9],\n                                  pJson[pMasqueradingVector[9]],\n                                  err,\n                                  true))\n                return false;\n        }\n    }\n    if (!pMasqueradingVector[10].empty())\n    {\n        if (pJson.isMember(pMasqueradingVector[10]))\n        {\n            if (!validJsonOfField(10,\n                                  pMasqueradingVector[10],\n                                  pJson[pMasqueradingVector[10]],\n                                  err,\n                                  true))\n                return false;\n        }\n    }\n    return true;\n}\n\nbool Groups::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"group_id\"))\n    {\n        if (!validJsonOfField(0, \"group_id\", pJson[\"group_id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"group_name\"))\n    {\n        if (!validJsonOfField(1, \"group_name\", pJson[\"group_name\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"creater_id\"))\n    {\n        if (!validJsonOfField(2, \"creater_id\", pJson[\"creater_id\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"create_time\"))\n    {\n        if (!validJsonOfField(\n                3, \"create_time\", pJson[\"create_time\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"inviting\"))\n    {\n        if (!validJsonOfField(4, \"inviting\", pJson[\"inviting\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"inviting_user_id\"))\n    {\n        if (!validJsonOfField(\n                5, \"inviting_user_id\", pJson[\"inviting_user_id\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        if (!validJsonOfField(6, \"avatar_id\", pJson[\"avatar_id\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"uuu\"))\n    {\n        if (!validJsonOfField(7, \"uuu\", pJson[\"uuu\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"text\"))\n    {\n        if (!validJsonOfField(8, \"text\", pJson[\"text\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"avatar\"))\n    {\n        if (!validJsonOfField(9, \"avatar\", pJson[\"avatar\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"is_default\"))\n    {\n        if (!validJsonOfField(\n                10, \"is_default\", pJson[\"is_default\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Groups::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 11)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!validJsonOfField(0,\n                              pMasqueradingVector[0],\n                              pJson[pMasqueradingVector[0]],\n                              err,\n                              false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        if (!validJsonOfField(1,\n                              pMasqueradingVector[1],\n                              pJson[pMasqueradingVector[1]],\n                              err,\n                              false))\n            return false;\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        if (!validJsonOfField(2,\n                              pMasqueradingVector[2],\n                              pJson[pMasqueradingVector[2]],\n                              err,\n                              false))\n            return false;\n    }\n    if (!pMasqueradingVector[3].empty() &&\n        pJson.isMember(pMasqueradingVector[3]))\n    {\n        if (!validJsonOfField(3,\n                              pMasqueradingVector[3],\n                              pJson[pMasqueradingVector[3]],\n                              err,\n                              false))\n            return false;\n    }\n    if (!pMasqueradingVector[4].empty() &&\n        pJson.isMember(pMasqueradingVector[4]))\n    {\n        if (!validJsonOfField(4,\n                              pMasqueradingVector[4],\n                              pJson[pMasqueradingVector[4]],\n                              err,\n                              false))\n            return false;\n    }\n    if (!pMasqueradingVector[5].empty() &&\n        pJson.isMember(pMasqueradingVector[5]))\n    {\n        if (!validJsonOfField(5,\n                              pMasqueradingVector[5],\n                              pJson[pMasqueradingVector[5]],\n                              err,\n                              false))\n            return false;\n    }\n    if (!pMasqueradingVector[6].empty() &&\n        pJson.isMember(pMasqueradingVector[6]))\n    {\n        if (!validJsonOfField(6,\n                              pMasqueradingVector[6],\n                              pJson[pMasqueradingVector[6]],\n                              err,\n                              false))\n            return false;\n    }\n    if (!pMasqueradingVector[7].empty() &&\n        pJson.isMember(pMasqueradingVector[7]))\n    {\n        if (!validJsonOfField(7,\n                              pMasqueradingVector[7],\n                              pJson[pMasqueradingVector[7]],\n                              err,\n                              false))\n            return false;\n    }\n    if (!pMasqueradingVector[8].empty() &&\n        pJson.isMember(pMasqueradingVector[8]))\n    {\n        if (!validJsonOfField(8,\n                              pMasqueradingVector[8],\n                              pJson[pMasqueradingVector[8]],\n                              err,\n                              false))\n            return false;\n    }\n    if (!pMasqueradingVector[9].empty() &&\n        pJson.isMember(pMasqueradingVector[9]))\n    {\n        if (!validJsonOfField(9,\n                              pMasqueradingVector[9],\n                              pJson[pMasqueradingVector[9]],\n                              err,\n                              false))\n            return false;\n    }\n    if (!pMasqueradingVector[10].empty() &&\n        pJson.isMember(pMasqueradingVector[10]))\n    {\n        if (!validJsonOfField(10,\n                              pMasqueradingVector[10],\n                              pJson[pMasqueradingVector[10]],\n                              err,\n                              false))\n            return false;\n    }\n    return true;\n}\n\nbool Groups::validJsonOfField(size_t index,\n                              const std::string &fieldName,\n                              const Json::Value &pJson,\n                              std::string &err,\n                              bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (isForCreation)\n            {\n                err = \"The automatic primary key cannot be set\";\n                return false;\n            }\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isUInt64())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 2:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isUInt64())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 3:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 4:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isUInt64())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 5:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isUInt64())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 6:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 7:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isNumeric())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 8:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 9:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 10:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isBool())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n\n        default:\n            err = \"Internal error in the server\";\n            return false;\n            break;\n    }\n    return true;\n}\n"
  },
  {
    "path": "orm_lib/src/sqlite3_impl/test/Groups.h",
    "content": "/**\n *\n *  Groups.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nusing namespace drogon::orm;\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace sqlite3\n{\nclass Groups\n{\n  public:\n    struct Cols\n    {\n        static const std::string _group_id;\n        static const std::string _group_name;\n        static const std::string _creater_id;\n        static const std::string _create_time;\n        static const std::string _inviting;\n        static const std::string _inviting_user_id;\n        static const std::string _avatar_id;\n        static const std::string _uuu;\n        static const std::string _text;\n        static const std::string _avatar;\n        static const std::string _is_default;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = uint64_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Groups(const Row &r, const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Groups(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Groups(const Json::Value &pJson,\n           const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Groups() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column group_id  */\n    /// Get the value of the column group_id, returns the default value if the\n    /// column is null\n    const uint64_t &getValueOfGroupId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<uint64_t> &getGroupId() const noexcept;\n\n    /**  For column group_name  */\n    /// Get the value of the column group_name, returns the default value if the\n    /// column is null\n    const std::string &getValueOfGroupName() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getGroupName() const noexcept;\n    /// Set the value of the column group_name\n    void setGroupName(const std::string &pGroupName) noexcept;\n    void setGroupName(std::string &&pGroupName) noexcept;\n    void setGroupNameToNull() noexcept;\n\n    /**  For column creater_id  */\n    /// Get the value of the column creater_id, returns the default value if the\n    /// column is null\n    const uint64_t &getValueOfCreaterId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<uint64_t> &getCreaterId() const noexcept;\n    /// Set the value of the column creater_id\n    void setCreaterId(const uint64_t &pCreaterId) noexcept;\n    void setCreaterIdToNull() noexcept;\n\n    /**  For column create_time  */\n    /// Get the value of the column create_time, returns the default value if\n    /// the column is null\n    const std::string &getValueOfCreateTime() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getCreateTime() const noexcept;\n    /// Set the value of the column create_time\n    void setCreateTime(const std::string &pCreateTime) noexcept;\n    void setCreateTime(std::string &&pCreateTime) noexcept;\n    void setCreateTimeToNull() noexcept;\n\n    /**  For column inviting  */\n    /// Get the value of the column inviting, returns the default value if the\n    /// column is null\n    const uint64_t &getValueOfInviting() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<uint64_t> &getInviting() const noexcept;\n    /// Set the value of the column inviting\n    void setInviting(const uint64_t &pInviting) noexcept;\n    void setInvitingToNull() noexcept;\n\n    /**  For column inviting_user_id  */\n    /// Get the value of the column inviting_user_id, returns the default value\n    /// if the column is null\n    const uint64_t &getValueOfInvitingUserId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<uint64_t> &getInvitingUserId() const noexcept;\n    /// Set the value of the column inviting_user_id\n    void setInvitingUserId(const uint64_t &pInvitingUserId) noexcept;\n    void setInvitingUserIdToNull() noexcept;\n\n    /**  For column avatar_id  */\n    /// Get the value of the column avatar_id, returns the default value if the\n    /// column is null\n    const std::string &getValueOfAvatarId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getAvatarId() const noexcept;\n    /// Set the value of the column avatar_id\n    void setAvatarId(const std::string &pAvatarId) noexcept;\n    void setAvatarId(std::string &&pAvatarId) noexcept;\n    void setAvatarIdToNull() noexcept;\n\n    /**  For column uuu  */\n    /// Get the value of the column uuu, returns the default value if the column\n    /// is null\n    const double &getValueOfUuu() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<double> &getUuu() const noexcept;\n    /// Set the value of the column uuu\n    void setUuu(const double &pUuu) noexcept;\n    void setUuuToNull() noexcept;\n\n    /**  For column text  */\n    /// Get the value of the column text, returns the default value if the\n    /// column is null\n    const std::string &getValueOfText() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getText() const noexcept;\n    /// Set the value of the column text\n    void setText(const std::string &pText) noexcept;\n    void setText(std::string &&pText) noexcept;\n    void setTextToNull() noexcept;\n\n    /**  For column avatar  */\n    /// Get the value of the column avatar, returns the default value if the\n    /// column is null\n    const std::vector<char> &getValueOfAvatar() const noexcept;\n    /// Return the column value by std::string with binary data\n    std::string getValueOfAvatarAsString() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::vector<char>> &getAvatar() const noexcept;\n    /// Set the value of the column avatar\n    void setAvatar(const std::vector<char> &pAvatar) noexcept;\n    void setAvatar(const std::string &pAvatar) noexcept;\n    void setAvatarToNull() noexcept;\n\n    /**  For column is_default  */\n    /// Get the value of the column is_default, returns the default value if the\n    /// column is null\n    const bool &getValueOfIsDefault() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<bool> &getIsDefault() const noexcept;\n    /// Set the value of the column is_default\n    void setIsDefault(const bool &pIsDefault) noexcept;\n    void setIsDefaultToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 11;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n  private:\n    friend Mapper<Groups>;\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<uint64_t> groupId_;\n    std::shared_ptr<std::string> groupName_;\n    std::shared_ptr<uint64_t> createrId_;\n    std::shared_ptr<std::string> createTime_;\n    std::shared_ptr<uint64_t> inviting_;\n    std::shared_ptr<uint64_t> invitingUserId_;\n    std::shared_ptr<std::string> avatarId_;\n    std::shared_ptr<double> uuu_;\n    std::shared_ptr<std::string> text_;\n    std::shared_ptr<std::vector<char>> avatar_;\n    std::shared_ptr<bool> isDefault_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[11] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where group_id = ?\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where group_id = ?\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        sql += \"group_id,\";\n        ++parametersCount;\n        if (dirtyFlag_[1])\n        {\n            sql += \"group_name,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[2])\n        {\n            sql += \"creater_id,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[3])\n        {\n            sql += \"create_time,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[4])\n        {\n            sql += \"inviting,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[5])\n        {\n            sql += \"inviting_user_id,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[6])\n        {\n            sql += \"avatar_id,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[7])\n        {\n            sql += \"uuu,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[8])\n        {\n            sql += \"text,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[9])\n        {\n            sql += \"avatar,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[10])\n        {\n            sql += \"is_default,\";\n            ++parametersCount;\n        }\n        needSelection = true;\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        sql += \"default,\";\n        if (dirtyFlag_[1])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[2])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[3])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[4])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[5])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[6])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[7])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[8])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[9])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[10])\n        {\n            sql.append(\"?,\");\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        sql.append(1, ')');\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace sqlite3\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/src/sqlite3_impl/test/test1.cc",
    "content": "#include \"Groups.h\"\n#include <drogon/drogon.h>\n#include <drogon/orm/DbClient.h>\n#include <iostream>\n#include <trantor/utils/Logger.h>\n#ifndef _WIN32\n#include <unistd.h>\n#endif\n#include <thread>\n#include <chrono>\n\nusing namespace std::chrono_literals;\nusing namespace drogon::orm;\n\nint main()\n{\n    trantor::Logger::setLogLevel(trantor::Logger::kTrace);\n    auto clientPtr = DbClient::newSqlite3Client(\"filename=test.db\", 1);\n    std::this_thread::sleep_for(1s);\n\n    LOG_DEBUG << \"start!\";\n    // *clientPtr << \"Drop table groups;\" << Mode::Blocking >>\n    //     [](const Result &r) {\n    //         LOG_DEBUG << \"dropped\";\n    //     } >>\n    //     [](const DrogonDbException &e) {\n    //         std::cout << e.base().what() << std::endl;\n    //     };\n    // ;\n    *clientPtr << \"CREATE TABLE IF NOT EXISTS GROUPS (GROUP_ID INTEGER PRIMARY \"\n                  \"KEY autoincrement,\"\n                  \"GROUP_NAME TEXT,\"\n                  \"CREATER_ID INTEGER,\"\n                  \"CREATE_TIME TEXT,\"\n                  \"INVITING INTEGER,\"\n                  \"INVITING_USER_ID INTEGER,\"\n                  \"AVATAR_ID TEXT, uuu double, text VARCHAR(255),avatar \"\n                  \"blob,is_default bool)\"\n               << Mode::Blocking >>\n        [](const Result &r) { LOG_DEBUG << \"created\"; } >>\n        [](const DrogonDbException &e) {\n            std::cout << e.base().what() << std::endl;\n        };\n    *clientPtr << \"insert into GROUPS (group_name) values(?)\"\n               << \"test_group\" << Mode::Blocking >>\n        [](const Result &r) {\n            LOG_DEBUG << \"inserted:\" << r.affectedRows();\n            LOG_DEBUG << \"id:\" << r.insertId();\n        } >>\n        [](const DrogonDbException &e) {\n            std::cout << e.base().what() << std::endl;\n        };\n    *clientPtr << \"insert into GROUPS (group_name) values(?)\"\n               << \"test_group\" << Mode::Blocking >>\n        [](const Result &r) {\n            LOG_DEBUG << \"inserted:\" << r.affectedRows();\n            LOG_DEBUG << \"id:\" << r.insertId();\n        } >>\n        [](const DrogonDbException &e) {\n            std::cout << e.base().what() << std::endl;\n        };\n    *clientPtr << \"select * from GROUPS \" >> [](const Result &r) {\n        LOG_DEBUG << \"affected rows:\" << r.affectedRows();\n        LOG_DEBUG << \"select \" << r.size() << \" rows\";\n        LOG_DEBUG << \"id:\" << r.insertId();\n        for (auto const &row : r)\n        {\n            LOG_DEBUG << \"group_id:\" << row[\"group_id\"].as<size_t>();\n        }\n    } >> [](const DrogonDbException &e) {\n        std::cout << e.base().what() << std::endl;\n    };\n    {\n        auto trans = clientPtr->newTransaction([](bool success) {\n            LOG_DEBUG << (success ? \"commit success!\" : \"commit failed!\");\n        });\n        Mapper<drogon_model::sqlite3::Groups> mapper(trans);\n        mapper.limit(2).offset(1).findAll(\n            [trans](const std::vector<drogon_model::sqlite3::Groups> &v) {\n                Mapper<drogon_model::sqlite3::Groups> mapper(trans);\n                for (auto group : v)\n                {\n                    LOG_DEBUG << \"group_id=\" << group.getValueOfGroupId();\n                    std::cout << group.toJson() << std::endl;\n                    std::cout << \"avatar:\" << group.getValueOfAvatarAsString()\n                              << std::endl;\n                    group.setAvatarId(\"xixi\");\n                    mapper.update(\n                        group,\n                        [](const size_t count) {\n                            LOG_DEBUG << \"update \" << count << \" rows\";\n                        },\n                        [](const DrogonDbException &e) {\n                            LOG_ERROR << e.base().what();\n                        });\n                }\n            },\n            [](const DrogonDbException &e) { LOG_ERROR << e.base().what(); });\n        drogon_model::sqlite3::Groups group;\n        group.setAvatar(\"hahahaha,xixixixix\");\n        try\n        {\n            mapper.insert(group);\n        }\n        catch (const DrogonDbException &e)\n        {\n            std::cerr << e.base().what() << std::endl;\n        }\n        *clientPtr << \"select is_default from groups\" >> [](const Result &r) {\n            for (auto row : r)\n            {\n                std::cout << \"is_default: \"\n                          << (row[0].isNull() ? \"is null, \" : \"is not null, \")\n                          << \"bool value:\" << row[0].as<bool>() << \"(\"\n                          << row[0].as<std::string>() << \")\" << std::endl;\n            }\n        } >> [](const DrogonDbException &e) {\n            std::cerr << e.base().what() << std::endl;\n        };\n    }\n    getchar();\n}\n"
  },
  {
    "path": "orm_lib/tests/CMakeLists.txt",
    "content": "link_libraries(${PROJECT_NAME})\nif (WIN32)\n    link_libraries(iphlpapi)\nendif (WIN32)\n\nadd_executable(db_test\n        db_test.cc\n        postgresql/Users.cc\n\t\tpostgresql/Wallets.cc\n        postgresql/Blog.cc\n\t\tpostgresql/Category.cc\n        postgresql/BlogTag.cc\n        postgresql/Tag.cc\n        mysql/Users.cc\n\t\tmysql/Wallets.cc\n        mysql/Blog.cc\n\t\tmysql/Category.cc\n        mysql/BlogTag.cc\n        mysql/Tag.cc\n        sqlite3/Users.cc\n\t\tsqlite3/Wallets.cc\n        sqlite3/Blog.cc\n\t\tsqlite3/Category.cc\n        sqlite3/BlogTag.cc\n        sqlite3/Tag.cc\n        )\n\nif (WIN32)\n    target_compile_options(db_test PRIVATE /bigobj)\nendif (WIN32)\n\nadd_executable(pipeline_test\n        pipeline_test.cpp\n        )\n\nadd_executable(db_listener_test\n        db_listener_test.cc\n        )\n\nadd_executable(conn_options_test\n\t\tconn_options_test.cc\n        )\n\nadd_executable(db_api_test\n\t\tdb_api_test.cc\n        )\n\nset_property(TARGET db_test PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})\nset_property(TARGET db_test PROPERTY CXX_STANDARD_REQUIRED ON)\nset_property(TARGET db_test PROPERTY CXX_EXTENSIONS OFF)\n\nset_property(TARGET pipeline_test PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})\nset_property(TARGET pipeline_test PROPERTY CXX_STANDARD_REQUIRED ON)\nset_property(TARGET pipeline_test PROPERTY CXX_EXTENSIONS OFF)\n\nset_property(TARGET db_listener_test PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})\nset_property(TARGET db_listener_test PROPERTY CXX_STANDARD_REQUIRED ON)\nset_property(TARGET db_listener_test PROPERTY CXX_EXTENSIONS OFF)\n\nset_property(TARGET conn_options_test PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})\nset_property(TARGET conn_options_test PROPERTY CXX_STANDARD_REQUIRED ON)\nset_property(TARGET conn_options_test PROPERTY CXX_EXTENSIONS OFF)\n\nset_property(TARGET db_api_test PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})\nset_property(TARGET db_api_test PROPERTY CXX_STANDARD_REQUIRED ON)\nset_property(TARGET db_api_test PROPERTY CXX_EXTENSIONS OFF)\n"
  },
  {
    "path": "orm_lib/tests/conn_options_test.cc",
    "content": "#define DROGON_TEST_MAIN\n#include <drogon/drogon_test.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/config.h>\n\nusing namespace drogon;\nusing namespace trantor;\n\n#if USE_POSTGRESQL\nDROGON_TEST(ConnOptionsTest)\n{\n    auto clientPtr = app().getDbClient();\n\n    // Run a query that will take longer than the statement_timeout\n    clientPtr->execSqlAsync(\n        \"select pg_sleep(5);\",\n        [TEST_CTX](const drogon::orm::Result &r) {\n            LOG_INFO << \"select pg_sleep(5);\";\n            FAULT(\"Statement should be canceled due to timeout.\");\n        },\n        [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n            LOG_INFO << \"select pg_sleep(5); error(expected):\"\n                     << e.base().what();\n            SUCCESS();\n        });\n\n    // Run sql codes to hold a lock for sometime\n    clientPtr->execSqlAsync(\n        R\"(\nDO $$\nBEGIN\n    perform pg_advisory_xact_lock(12345);\n    perform pg_sleep(2);\nEND $$;)\",\n        [TEST_CTX](const drogon::orm::Result &r) {\n            LOG_INFO << \"pg_advisory_xact_lock transaction1 finished;\";\n            SUCCESS();\n        },\n        [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n            FAULT(\"query failed: \", e.base().what());\n        });\n\n    // Run sql codes that requires the same lock, and will be canceled due to\n    // lock_timeout\n    clientPtr->execSqlAsync(\n        R\"(\nDO $$\nBEGIN\n    perform pg_sleep(0.5);\n    perform pg_advisory_xact_lock(12345);\nEND $$;)\",\n        [TEST_CTX](const drogon::orm::Result &r) {\n            LOG_INFO << \"pg_advisory_xact_lock transaction2 finished;\";\n            FAULT(\"Statement should be canceled due to timeout.\");\n        },\n        [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n            LOG_INFO << \"pg_advisory_xact_lock() error(expected):\"\n                     << e.base().what();\n            SUCCESS();\n        });\n}\n\n#endif\n\nint main(int argc, char **argv)\n{\n    std::promise<void> p1;\n    std::future<void> f1 = p1.get_future();\n    app().setThreadNum(1);\n#if USE_POSTGRESQL\n    app().addDbClient(orm::PostgresConfig{\n        \"127.0.0.1\",  // host\n        5432,         // port\n        \"postgres\",   // dbname\n        \"postgres\",   // username\n        \"12345\",      // password\n        4,            // connectionNum\n        \"default\",    // name\n        false,        // isFast\n        \"\",           // charset\n        10,           // timeout\n        false,        // autobatch\n        {\n            {\"statement_timeout\", \"3s\"},\n            {\"lock_timeout\", \"0.5s\"},\n        }  // connectOptions\n    });\n#endif\n    std::thread thr([&]() {\n        app().getLoop()->queueInLoop([&]() { p1.set_value(); });\n        app().run();\n    });\n\n    f1.get();\n    int testStatus = test::run(argc, argv);\n    app().getLoop()->queueInLoop([]() { app().quit(); });\n    thr.join();\n    return testStatus;\n}\n"
  },
  {
    "path": "orm_lib/tests/db_api_test.cc",
    "content": "#define DROGON_TEST_MAIN\n#include <drogon/drogon_test.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/config.h>\n\nusing namespace drogon;\nusing namespace trantor;\n\nDROGON_TEST(DbApiTest)\n{\n#if USE_POSTGRESQL\n    {\n        auto client = app().getDbClient(\"pg_non_fast\");\n        CHECK(client != nullptr);\n        client->closeAll();\n        drogon::app().getLoop()->runInLoop([TEST_CTX]() {\n            auto client = app().getFastDbClient(\"pg_fast\");\n            CHECK(client != nullptr);\n            client->closeAll();\n        });\n        drogon::app().getIOLoop(0)->runInLoop([TEST_CTX]() {\n            auto client = app().getFastDbClient(\"pg_fast\");\n            CHECK(client != nullptr);\n            client->closeAll();\n        });\n    }\n#endif\n\n#if USE_MYSQL\n    {\n        auto client = app().getDbClient(\"mysql_non_fast\");\n        CHECK(client != nullptr);\n        client->closeAll();\n        drogon::app().getLoop()->runInLoop([TEST_CTX]() {\n            auto client = app().getFastDbClient(\"mysql_fast\");\n            CHECK(client != nullptr);\n            client->closeAll();\n        });\n        drogon::app().getIOLoop(0)->runInLoop([TEST_CTX]() {\n            auto client = app().getFastDbClient(\"mysql_fast\");\n            CHECK(client != nullptr);\n            client->closeAll();\n        });\n    }\n#endif\n\n#if USE_SQLITE3\n    {\n        auto client = app().getDbClient(\"sqlite3_non_fast\");\n        CHECK(client != nullptr);\n        client->closeAll();\n    }\n#endif\n\n    app().getLoop()->runAfter(5, [TEST_CTX]() {});  // wait for some time\n}\n\nconst std::string_view pg_non_fast_config = R\"({\n    \"name\": \"pg_non_fast\",\n    \"rdbms\": \"postgresql\",\n    \"host\": \"127.0.0.1\",\n    \"port\": 5432,\n    \"dbname\": \"test\",\n    \"user\": \"postgres\",\n    \"passwd\": \"123456\",\n    \"is_fast\": false\n})\";\n\nconst std::string_view pg_fast_config = R\"({\n    \"name\": \"pg_fast\",\n    \"rdbms\": \"postgresql\",\n    \"host\": \"127.0.0.1\",\n    \"port\": 5432,\n    \"dbname\": \"test\",\n    \"user\": \"postgres\",\n    \"passwd\": \"123456\",\n    \"is_fast\": true\n})\";\n\nconst std::string_view mysql_non_fast_config = R\"({\n    \"name\": \"mysql_non_fast\",\n    \"rdbms\": \"mysql\",\n    \"host\": \"127.0.0.1\",\n    \"port\": 3306,\n    \"dbname\": \"test\",\n    \"user\": \"root\",\n    \"passwd\": \"123456\",\n    \"is_fast\": false\n})\";\n\nconst std::string_view mysql_fast_config = R\"({\n    \"name\": \"mysql_fast\",\n    \"rdbms\": \"mysql\",\n    \"host\": \"127.0.0.1\",\n    \"port\": 3306,\n    \"dbname\": \"test\",\n    \"user\": \"root\",\n    \"passwd\": \"123456\",\n    \"is_fast\": true\n})\";\n\nconst std::string_view sqlite3_non_fast_config = R\"({\n    \"name\": \"sqlite3_non_fast\",\n    \"rdbms\": \"sqlite3\",\n    \"filename\": \"test.db\",\n    \"is_fast\": false\n})\";\n\nbool parseJson(std::string_view str, Json::Value *root, Json::String *errs)\n{\n    static Json::CharReaderBuilder &builder =\n        []() -> Json::CharReaderBuilder & {\n        static Json::CharReaderBuilder builder;\n        builder[\"collectComments\"] = false;\n        return builder;\n    }();\n    std::unique_ptr<Json::CharReader> jsonReader(builder.newCharReader());\n    return jsonReader->parse(str.data(), str.data() + str.size(), root, errs);\n}\n\nint main(int argc, char **argv)\n{\n    Json::Value config;\n    config[\"app\"][\"number_of_threads\"] = 1;\n    config[\"db_clients\"] = Json::arrayValue;\n\n    std::string err;\n    Json::Value cfg;\n#if USE_POSTGRESQL\n    cfg = Json::objectValue;\n    if (!parseJson(pg_non_fast_config, &cfg, &err))\n    {\n        LOG_ERROR << \"Failed to parse json: \" << err;\n        return 1;\n    }\n    config[\"db_clients\"].append(cfg);\n    cfg = Json::objectValue;\n    if (!parseJson(pg_fast_config.data(), &cfg, &err))\n    {\n        LOG_ERROR << \"Failed to parse json: \" << err;\n        return 1;\n    }\n    config[\"db_clients\"].append(cfg);\n#endif\n\n#if USE_MYSQL\n    cfg = Json::objectValue;\n    if (!parseJson(mysql_non_fast_config, &cfg, &err))\n    {\n        LOG_ERROR << \"Failed to parse json: \" << err;\n        return 1;\n    }\n    config[\"db_clients\"].append(cfg);\n    cfg = Json::objectValue;\n    if (!parseJson(mysql_fast_config.data(), &cfg, &err))\n    {\n        LOG_ERROR << \"Failed to parse json: \" << err;\n        return 1;\n    }\n    config[\"db_clients\"].append(cfg);\n#endif\n\n#if USE_SQLITE3\n    cfg = Json::objectValue;\n    if (!parseJson(sqlite3_non_fast_config, &cfg, &err))\n    {\n        LOG_ERROR << \"Failed to parse json: \" << err;\n        return 1;\n    }\n    config[\"db_clients\"].append(cfg);\n#endif\n\n    std::promise<void> p1;\n    std::future<void> f1 = p1.get_future();\n    app().setThreadNum(1);\n    std::thread thr([&]() {\n        app().getLoop()->queueInLoop([&]() { p1.set_value(); });\n        app().loadConfigJson(config).run();\n    });\n\n    f1.get();\n    int testStatus = test::run(argc, argv);\n    app().getLoop()->queueInLoop([]() { app().quit(); });\n    thr.join();\n    return testStatus;\n}\n"
  },
  {
    "path": "orm_lib/tests/db_listener_test.cc",
    "content": "/**\n *\n *  @file db_listener_test.cc\n *  @author Nitromelon\n *\n *  Copyright 2022, Nitromelon.  All rights reserved.\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n *  Drogon database test program\n *\n */\n\n#define DROGON_TEST_MAIN\n#include <drogon/drogon_test.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/config.h>\n#include <drogon/orm/DbListener.h>\n#include <chrono>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace trantor;\nusing namespace std::chrono_literals;\n\n#if USE_POSTGRESQL\norm::DbClientPtr postgreClient;\n\nDROGON_TEST(ListenNotifyTest)\n{\n    auto clientPtr = postgreClient;\n    auto dbListener = DbListener::newPgListener(clientPtr->connectionInfo());\n    MANDATE(dbListener);\n\n    std::vector<std::string> channels{\"listen_test_0\",\n                                      \"listen_test_1\",\n                                      \"listen_test_2\"};\n\n    static int numNotifications = 0;\n    LOG_INFO << \"Start listen.\";\n    for (auto &chan : channels)\n    {\n        dbListener->listen(chan,\n                           [TEST_CTX, chan](const std::string &channel,\n                                            const std::string &message) {\n                               MANDATE(channel == chan);\n                               LOG_INFO << \"Message from \" << channel << \": \"\n                                        << message;\n                               ++numNotifications;\n                           });\n    }\n\n    std::this_thread::sleep_for(1s);  // ensure listen success\n    LOG_INFO << \"Start sending notifications.\";\n    for (int i = 0; i < 5; ++i)\n    {\n        for (auto &chan : channels)\n        {\n            // Can not use placeholders in LISTEN or NOTIFY command!!!\n            std::string cmd =\n                \"NOTIFY \" + chan + \", '\" + std::to_string(i) + \"'\";\n            clientPtr->execSqlAsync(\n                cmd,\n                [i, chan](const orm::Result &result) {\n                    LOG_INFO << chan << \" notified \" << i;\n                },\n                [](const orm::DrogonDbException &ex) {\n                    LOG_ERROR << \"Failed to notify \" << ex.base().what();\n                });\n        }\n    }\n\n    std::this_thread::sleep_for(5s);\n    LOG_INFO << \"Unlisten.\";\n    for (auto &chan : channels)\n    {\n        dbListener->unlisten(chan);\n    }\n    CHECK(numNotifications == 15);\n    std::this_thread::sleep_for(1s);\n}\n#endif\n\nint main(int argc, char **argv)\n{\n    trantor::Logger::setLogLevel(trantor::Logger::LogLevel::kDebug);\n\n    std::string dbConnInfo;\n    const char *dbUrl = std::getenv(\"DROGON_TEST_DB_CONN_INFO\");\n    if (dbUrl)\n    {\n        dbConnInfo = std::string{dbUrl};\n    }\n    else\n    {\n        dbConnInfo =\n            \"host=127.0.0.1 port=5432 dbname=postgres user=postgres \"\n            \"password=12345 \"\n            \"client_encoding=utf8\";\n    }\n    LOG_INFO << \"Database conn info: \" << dbConnInfo;\n#if USE_POSTGRESQL\n    postgreClient = orm::DbClient::newPgClient(dbConnInfo, 2, true);\n#else\n    LOG_DEBUG << \"Drogon is built without Postgresql. No tests executed.\";\n    return 0;\n#endif\n\n    int testStatus = test::run(argc, argv);\n    return testStatus;\n}\n"
  },
  {
    "path": "orm_lib/tests/db_test.cc",
    "content": "/**\n *\n *  @file db_test.cc\n *  @author An Tao\n *\n *  Copyright 2018, An Tao.  All rights reserved.\n *  Use of this source code is governed by a MIT license\n *  that can be found in the License file.\n *\n *  Drogon\n *\n *  Drogon database test program\n *\n */\n#define DROGON_TEST_MAIN\n#include <drogon/HttpAppFramework.h>\n#include <drogon/config.h>\n#include <drogon/drogon_test.h>\n#include <drogon/orm/CoroMapper.h>\n#include <drogon/orm/DbClient.h>\n#include <drogon/orm/DbTypes.h>\n#include <string_view>\n#include <drogon/orm/QueryBuilder.h>\n#include <trantor/utils/Logger.h>\n\n#include <stdlib.h>\n#include <chrono>\n#include <iostream>\n#include <string>\n#include <thread>\n#include <vector>\n\n#include \"mysql/Users.h\"\n#include \"mysql/Wallets.h\"\n#include \"mysql/Blog.h\"\n#include \"mysql/Category.h\"\n#include \"mysql/BlogTag.h\"\n#include \"mysql/Tag.h\"\n\n#include \"postgresql/Users.h\"\n#include \"postgresql/Wallets.h\"\n#include \"postgresql/Blog.h\"\n#include \"postgresql/Category.h\"\n#include \"postgresql/BlogTag.h\"\n#include \"postgresql/Tag.h\"\n\n#include \"sqlite3/Users.h\"\n#include \"sqlite3/Wallets.h\"\n#include \"sqlite3/Blog.h\"\n#include \"sqlite3/Category.h\"\n#include \"sqlite3/BlogTag.h\"\n#include \"sqlite3/Tag.h\"\n\nusing namespace std::chrono_literals;\nusing namespace drogon::orm;\n\nvoid expFunction(const DrogonDbException &e)\n{\n}\n#if USE_POSTGRESQL\nDbClientPtr postgreClient;\n\nDROGON_TEST(PostgreTest)\n{\n    auto &clientPtr = postgreClient;\n\n    // Test bugfix #1588\n    // PgBatchConnection.cc did not report error message to application\n    try\n    {\n        clientPtr->execSqlSync(\"select * from t_not_exists\");\n    }\n    catch (const DrogonDbException &e)\n    {\n        MANDATE(!std::string{e.base().what()}.empty());\n    }\n\n    // Prepare the test environment\n    *clientPtr << \"DROP TABLE IF EXISTS USERS\" >> [TEST_CTX,\n                                                   clientPtr](const Result &r) {\n        SUCCESS();\n        clientPtr->execSqlAsync(\n            \"select 1 as result\",\n            [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },\n            expFunction);\n    } >> [TEST_CTX](const DrogonDbException &e) {\n        FAULT(\"postgresql - Prepare the test environment(0) what():\",\n              e.base().what());\n    };\n    *clientPtr << \"CREATE TABLE users \"\n                  \"(\"\n                  \"    user_id character varying(32),\"\n                  \"    user_name character varying(64),\"\n                  \"    password character varying(64),\"\n                  \"    org_name character varying(20),\"\n                  \"    signature character varying(50),\"\n                  \"    avatar_id character varying(32),\"\n                  \"    id serial PRIMARY KEY,\"\n                  \"    salt character varying(20),\"\n                  \"    admin boolean DEFAULT false,\"\n                  \"    CONSTRAINT user_id_org UNIQUE(user_id, org_name)\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - Prepare the test environment(1) what():\",\n                  e.base().what());\n        };\n    // wallets table one-to-one with users table\n    *clientPtr << \"DROP TABLE IF EXISTS wallets\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - Prepare the test environment(2) what():\",\n                  e.base().what());\n        };\n    *clientPtr << \"CREATE TABLE wallets (\"\n                  \"    id serial PRIMARY KEY,\"\n                  \"    user_id character varying(32) DEFAULT NULL,\"\n                  \"    amount numeric(16,2) DEFAULT NULL\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - Prepare the test environment(3) what():\",\n                  e.base().what());\n        };\n    // blog\n    *clientPtr << \"DROP TABLE IF EXISTS blog\" >> [TEST_CTX](const Result &r) {\n        SUCCESS();\n    } >> [TEST_CTX](const DrogonDbException &e) {\n        FAULT(\"postgresql - Prepare the test environment(4) what():\",\n              e.base().what());\n    };\n    *clientPtr << \"CREATE TABLE blog (\"\n                  \"    id serial PRIMARY KEY,\"\n                  \"    title character varying(30) DEFAULT NULL,\"\n                  \"    category_id int4 DEFAULT NULL\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - Prepare the test environment(5) what():\",\n                  e.base().what());\n        };\n    // category table one-to-many with blog table\n    *clientPtr << \"DROP TABLE IF EXISTS category\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - Prepare the test environment(6) what():\",\n                  e.base().what());\n        };\n    *clientPtr << \"CREATE TABLE category (\"\n                  \"    id serial PRIMARY KEY,\"\n                  \"    name character varying(30) DEFAULT NULL\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - Prepare the test environment(7) what():\",\n                  e.base().what());\n        };\n    // tag table many-to-many with blog table\n    *clientPtr << \"DROP TABLE IF EXISTS tag\" >> [TEST_CTX](const Result &r) {\n        SUCCESS();\n    } >> [TEST_CTX](const DrogonDbException &e) {\n        FAULT(\"postgresql - Prepare the test environment(8) what():\",\n              e.base().what());\n    };\n    *clientPtr << \"CREATE TABLE tag (\"\n                  \"    id serial PRIMARY KEY,\"\n                  \"    name character varying(30) DEFAULT NULL\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - Prepare the test environment(9) what():\",\n                  e.base().what());\n        };\n    // blog_tag table is an intermediate table\n    *clientPtr << \"DROP TABLE IF EXISTS blog_tag\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - Prepare the test environment(10) what():\",\n                  e.base().what());\n        };\n    *clientPtr << \"CREATE TABLE blog_tag (\"\n                  \"    blog_id int4 NOT NULL,\"\n                  \"    tag_id int4 NOT NULL,\"\n                  \"    CONSTRAINT blog_tag_pkey PRIMARY KEY (blog_id, tag_id)\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - Prepare the test environment(11) what():\",\n                  e.base().what());\n        };\n    /// Test1:DbClient streaming-type interface\n    /// 1.1 insert,non-blocking\n    *clientPtr << \"insert into users (user_id,user_name,password,org_name) \"\n                  \"values($1,$2,$3,$4) returning *\"\n               << \"pg\"\n               << \"postgresql\"\n               << \"123\"\n               << \"default\" >>\n        [TEST_CTX](const Result &r) {\n            // std::cout << \"id=\" << r[0][\"id\"].as<int64_t>() << std::endl;\n            MANDATE(r[0][\"id\"].as<int64_t>() == 1);\n        } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient streaming-type interface(0) what():\",\n                  e.base().what());\n        };\n    /// 1.2 insert,blocking\n    *clientPtr\n            << \"insert into users (user_id,user_name,admin,password,org_name) \"\n               \"values($1,$2,$3,$4,$5) returning *\"\n            << \"pg1\"\n            << \"postgresql1\" << DefaultValue{} << \"123\"\n            << \"default\" << Mode::Blocking >>\n        [TEST_CTX](const Result &r) {\n            // std::cout << \"id=\" << r[0][\"id\"].as<int64_t>() << std::endl;\n            MANDATE(r[0][\"id\"].as<int64_t>() == 2);\n        } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient streaming-type interface(1) what():\",\n                  e.base().what());\n        };\n    /// 1.3 query,no-blocking\n    *clientPtr << \"select * from users where 1 = 1\" << Mode::NonBlocking >>\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 2); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient streaming-type interface(2) what():\",\n                  e.base().what());\n        };\n    /// 1.4 query,blocking\n    *clientPtr << \"select * from users where 1 = 1\" << Mode::Blocking >>\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 2); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient streaming-type interface(3) what():\",\n                  e.base().what());\n        };\n    /// 1.5 query,blocking\n    int count = 0;\n    *clientPtr << \"select user_name, user_id, id from users where 1 = 1\"\n               << Mode::Blocking >>\n        [&count, TEST_CTX](bool isNull,\n                           const std::string &name,\n                           std::string &&user_id,\n                           int id) {\n            if (!isNull)\n                ++count;\n            else\n                MANDATE(count == 2);\n        } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient streaming-type interface(4) what():\",\n                  e.base().what());\n        };\n    /// 1.6 query, parameter binding\n    *clientPtr << \"select * from users where id = $1\" << 1 >>\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient streaming-type interface(5) what():\",\n                  e.base().what());\n        };\n    /// 1.7 query, parameter binding\n    *clientPtr << \"select * from users where user_id = $1 and user_name = $2\"\n               << \"pg1\"\n               << \"postgresql1\" >>\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient streaming-type interface(6) what():\",\n                  e.base().what());\n        };\n    /// 1.8 delete\n    *clientPtr << \"delete from users where user_id = $1 and user_name = $2\"\n               << \"pg1\"\n               << \"postgresql1\" >>\n        [TEST_CTX](const Result &r) { MANDATE(r.affectedRows() == 1); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient streaming-type interface(7) what():\",\n                  e.base().what());\n        };\n    /// 1.9 update\n    *clientPtr << \"update users set user_id = $1, user_name = $2 where user_id \"\n                  \"= $3 and user_name = $4\"\n               << \"pg1\"\n               << \"postgresql1\"\n               << \"pg\"\n               << \"postgresql\" >>\n        [TEST_CTX](const Result &r) { MANDATE(r.affectedRows() == 1); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient streaming-type interface(8) what():\",\n                  e.base().what());\n        };\n    /// 1.10 query with raw parameter\n    auto rawParamData = std::make_shared<int>(htonl(3));\n    auto rawParam = RawParameter{rawParamData,\n                                 reinterpret_cast<char *>(rawParamData.get()),\n                                 sizeof(int),\n                                 1};\n    *clientPtr << \"select * from users where length(user_id)=$1\" << rawParam >>\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient streaming-type interface(9) what():\",\n                  e.base().what());\n        };\n\n    /// 1.11 clean up\n    *clientPtr << \"truncate table users restart identity\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient streaming-type interface(10) what():\",\n                  e.base().what());\n        };\n    /// Test asynchronous method\n    /// 2.1 insert\n    clientPtr->execSqlAsync(\n        \"insert into users \"\n        \"(user_id,user_name,password,org_name) \"\n        \"values($1,$2,$3,$4) returning *\",\n        [TEST_CTX](const Result &r) {\n            // std::cout << \"id=\" << r[0][\"id\"].as<int64_t>() << std::endl;\n            MANDATE(r[0][\"id\"].as<int64_t>() == 1);\n        },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient asynchronous interface(0) what():\",\n                  e.base().what());\n        },\n        \"pg\",\n        \"postgresql\",\n        \"123\",\n        \"default\");\n    /// 2.2 insert\n    clientPtr->execSqlAsync(\n        \"insert into users \"\n        \"(user_id,user_name,password,org_name) \"\n        \"values($1,$2,$3,$4)\",\n        [TEST_CTX](const Result &r) {\n            // std::cout << \"id=\" << r[0][\"id\"].as<int64_t>() << std::endl;\n            MANDATE(r.affectedRows() == 1);\n        },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient asynchronous interface(1) what():\",\n                  e.base().what());\n        },\n        \"pg1\",\n        \"postgresql1\",\n        \"123\",\n        \"default\");\n    /// 2.3 query\n    clientPtr->execSqlAsync(\n        \"select * from users where 1 = 1\",\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 2); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient asynchronous interface(2) what():\",\n                  e.base().what());\n        });\n    /// 2.2 query, parameter binding\n    clientPtr->execSqlAsync(\n        \"select * from users where id = $1\",\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient asynchronous interface(3) what():\",\n                  e.base().what());\n        },\n        1);\n    /// 2.3 query, parameter binding (std::string_view)\n    clientPtr->execSqlAsync(\n        \"select * from users where user_id = $1 and user_name = $2\",\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient asynchronous interface(4) what():\",\n                  e.base().what());\n        },\n        std::string_view(\"pg1\"),\n        \"postgresql1\");\n    /// 2.4 delete\n    clientPtr->execSqlAsync(\n        \"delete from users where user_id = $1 and user_name = $2\",\n        [TEST_CTX](const Result &r) { MANDATE(r.affectedRows() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient asynchronous interface(5) what():\",\n                  e.base().what());\n        },\n        \"pg1\",\n        \"postgresql1\");\n    /// 2.5 update\n    clientPtr->execSqlAsync(\n        \"update users set user_id = $1, user_name = $2 where user_id \"\n        \"= $3 and user_name = $4\",\n        [TEST_CTX](const Result &r) { MANDATE(r.affectedRows() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient asynchronous interface(6) what():\",\n                  e.base().what());\n        },\n        \"pg1\",\n        \"postgresql1\",\n        \"pg\",\n        \"postgresql\");\n    /// 2.6 query with raw parameter\n    clientPtr->execSqlAsync(\n        \"select * from users where length(user_id)=$1\",\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient asynchronous interface(7) what():\",\n                  e.base().what());\n        },\n        rawParam);\n    /// 2.7 clean up\n    clientPtr->execSqlAsync(\n        \"truncate table users restart identity\",\n        [TEST_CTX](const Result &r) { SUCCESS(); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - DbClient asynchronous interface(8) what():\",\n                  e.base().what());\n        });\n\n    /// Test synchronous method\n    /// 3.1 insert\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"insert into users  (user_id,user_name,password,org_name) \"\n            \"values($1,$2,$3,$4) returning *\",\n            \"pg\",\n            \"postgresql\",\n            \"123\",\n            \"default\");\n        MANDATE(r[0][\"id\"].as<int64_t>() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - DbClient asynchronous interface(0) what():\",\n              e.base().what());\n    }\n    /// 3.2 insert\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"insert into users  (user_id,user_name,password,org_name) \"\n            \"values($1,$2,$3,$4)\",\n            \"pg1\",\n            \"postgresql1\",\n            \"123\",\n            \"default\");\n        MANDATE(r.affectedRows() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - DbClient asynchronous interface(1) what():\",\n              e.base().what());\n    }\n    /// 3.3 query\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"select * from users where user_id=$1 and user_name=$2\",\n            \"pg1\",\n            \"postgresql1\");\n        MANDATE(r.size() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - DbClient asynchronous interface(2) what():\",\n              e.base().what());\n    }\n    /// 3.4 query for none\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"select * from users where user_id=$1 and user_name=$2\",\n            \"pg111\",\n            \"postgresql1\");\n        MANDATE(r.size() == 0);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - DbClient asynchronous interface(3) what():\",\n              e.base().what());\n    }\n    /// 3.5 bad sql\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"select * from users where user_id=$1 and user_name='1234'\",\n            \"pg111\",\n            \"postgresql1\");\n        MANDATE(r.size() == 0);\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n    /// 3.6 query with raw parameter\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"select * from users where length(user_id)=$1\", rawParam);\n        MANDATE(r.size() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - DbClient asynchronous interface(4) what():\",\n              e.base().what());\n    }\n    /// 3.7 clean up\n    try\n    {\n        auto r =\n            clientPtr->execSqlSync(\"truncate table users restart identity\");\n        SUCCESS();\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - DbClient asynchronous interface(5) what():\",\n              e.base().what());\n    }\n    /// Test future interface\n    /// 4.1 insert\n    auto f = clientPtr->execSqlAsyncFuture(\n        \"insert into users  (user_id,user_name,password,org_name) \"\n        \"values($1,$2,$3,$4) returning *\",\n        \"pg\",\n        \"postgresql\",\n        \"123\",\n        \"default\");\n    try\n    {\n        auto r = f.get();\n        MANDATE(r[0][\"id\"].as<int64_t>() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - DbClient future interface(0) what():\",\n              e.base().what());\n    }\n    /// 4.2 insert\n    f = clientPtr->execSqlAsyncFuture(\n        \"insert into users  (user_id,user_name,password,org_name) \"\n        \"values($1,$2,$3,$4)\",\n        \"pg1\",\n        \"postgresql1\",\n        \"123\",\n        \"default\");\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.affectedRows() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - DbClient future interface(1) what():\",\n              e.base().what());\n    }\n    /// 4.3 query\n    f = clientPtr->execSqlAsyncFuture(\n        \"select * from users where user_id=$1 and user_name=$2\",\n        \"pg1\",\n        \"postgresql1\");\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.size() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - DbClient future interface(2) what():\",\n              e.base().what());\n    }\n    /// 4.4 query for none\n    f = clientPtr->execSqlAsyncFuture(\n        \"select * from users where user_id=$1 and user_name=$2\",\n        \"pg111\",\n        \"postgresql1\");\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.size() == 0);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - DbClient future interface(3) what():\",\n              e.base().what());\n    }\n    /// 4.5 bad sql\n    f = clientPtr->execSqlAsyncFuture(\n        \"select * from users where user_id=$1 and user_name='12'\",\n        \"pg111\",\n        \"postgresql1\");\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.size() == 0);\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n    /// 4.6 query with raw parameter\n    f = clientPtr->execSqlAsyncFuture(\n        \"select * from users where length(user_id)=$1\", rawParam);\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.size() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - DbClient future interface(4) what():\",\n              e.base().what());\n    }\n    /// 4.7 clean up\n    f = clientPtr->execSqlAsyncFuture(\"truncate table users restart identity\");\n    try\n    {\n        auto r = f.get();\n        SUCCESS();\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - DbClient future interface(5) what():\",\n              e.base().what());\n    }\n\n    /// 5 Test Result and Row exception throwing\n    // 5.1 query for none and try to access\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"select * from users where user_id=$1 and user_name=$2\",\n            \"pg111\",\n            \"postgresql1\");\n        r.at(0);\n        FAULT(\"postgresql - Result throwing exceptions(0)\");\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n    // 5.2 insert one just for setup\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"insert into users  (user_id,user_name,password,org_name) \"\n            \"values($1,$2,$3,$4) returning *\",\n            \"pg\",\n            \"postgresql\",\n            \"123\",\n            \"default\");\n        MANDATE(r[0][\"id\"].as<int64_t>() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - Row throwing exceptions(0) what():\",\n              e.base().what());\n    }\n\n    // 5.3 try to access nonexistent column by name\n    try\n    {\n        auto r = clientPtr->execSqlSync(\"select * from users\");\n        auto row = r.at(0);\n        row[\"imaginary_column\"];\n        FAULT(\"postgresql - Row throwing exceptions(1)\");\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n\n    // 5.4 try to access nonexistent column by index\n    try\n    {\n        auto r = clientPtr->execSqlSync(\"select * from users\");\n        auto row = r.at(0);\n        row.at(420);\n        FAULT(\"postgresql - Row throwing exceptions(2)\");\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n\n    // 5.5 cleanup\n    try\n    {\n        auto r =\n            clientPtr->execSqlSync(\"truncate table users restart identity\");\n        SUCCESS();\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - Row throwing exceptions(3) what():\",\n              e.base().what());\n    }\n\n    /// Test ORM mapper\n    /// 6.1 insert, noneblocking\n    using namespace drogon_model::postgres;\n    Mapper<Users> mapper(clientPtr);\n    Users user;\n    user.setUserId(\"pg\");\n    user.setUserName(\"postgres\");\n    user.setPassword(\"123\");\n    user.setOrgName(\"default\");\n    mapper.insert(\n        user,\n        [TEST_CTX](Users ret) { MANDATE(ret.getPrimaryKey() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - ORM mapper asynchronous interface(0) what():\",\n                  e.base().what());\n        });\n\n    /// 6.1.5 insert future\n    user.setUserId(\"pg_future\");\n    auto fu = mapper.insertFuture(user);\n    try\n    {\n        auto u = fu.get();\n        SUCCESS();\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\n            \"postgresql - ORM mapper asynchronous future interface(0) what():\",\n            e.base().what());\n    }\n\n    /// 6.2 insert\n    user.setUserId(\"pg1\");\n    user.setUserName(\"postgres1\");\n    mapper.insert(\n        user,\n        [TEST_CTX](Users ret) { MANDATE(ret.getPrimaryKey() == 3); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - ORM mapper asynchronous interface(1) what():\",\n                  e.base().what());\n        });\n    /// 6.3 select where in\n    mapper.findBy(\n        Criteria(Users::Cols::_id,\n                 CompareOperator::In,\n                 std::vector<int32_t>{2, 200}),\n        [TEST_CTX](std::vector<Users> users) { MANDATE(users.size() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - ORM mapper asynchronous interface(2) what():\",\n                  e.base().what());\n        });\n    /// 6.3.5 count\n    mapper.count(\n        Criteria(Users::Cols::_id, CompareOperator::EQ, 2020),\n        [TEST_CTX](const size_t c) { MANDATE(c == 0); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - ORM mapper asynchronous interface(3) what():\",\n                  e.base().what());\n        });\n    /// 6.3.6 custom where query\n    mapper.findBy(\n        Criteria(\"id <@ int4range($?, null)\"_sql, 3),\n        [TEST_CTX](std::vector<Users> users) { MANDATE(users.size() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - ORM mapper asynchronous interface(4) what():\",\n                  e.base().what());\n        });\n    /// 6.3.7 pagination\n    mapper.paginate(2, 1).findAll(\n        [TEST_CTX](std::vector<Users> users) { MANDATE(users.size() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"postgresql - ORM mapper asynchronous interface(5) what():\",\n                  e.base().what());\n        });\n    /// 6.4 find by primary key. blocking\n    try\n    {\n        auto user = mapper.findByPrimaryKey(2);\n        SUCCESS();\n        Users newUser;\n        newUser.setId(user.getValueOfId());\n        newUser.setSalt(\"xxx\");\n        auto c = mapper.update(newUser);\n        MANDATE(c == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - ORM mapper synchronous interface(0) what():\",\n              e.base().what());\n    }\n    /// 6.5.1 update by criteria. blocking\n    try\n    {\n        auto user = mapper.findByPrimaryKey(2);\n        SUCCESS();\n        Users newUser;\n        newUser.setId(user.getValueOfId());\n        newUser.setSalt(\"xxx\");\n        newUser.setUserName(user.getValueOfUserName());\n        auto c = mapper.update(newUser);\n        MANDATE(c == 1);\n        c = mapper.updateBy({Users::Cols::_avatar_id, Users::Cols::_salt},\n                            Criteria(Users::Cols::_user_id,\n                                     CompareOperator::EQ,\n                                     \"pg\"),\n                            \"avatar of pg\",\n                            \"salt of pg\");\n        MANDATE(c == 1);\n        c = mapper.updateBy({Users::Cols::_avatar_id, Users::Cols::_salt},\n                            Criteria(Users::Cols::_user_id,\n                                     CompareOperator::EQ,\n                                     \"none\"),\n                            \"avatar of none\",\n                            \"salt of none\");\n        MANDATE(c == 0);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - ORM mapper synchronous interface(1) what():\",\n              e.base().what());\n    }\n\n    /// 6.5.2 update by criteria. asynchronous\n    {\n        std::vector<std::string> cols{Users::Cols::_avatar_id,\n                                      Users::Cols::_salt};\n        mapper.updateBy(\n            cols,\n            [TEST_CTX](const size_t c) { MANDATE(c == 1); },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\n                    \"postgresql - ORM mapper asynchronous updateBy \"\n                    \"interface(0) \"\n                    \"what():\",\n                    e.base().what());\n            },\n            Criteria(Users::Cols::_user_id, CompareOperator::EQ, \"pg\"),\n            \"avatar of pg\",\n            \"salt of pg\");\n    }\n\n    /// Test ORM QueryBuilder\n    /// execSync\n    try\n    {\n        const std::vector<Users> users =\n            QueryBuilder<Users>{}.from(\"users\").selectAll().execSync(clientPtr);\n        MANDATE(users.size() == 3);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - ORM QueryBuilder synchronous interface(0) what():\",\n              e.base().what());\n    }\n    try\n    {\n        const Result users =\n            QueryBuilder<Users>{}.from(\"users\").select(\"id\").execSync(\n                clientPtr);\n        MANDATE(users.size() == 3);\n        for (const Row &u : users)\n        {\n            MANDATE(!u[\"id\"].isNull());\n        }\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - ORM QueryBuilder synchronous interface(1) what():\",\n              e.base().what());\n    }\n    try\n    {\n        const Users user = QueryBuilder<Users>{}\n                               .from(\"users\")\n                               .selectAll()\n                               .eq(\"id\", \"3\")\n                               .limit(1)\n                               .single()\n                               .order(\"id\", false)\n                               .execSync(clientPtr);\n        MANDATE(user.getPrimaryKey() == 3);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - ORM QueryBuilder synchronous interface(2) what():\",\n              e.base().what());\n    }\n    try\n    {\n        const Row user = QueryBuilder<Users>{}\n                             .from(\"users\")\n                             .select(\"id\")\n                             .limit(1)\n                             .single()\n                             .order(\"id\", false)\n                             .execSync(clientPtr);\n        MANDATE(user[\"id\"].as<int32_t>() == 3);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"postgresql - ORM QueryBuilder synchronous interface(3) what():\",\n              e.base().what());\n    }\n\n    /// execAsyncFuture\n    {\n        std::future<std::vector<Users>> users =\n            QueryBuilder<Users>{}.from(\"users\").selectAll().execAsyncFuture(\n                clientPtr);\n        try\n        {\n            const std::vector<Users> r = users.get();\n            MANDATE(r.size() == 3);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\n                \"postgresql - ORM QueryBuilder asynchronous interface(0) \"\n                \"what():\",\n                e.base().what());\n        }\n    }\n    {\n        std::future<Result> users =\n            QueryBuilder<Users>{}.from(\"users\").select(\"id\").execAsyncFuture(\n                clientPtr);\n        try\n        {\n            const Result r = users.get();\n            MANDATE(r.size() == 3);\n            for (const Row &u : r)\n            {\n                MANDATE(!u[\"id\"].isNull());\n            }\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\n                \"postgresql - ORM QueryBuilder asynchronous interface(1) \"\n                \"what():\",\n                e.base().what());\n        }\n    }\n    {\n        std::future<Users> user = QueryBuilder<Users>{}\n                                      .from(\"users\")\n                                      .selectAll()\n                                      .eq(\"id\", \"3\")\n                                      .limit(1)\n                                      .single()\n                                      .order(\"id\", false)\n                                      .execAsyncFuture(clientPtr);\n        try\n        {\n            const Users r = user.get();\n            MANDATE(r.getPrimaryKey() == 3);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\n                \"postgresql - ORM QueryBuilder asynchronous interface(2) \"\n                \"what():\",\n                e.base().what());\n        }\n    }\n    {\n        std::future<Row> users = QueryBuilder<Users>{}\n                                     .from(\"users\")\n                                     .select(\"id\")\n                                     .limit(1)\n                                     .single()\n                                     .order(\"id\", false)\n                                     .execAsyncFuture(clientPtr);\n        try\n        {\n            const Row r = users.get();\n            MANDATE(r[\"id\"].as<int32_t>() == 3);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\n                \"postgresql - ORM QueryBuilder asynchronous interface(3) \"\n                \"what():\",\n                e.base().what());\n        }\n    }\n\n#ifdef __cpp_impl_coroutine\n    auto coro_test = [clientPtr, TEST_CTX]() -> drogon::Task<> {\n        /// 7 Test coroutines.\n        /// This is by no means comprehensive. But coroutine API is essentially\n        /// a wrapper around callbacks. The purpose is to test the interface\n        /// works 7.1 Basic queries\n        try\n        {\n            auto result =\n                co_await clientPtr->execSqlCoro(\"select * from users;\");\n            MANDATE(result.size() != 0);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - DbClient coroutine interface(0) what():\",\n                  e.base().what());\n        }\n        /// 7.2 Parameter binding\n        try\n        {\n            auto result = co_await clientPtr->execSqlCoro(\n                \"select * from users where 1=$1;\", 1);\n            MANDATE(result.size() != 0);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - DbClient coroutine interface(1) what():\",\n                  e.base().what());\n        }\n        /// 7.3 CoroMapper\n        try\n        {\n            CoroMapper<Users> mapper(clientPtr);\n            auto user = co_await mapper.findByPrimaryKey(2);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper coroutine interface(0) what():\",\n                  e.base().what());\n        }\n        try\n        {\n            CoroMapper<Users> mapper(clientPtr);\n            auto user = co_await mapper.findByPrimaryKey(314);\n            FAULT(\"postgresql - ORM mapper coroutine interface(1)\");\n        }\n        catch (const DrogonDbException &e)\n        {\n            SUCCESS();\n        }\n        try\n        {\n            CoroMapper<Users> mapper(clientPtr);\n            auto users = co_await mapper.findAll();\n            auto count = co_await mapper.count();\n            MANDATE(users.size() == count);\n        }\n        catch (const DrogonDbException &e)\n        {\n            SUCCESS();\n        }\n        try\n        {\n            CoroMapper<Users> mapper(clientPtr);\n            auto users = co_await mapper.orderBy(Users::Cols::_user_id)\n                             .limit(2)\n                             .findAll();\n            MANDATE(users.size() == 2);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper coroutine interface(2) what():\",\n                  e.base().what());\n        }\n        // CoroMapper::update\n        try\n        {\n            CoroMapper<Users> mapper(clientPtr);\n            auto user = co_await mapper.findByPrimaryKey(2);\n            SUCCESS();\n            Users newUser;\n            newUser.setId(user.getValueOfId());\n            newUser.setSalt(\"xxx\");\n            size_t c = co_await mapper.update(newUser);\n            MANDATE(c == 1);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper coroutine interface(3) what():\",\n                  e.base().what());\n        }\n        // CoroMapper::updateBy\n        try\n        {\n            CoroMapper<Users> mapper(clientPtr);\n            auto awaiter = mapper.updateBy(\n                {Users::Cols::_avatar_id, Users::Cols::_salt},\n                Criteria(Users::Cols::_user_id, CompareOperator::EQ, \"pg\"),\n                \"avatar of pg\",\n                \"salt of pg\");\n            size_t c = co_await awaiter;\n            MANDATE(c == 1);\n            // Can not compile if use co_await and initilizer_list together.\n            // Seems to be a bug in gcc.\n            std::vector<std::string> updateFields{Users::Cols::_avatar_id,\n                                                  Users::Cols::_salt};\n            c = co_await mapper.updateBy(updateFields,\n                                         Criteria(Users::Cols::_user_id,\n                                                  CompareOperator::EQ,\n                                                  \"none\"),\n                                         \"avatar of none\",\n                                         \"salt of none\");\n            MANDATE(c == 0);\n            auto count = co_await mapper.count();\n            // Use std::make_tuple to as an alternative to initializer_list\n            c = co_await mapper.updateBy(\n                std::make_tuple(Users::Cols::_avatar_id, Users::Cols::_salt),\n                Criteria(),\n                \"avatar\",\n                \"salt\");\n            MANDATE(c == count);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper coroutine interface(4) what():\",\n                  e.base().what());\n        }\n        /// 7.4 Transactions\n        try\n        {\n            auto trans = co_await clientPtr->newTransactionCoro();\n            auto result =\n                co_await trans->execSqlCoro(\"select * from users where 1=$1;\",\n                                            1);\n            MANDATE(result.size() != 0);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\n                \"postgresql - DbClient coroutine transaction interface(0) \"\n                \"what():\",\n                e.base().what());\n        }\n    };\n    drogon::sync_wait(coro_test());\n\n#endif\n\n    /// 8 Test ORM related query\n    /// 8.1 async\n    /// 8.1.1 one-to-one\n    Mapper<Wallets> walletsMapper(clientPtr);\n\n    /// prepare\n    {\n        Wallets wallet;\n        wallet.setUserId(\"pg\");\n        wallet.setAmount(\"2000.00\");\n        try\n        {\n            walletsMapper.insert(wallet);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper related prepare(0) what():\",\n                  e.base().what());\n        }\n    }\n\n    /// users to wallets\n    {\n        Users user;\n        user.setUserId(\"pg\");\n        user.getWallet(\n            clientPtr,\n            [TEST_CTX](Wallets r) {\n                MANDATE(r.getValueOfAmount() == \"2000.00\");\n            },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\n                    \"postgresql - ORM mapper related async one-to-one(0) \"\n                    \"what():\",\n                    e.base().what());\n            });\n    }\n    /// users to wallets without data\n    {\n        Users user;\n        user.setUserId(\"pg1\");\n        user.getWallet(\n            clientPtr,\n            [TEST_CTX](Wallets w) {\n                FAULT(\"postgresql - ORM mapper related async one-to-one(1)\");\n            },\n            [TEST_CTX](const DrogonDbException &e) { SUCCESS(); });\n    }\n\n    /// 8.1.2 one-to-many\n    Mapper<Category> categoryMapper(clientPtr);\n    Mapper<Blog> blogMapper(clientPtr);\n\n    /// prepare\n    {\n        Category category;\n        category.setName(\"category1\");\n        try\n        {\n            categoryMapper.insert(category);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper related prepare(1) what():\",\n                  e.base().what());\n        }\n        category.setName(\"category2\");\n        try\n        {\n            categoryMapper.insert(category);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper related prepare(2) what():\",\n                  e.base().what());\n        }\n        Blog blog;\n        blog.setTitle(\"title1\");\n        blog.setCategoryId(1);\n        try\n        {\n            blogMapper.insert(blog);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper related prepare(3) what():\",\n                  e.base().what());\n        }\n        blog.setTitle(\"title2\");\n        blog.setCategoryId(1);\n        try\n        {\n            blogMapper.insert(blog);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper related prepare(4) what():\",\n                  e.base().what());\n        }\n        blog.setTitle(\"title3\");\n        blog.setCategoryId(3);\n        try\n        {\n            blogMapper.insert(blog);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper related prepare(5) what():\",\n                  e.base().what());\n        }\n    }\n\n    /// categories to blogs\n    {\n        Category category;\n        category.setId(1);\n        category.getBlogs(\n            clientPtr,\n            [TEST_CTX](std::vector<Blog> r) { MANDATE(r.size() == 2); },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\n                    \"postgresql - ORM mapper related async one-to-many(0) \"\n                    \"what():\",\n                    e.base().what());\n            });\n    }\n    /// categories to blogs without data\n    {\n        Category category;\n        category.setId(2);\n        category.getBlogs(\n            clientPtr,\n            [TEST_CTX](std::vector<Blog> r) { MANDATE(r.size() == 0); },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\n                    \"postgresql - ORM mapper related async one-to-many(1) \"\n                    \"what():\",\n                    e.base().what());\n            });\n    }\n    /// blogs to categories\n    {\n        Blog blog;\n        blog.setCategoryId(1);\n        blog.getCategory(\n            clientPtr,\n            [TEST_CTX](Category r) {\n                MANDATE(r.getValueOfName() == \"category1\");\n            },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\n                    \"postgresql - ORM mapper related async one-to-many(2) \"\n                    \"what():\",\n                    e.base().what());\n            });\n    }\n    /// blogs to categories without data\n    {\n        Blog blog;\n        blog.setCategoryId(3);\n        blog.getCategory(\n            clientPtr,\n            [TEST_CTX](Category r) {\n                FAULT(\"postgresql - ORM mapper related async one-to-many(3)\");\n            },\n            [TEST_CTX](const DrogonDbException &e) { SUCCESS(); });\n    }\n\n    /// 8.1.3 many-to-many\n    Mapper<BlogTag> blogTagMapper(clientPtr);\n    Mapper<Tag> tagMapper(clientPtr);\n\n    /// prepare\n    {\n        BlogTag blogTag;\n        blogTag.setBlogId(1);\n        blogTag.setTagId(1);\n        try\n        {\n            blogTagMapper.insert(blogTag);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper related prepare(6) what():\",\n                  e.base().what());\n        }\n        blogTag.setBlogId(1);\n        blogTag.setTagId(2);\n        try\n        {\n            blogTagMapper.insert(blogTag);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper related prepare(7) what():\",\n                  e.base().what());\n        }\n        Tag tag;\n        tag.setName(\"tag1\");\n        try\n        {\n            tagMapper.insert(tag);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper related prepare(8) what():\",\n                  e.base().what());\n        }\n        tag.setName(\"tag2\");\n        try\n        {\n            tagMapper.insert(tag);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper related prepare(9) what():\",\n                  e.base().what());\n        }\n    }\n\n    /// blogs to tags\n    {\n        Blog blog;\n        blog.setId(1);\n        blog.getTags(\n            clientPtr,\n            [TEST_CTX](std::vector<std::pair<Tag, BlogTag>> r) {\n                MANDATE(r.size() == 2);\n            },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\n                    \"postgresql - ORM mapper related async many-to-many(0) \"\n                    \"what():\",\n                    e.base().what());\n            });\n    }\n\n    /// 8.2 async\n    /// 8.2.1 one-to-one\n    /// users to wallets\n    {\n        Users user;\n        user.setUserId(\"pg\");\n        try\n        {\n            auto r = user.getWallet(clientPtr);\n            MANDATE(r.getValueOfAmount() == \"2000.00\");\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper related sync one-to-one(0) what():\",\n                  e.base().what());\n        }\n    }\n    /// users to wallets without data\n    {\n        Users user;\n        user.setUserId(\"pg1\");\n        try\n        {\n            auto r = user.getWallet(clientPtr);\n            FAULT(\"postgresql - ORM mapper related sync one-to-one(1)\");\n        }\n        catch (const DrogonDbException &e)\n        {\n            SUCCESS();\n        }\n    }\n\n    /// 8.2.2 one-to-many\n    /// categories to blogs\n    {\n        Category category;\n        category.setId(1);\n        try\n        {\n            auto r = category.getBlogs(clientPtr);\n            MANDATE(r.size() == 2);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper related sync one-to-many(0) what():\",\n                  e.base().what());\n        }\n    }\n    /// categories to blogs without data\n    {\n        Category category;\n        category.setId(2);\n        try\n        {\n            auto r = category.getBlogs(clientPtr);\n            MANDATE(r.size() == 0);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper related sync one-to-many(1) what():\",\n                  e.base().what());\n        }\n    }\n    /// blogs to categories\n    {\n        Blog blog;\n        blog.setCategoryId(1);\n        try\n        {\n            auto r = blog.getCategory(clientPtr);\n            MANDATE(r.getValueOfName() == \"category1\");\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"postgresql - ORM mapper related sync one-to-many(2) what():\",\n                  e.base().what());\n        }\n    }\n    /// blogs to categories without data\n    {\n        Blog blog;\n        blog.setCategoryId(3);\n        try\n        {\n            auto r = blog.getCategory(clientPtr);\n            FAULT(\"postgresql - ORM mapper related sync one-to-many(3)\");\n        }\n        catch (const DrogonDbException &e)\n        {\n            SUCCESS();\n        }\n    }\n\n    /// 8.2.3 many-to-many\n    /// blogs to tags\n    {\n        Blog blog;\n        blog.setId(1);\n        try\n        {\n            auto r = blog.getTags(clientPtr);\n            MANDATE(r.size() == 2);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\n                \"postgresql - ORM mapper related sync many-to-many(0) what():\",\n                e.base().what());\n        }\n    }\n}\n#endif\n\n#if USE_MYSQL\nDbClientPtr mysqlClient;\n\nDROGON_TEST(MySQLTest)\n{\n    auto &clientPtr = mysqlClient;\n    REQUIRE(clientPtr != nullptr);\n    // Prepare the test environment\n    *clientPtr << \"CREATE DATABASE IF NOT EXISTS drogonTestMysql\" >>\n        [TEST_CTX, clientPtr](const Result &r) {\n            SUCCESS();\n            clientPtr->execSqlAsync(\n                \"select 1 as result\",\n                [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },\n                expFunction);\n        } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - Prepare the test environment(0) what():\",\n                  e.base().what());\n        };\n    *clientPtr << \"USE drogonTestMysql\" >> [TEST_CTX](const Result &r) {\n        SUCCESS();\n    } >> [TEST_CTX](const DrogonDbException &e) {\n        FAULT(\"mysql - Prepare the test environment(0) what():\",\n              e.base().what());\n    };\n    // mysql is case sensitive\n    *clientPtr << \"DROP TABLE IF EXISTS users\" >> [TEST_CTX](const Result &r) {\n        SUCCESS();\n    } >> [TEST_CTX](const DrogonDbException &e) {\n        FAULT(\"mysql - Prepare the test environment(1) what():\",\n              e.base().what());\n    };\n    *clientPtr << \"CREATE TABLE users \"\n                  \"(\"\n                  \"    id int(11) auto_increment PRIMARY KEY,\"\n                  \"    user_id varchar(32),\"\n                  \"    user_name varchar(64),\"\n                  \"    password varchar(64),\"\n                  \"    org_name varchar(20),\"\n                  \"    signature varchar(50),\"\n                  \"    avatar_id varchar(32),\"\n                  \"    salt character varying(20),\"\n                  \"    admin boolean DEFAULT false,\"\n                  \"    CONSTRAINT user_id_org UNIQUE(user_id, org_name)\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - Prepare the test environment(2) what():\",\n                  e.base().what());\n        };\n    // wallets table one-to-one with users table\n    *clientPtr << \"DROP TABLE IF EXISTS wallets\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - Prepare the test environment(3) what():\",\n                  e.base().what());\n        };\n    *clientPtr << \"CREATE TABLE `wallets` (\"\n                  \"    `id` int(11) AUTO_INCREMENT PRIMARY KEY,\"\n                  \"    `user_id` varchar(32) DEFAULT NULL,\"\n                  \"    `amount` decimal(16,2) DEFAULT NULL\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - Prepare the test environment(4) what():\",\n                  e.base().what());\n        };\n    // blog\n    *clientPtr << \"DROP TABLE IF EXISTS blog\" >> [TEST_CTX](const Result &r) {\n        SUCCESS();\n    } >> [TEST_CTX](const DrogonDbException &e) {\n        FAULT(\"mysql - Prepare the test environment(5) what():\",\n              e.base().what());\n    };\n    *clientPtr << \"CREATE TABLE `blog` (\"\n                  \"    `id` int(11) AUTO_INCREMENT PRIMARY KEY,\"\n                  \"    `title` varchar(30) DEFAULT NULL,\"\n                  \"    `category_id` int(11) DEFAULT NULL\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - Prepare the test environment(6) what():\",\n                  e.base().what());\n        };\n    // category table one-to-many with blog table\n    *clientPtr << \"DROP TABLE IF EXISTS category\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - Prepare the test environment(7) what():\",\n                  e.base().what());\n        };\n    *clientPtr << \"CREATE TABLE `category` (\"\n                  \"    `id` int(11) AUTO_INCREMENT PRIMARY KEY,\"\n                  \"    `name` varchar(30) DEFAULT NULL\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - Prepare the test environment(8) what():\",\n                  e.base().what());\n        };\n    // tag table many-to-many with blog table\n    *clientPtr << \"DROP TABLE IF EXISTS tag\" >> [TEST_CTX](const Result &r) {\n        SUCCESS();\n    } >> [TEST_CTX](const DrogonDbException &e) {\n        FAULT(\"mysql - Prepare the test environment(9) what():\",\n              e.base().what());\n    };\n    *clientPtr << \"CREATE TABLE `tag` (\"\n                  \"    `id` int(11) AUTO_INCREMENT PRIMARY KEY,\"\n                  \"    `name` varchar(30) DEFAULT NULL\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - Prepare the test environment(10) what():\",\n                  e.base().what());\n        };\n    // blog_tag table is an intermediate table\n    *clientPtr << \"DROP TABLE IF EXISTS blog_tag\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - Prepare the test environment(11) what():\",\n                  e.base().what());\n        };\n    *clientPtr << \"CREATE TABLE `blog_tag` (\"\n                  \"    `blog_id` int(11) NOT NULL,\"\n                  \"    `tag_id` int(11) NOT NULL,\"\n                  \"    PRIMARY KEY (`blog_id`,`tag_id`)\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - Prepare the test environment(12) what():\",\n                  e.base().what());\n        };\n    /// Test1:DbClient streaming-type interface\n    /// 1.1 insert,non-blocking\n    *clientPtr\n            << \"insert into users (user_id,user_name,password,org_name,admin) \"\n               \"values(?,?,?,?,?)\"\n            << \"pg\"\n            << \"postgresql\"\n            << \"123\"\n            << \"default\" << DefaultValue{} >>\n        [TEST_CTX](const Result &r) { MANDATE(r.insertId() == 1); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient streaming-type interface(0) what():\",\n                  e.base().what());\n        };\n    /// 1.2 insert,blocking\n    *clientPtr << \"insert into users (user_id,user_name,password,org_name) \"\n                  \"values(?,?,?,?)\"\n               << \"pg1\"\n               << \"postgresql1\"\n               << \"123\"\n               << \"default\" << Mode::Blocking >>\n        [TEST_CTX](const Result &r) { MANDATE(r.insertId() == 2); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient streaming-type interface(1) what():\",\n                  e.base().what());\n        };\n    /// 1.3 query,no-blocking\n    *clientPtr << \"select * from users where 1 = 1\" << Mode::NonBlocking >>\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 2); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient streaming-type interface(2) what():\",\n                  e.base().what());\n        };\n    /// 1.4 query,blocking\n    *clientPtr << \"select * from users where 1 = 1\" << Mode::Blocking >>\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 2); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient streaming-type interface(3) what():\",\n                  e.base().what());\n        };\n    /// 1.5 query,blocking\n    int count = 0;\n    *clientPtr << \"select user_name, user_id, id from users where 1 = 1\"\n               << Mode::Blocking >>\n        [&count, TEST_CTX](bool isNull,\n                           const std::string &name,\n                           std::string &&user_id,\n                           int id) {\n            if (!isNull)\n                ++count;\n            else\n                MANDATE(count == 2);\n        } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient streaming-type interface(4) what():\",\n                  e.base().what());\n        };\n    /// 1.6 query, parameter binding\n    *clientPtr << \"select * from users where id = ?\" << 1 >>\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient streaming-type interface(5) what():\",\n                  e.base().what());\n        };\n    /// 1.7 query, parameter binding\n    *clientPtr << \"select * from users where user_id = ? and user_name = ?\"\n               << \"pg1\"\n               << \"postgresql1\" >>\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient streaming-type interface(6) what():\",\n                  e.base().what());\n        };\n    /// 1.8 delete\n    *clientPtr << \"delete from users where user_id = ? and user_name = ?\"\n               << \"pg1\"\n               << \"postgresql1\" >>\n        [TEST_CTX](const Result &r) { MANDATE(r.affectedRows() == 1); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient streaming-type interface(7) what():\",\n                  e.base().what());\n        };\n    /// 1.9 update\n    *clientPtr << \"update users set user_id = ?, user_name = ? where user_id \"\n                  \"= ? and user_name = ?\"\n               << \"pg1\"\n               << \"postgresql1\"\n               << \"pg\"\n               << \"postgresql\" >>\n        [TEST_CTX](const Result &r) { MANDATE(r.affectedRows() == 1); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient streaming-type interface(8) what():\",\n                  e.base().what());\n        };\n    /// 1.10 query with raw parameter\n    // MariaDB uses little-endian, so the opposite of network ordering :P\n#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n    auto rawParamData = std::make_shared<int>(3);\n#else\n    auto rawParamData = std::make_shared<int>(0x03000000);  // byteswapped 3\n#endif\n    auto rawParam = RawParameter{rawParamData,\n                                 reinterpret_cast<char *>(rawParamData.get()),\n                                 sizeof(int),\n                                 internal::MySqlLong};\n    *clientPtr << \"select * from users where length(user_id)=?\" << rawParam >>\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient streaming-type interface(9) what():\",\n                  e.base().what());\n        };\n    /// 1.11 truncate\n    *clientPtr << \"truncate table users\" >> [TEST_CTX](const Result &r) {\n        SUCCESS();\n    } >> [TEST_CTX](const DrogonDbException &e) {\n        FAULT(\"mysql - DbClient streaming-type interface(10) what():\",\n              e.base().what());\n    };\n    /// Test asynchronous method\n    /// 2.1 insert\n    clientPtr->execSqlAsync(\n        \"insert into users \"\n        \"(user_id,user_name,password,org_name) \"\n        \"values(?,?,?,?)\",\n        [TEST_CTX](const Result &r) { MANDATE(r.insertId() != 0); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient asynchronous interface(0) what():\",\n                  e.base().what());\n        },\n        \"pg\",\n        \"postgresql\",\n        \"123\",\n        \"default\");\n    /// 2.2 insert\n    clientPtr->execSqlAsync(\n        \"insert into users \"\n        \"(user_id,user_name,password,org_name) \"\n        \"values(?,?,?,?)\",\n        [TEST_CTX](const Result &r) { MANDATE(r.affectedRows() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient asynchronous interface(1) what():\",\n                  e.base().what());\n        },\n        \"pg1\",\n        \"postgresql1\",\n        \"123\",\n        \"default\");\n    /// 2.3 query\n    clientPtr->execSqlAsync(\n        \"select * from users where 1 = 1\",\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 2); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient asynchronous interface(2) what():\",\n                  e.base().what());\n        });\n    /// 2.2 query, parameter binding\n    clientPtr->execSqlAsync(\n        \"select * from users where id = ?\",\n        [TEST_CTX](const Result &r) {\n            // std::cout << r.size() << \"\\n\";\n            MANDATE(r.size() == 1);\n        },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient asynchronous interface(3) what():\",\n                  e.base().what());\n        },\n        1);\n    /// 2.3 query, parameter binding\n    clientPtr->execSqlAsync(\n        \"select * from users where user_id = ? and user_name = ?\",\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient asynchronous interface(4) what():\",\n                  e.base().what());\n        },\n        \"pg1\",\n        \"postgresql1\");\n    /// 2.4 delete\n    clientPtr->execSqlAsync(\n        \"delete from users where user_id = ? and user_name = ?\",\n        [TEST_CTX](const Result &r) { MANDATE(r.affectedRows() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient asynchronous interface(5) what():\",\n                  e.base().what());\n        },\n        \"pg1\",\n        \"postgresql1\");\n    /// 2.5 update\n    clientPtr->execSqlAsync(\n        \"update users set user_id = ?, user_name = ? where user_id \"\n        \"= ? and user_name = ?\",\n        [TEST_CTX](const Result &r) { MANDATE(r.affectedRows() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient asynchronous interface(6) what():\",\n                  e.base().what());\n        },\n        \"pg1\",\n        \"postgresql1\",\n        \"pg\",\n        \"postgresql\");\n    /// 2.6 query with raw parameter\n    clientPtr->execSqlAsync(\n        \"select * from users where length(user_id)=?\",\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient asynchronous interface(7) what():\",\n                  e.base().what());\n        },\n        rawParam);\n    /// 2.7 truncate\n    clientPtr->execSqlAsync(\n        \"truncate table users\",\n        [TEST_CTX](const Result &r) { SUCCESS(); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - DbClient asynchronous interface(9) what():\",\n                  e.base().what());\n        });\n\n    /// Test synchronous method\n    /// 3.1 insert\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"insert into users  (user_id,user_name,password,org_name) \"\n            \"values(?,?,?,?)\",\n            \"pg\",\n            \"postgresql\",\n            \"123\",\n            \"default\");\n        // std::cout << r.insertId();\n        MANDATE(r.insertId() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - DbClient asynchronous interface(0) what():\",\n              e.base().what());\n    }\n    /// 3.2 insert\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"insert into users  (user_id,user_name,password,org_name) \"\n            \"values(?,?,?,?)\",\n            \"pg1\",\n            \"postgresql1\",\n            \"123\",\n            \"default\");\n        MANDATE(r.affectedRows() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - DbClient asynchronous interface(1) what():\",\n              e.base().what());\n    }\n    /// 3.3 query\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"select * from users where user_id=? and user_name=?\",\n            \"pg1\",\n            \"postgresql1\");\n        MANDATE(r.size() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - DbClient asynchronous interface(2) what():\",\n              e.base().what());\n    }\n    /// 3.4 query for none\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"select * from users where user_id=? and user_name=?\",\n            \"pg111\",\n            \"postgresql1\");\n        MANDATE(r.size() == 0);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - DbClient asynchronous interface(3) what():\",\n              e.base().what());\n    }\n    /// 3.5 bad sql\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"select * from users where user_id=? and user_name='1234'\",\n            \"pg111\",\n            \"postgresql1\");\n        MANDATE(r.size() == 0);\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n    /// 3.6 query with raw parameter\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"select * from users where length(user_id)=?\", rawParam);\n        MANDATE(r.size() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - DbClient asynchronous interface(4) what():\",\n              e.base().what());\n    }\n    /// 3.7 truncate\n    try\n    {\n        auto r = clientPtr->execSqlSync(\"truncate table users\");\n        SUCCESS();\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n    /// Test future interface\n    /// 4.1 insert\n    auto f = clientPtr->execSqlAsyncFuture(\n        \"insert into users  (user_id,user_name,password,org_name) \"\n        \"values(?,?,?,?) \",\n        \"pg\",\n        \"postgresql\",\n        \"123\",\n        \"default\");\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.insertId() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - DbClient future interface(0) what():\", e.base().what());\n    }\n    /// 4.2 insert\n    f = clientPtr->execSqlAsyncFuture(\n        \"insert into users  (user_id,user_name,password,org_name) \"\n        \"values(?,?,?,?)\",\n        \"pg1\",\n        \"postgresql1\",\n        \"123\",\n        \"default\");\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.affectedRows() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - DbClient future interface(1) what():\", e.base().what());\n    }\n    /// 4.3 query\n    f = clientPtr->execSqlAsyncFuture(\n        \"select * from users where user_id=? and user_name=?\",\n        \"pg1\",\n        \"postgresql1\");\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.size() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - DbClient future interface(2) what():\", e.base().what());\n    }\n    /// 4.4 query for none\n    f = clientPtr->execSqlAsyncFuture(\n        \"select * from users where user_id=? and user_name=?\",\n        \"pg111\",\n        \"postgresql1\");\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.size() == 0);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - DbClient future interface(3) what():\", e.base().what());\n    }\n    /// 4.5 bad sql\n    f = clientPtr->execSqlAsyncFuture(\n        \"select * from users where user_id=? and user_name='12'\",\n        \"pg111\",\n        \"postgresql1\");\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.size() == 0);\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n    /// 4.6. query with raw parameter\n    f = clientPtr->execSqlAsyncFuture(\n        \"select * from users where length(user_id)=?\", rawParam);\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.size() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - DbClient future interface(5) what():\", e.base().what());\n    }\n    /// 4.7 truncate\n    f = clientPtr->execSqlAsyncFuture(\"truncate table users\");\n    try\n    {\n        auto r = f.get();\n        SUCCESS();\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - DbClient future interface(6) what():\", e.base().what());\n    }\n\n    /// 5 Test Result and Row exception throwing\n    // 5.1 query for none and try to access\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"select * from users where user_id=? and user_name=?\",\n            \"pg111\",\n            \"postgresql1\");\n        r.at(0);\n        FAULT(\"mysql - Result throwing exceptions(0)\");\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n    // 5.2 insert one just for setup\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"insert into users  (user_id,user_name,password,org_name) \"\n            \"values(?,?,?,?)\",\n            \"pg\",\n            \"postgresql\",\n            \"123\",\n            \"default\");\n        MANDATE(r.insertId() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - Row throwing exceptions(0) what():\", e.base().what());\n    }\n\n    // 5.3 try to access nonexistent column by name\n    try\n    {\n        auto r = clientPtr->execSqlSync(\"select * from users\");\n        auto row = r.at(0);\n        row[\"imaginary_column\"];\n        FAULT(\"mysql - Row throwing exceptions(1)\");\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n\n    // 5.4 try to access nonexistent column by index\n    try\n    {\n        auto r = clientPtr->execSqlSync(\"select * from users\");\n        auto row = r.at(0);\n        row.at(420);\n        FAULT(\"mysql - Row throwing exceptions(2)\");\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n\n    // 5.5 cleanup\n    try\n    {\n        auto r = clientPtr->execSqlSync(\"truncate table users\");\n        SUCCESS();\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql -  Row throwing exceptions(3) what():\", e.base().what());\n    }\n\n    /// Test ORM mapper\n    /// 6.1 insert, noneblocking\n    using namespace drogon_model::drogonTestMysql;\n    Mapper<Users> mapper(clientPtr);\n    Users user;\n    user.setUserId(\"pg\");\n    user.setUserName(\"postgres\");\n    user.setPassword(\"123\");\n    user.setOrgName(\"default\");\n    mapper.insert(\n        user,\n        [TEST_CTX](Users ret) { MANDATE(ret.getPrimaryKey() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - ORM mapper asynchronous interface(0) what():\",\n                  e.base().what());\n        });\n    /// 6.1.5 count\n    mapper.count(\n        Criteria(Users::Cols::_id, CompareOperator::EQ, 1),\n        [TEST_CTX](const size_t c) { MANDATE(c == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - ORM mapper asynchronous interface(1) what():\",\n                  e.base().what());\n        });\n    /// 6.2 insert\n    user.setUserId(\"pg1\");\n    user.setUserName(\"postgres1\");\n    mapper.insert(\n        user,\n        [TEST_CTX](Users ret) { MANDATE(ret.getPrimaryKey() == 2); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - ORM mapper asynchronous interface(2) what():\",\n                  e.base().what());\n        });\n    /// 6.3 select where in\n    mapper.findBy(\n        Criteria(Users::Cols::_id,\n                 CompareOperator::In,\n                 std::vector<int32_t>{2, 200}),\n        [TEST_CTX](std::vector<Users> users) { MANDATE(users.size() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - ORM mapper asynchronous interface(3) what():\",\n                  e.base().what());\n        });\n    /// 6.3.6 custom where query\n    mapper.findBy(\n        Criteria(\"id between $? and $?\"_sql, 2, 200),\n        [TEST_CTX](std::vector<Users> users) { MANDATE(users.size() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"mysql - ORM mapper asynchronous interface(4) what():\",\n                  e.base().what());\n        });\n    /// 6.4 find by primary key. blocking\n    try\n    {\n        auto user = mapper.findByPrimaryKey(1);\n        SUCCESS();\n        Users newUser;\n        newUser.setId(user.getValueOfId());\n        newUser.setSalt(\"xxx\");\n        auto c = mapper.update(newUser);\n        MANDATE(c == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - ORM mapper synchronous interface(0) what():\",\n              e.base().what());\n    }\n    /// 6.5 update by criteria. blocking\n    try\n    {\n        auto user = mapper.findByPrimaryKey(2);\n        SUCCESS();\n        Users newUser;\n        newUser.setId(user.getValueOfId());\n        newUser.setSalt(\"xxx\");\n        newUser.setUserName(user.getValueOfUserName());\n        auto c = mapper.update(newUser);\n        MANDATE(c == 1);\n        c = mapper.updateBy({Users::Cols::_avatar_id, Users::Cols::_salt},\n                            Criteria(Users::Cols::_user_id,\n                                     CompareOperator::EQ,\n                                     \"pg\"),\n                            \"avatar of pg\",\n                            \"salt of pg\");\n        MANDATE(c == 1);\n        c = mapper.updateBy({Users::Cols::_avatar_id, Users::Cols::_salt},\n                            Criteria(Users::Cols::_user_id,\n                                     CompareOperator::EQ,\n                                     \"none\"),\n                            \"avatar of none\",\n                            \"salt of none\");\n        MANDATE(c == 0);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - ORM mapper synchronous interface(1) what():\",\n              e.base().what());\n    }\n\n    /// Test ORM QueryBuilder\n    /// execSync\n    try\n    {\n        const std::vector<Users> users =\n            QueryBuilder<Users>{}.from(\"users\").selectAll().execSync(clientPtr);\n        MANDATE(users.size() == 2);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - ORM QueryBuilder synchronous interface(0) what():\",\n              e.base().what());\n    }\n    try\n    {\n        const Result users =\n            QueryBuilder<Users>{}.from(\"users\").select(\"id\").execSync(\n                clientPtr);\n        MANDATE(users.size() == 2);\n        for (const Row &u : users)\n        {\n            MANDATE(!u[\"id\"].isNull());\n        }\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - ORM QueryBuilder synchronous interface(1) what():\",\n              e.base().what());\n    }\n    try\n    {\n        const Users user = QueryBuilder<Users>{}\n                               .from(\"users\")\n                               .selectAll()\n                               .eq(\"id\", \"2\")\n                               .limit(1)\n                               .single()\n                               .order(\"id\", false)\n                               .execSync(clientPtr);\n        MANDATE(user.getPrimaryKey() == 2);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - ORM QueryBuilder synchronous interface(2) what():\",\n              e.base().what());\n    }\n    try\n    {\n        const Row user = QueryBuilder<Users>{}\n                             .from(\"users\")\n                             .select(\"id\")\n                             .limit(1)\n                             .single()\n                             .order(\"id\", false)\n                             .execSync(clientPtr);\n        MANDATE(user[\"id\"].as<int32_t>() == 2);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"mysql - ORM QueryBuilder synchronous interface(3) what():\",\n              e.base().what());\n    }\n\n    /// execAsyncFuture\n    {\n        std::future<std::vector<Users>> users =\n            QueryBuilder<Users>{}.from(\"users\").selectAll().execAsyncFuture(\n                clientPtr);\n        try\n        {\n            const std::vector<Users> r = users.get();\n            MANDATE(r.size() == 2);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\n                \"mysql - ORM QueryBuilder asynchronous interface(0) \"\n                \"what():\",\n                e.base().what());\n        }\n    }\n    {\n        std::future<Result> users =\n            QueryBuilder<Users>{}.from(\"users\").select(\"id\").execAsyncFuture(\n                clientPtr);\n        try\n        {\n            const Result r = users.get();\n            MANDATE(r.size() == 2);\n            for (const Row &u : r)\n            {\n                MANDATE(!u[\"id\"].isNull());\n            }\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\n                \"mysql - ORM QueryBuilder asynchronous interface(1) \"\n                \"what():\",\n                e.base().what());\n        }\n    }\n    {\n        std::future<Users> user = QueryBuilder<Users>{}\n                                      .from(\"users\")\n                                      .selectAll()\n                                      .eq(\"id\", \"2\")\n                                      .limit(1)\n                                      .single()\n                                      .order(\"id\", false)\n                                      .execAsyncFuture(clientPtr);\n        try\n        {\n            const Users r = user.get();\n            MANDATE(r.getPrimaryKey() == 2);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\n                \"mysql - ORM QueryBuilder asynchronous interface(2) \"\n                \"what():\",\n                e.base().what());\n        }\n    }\n    {\n        std::future<Row> users = QueryBuilder<Users>{}\n                                     .from(\"users\")\n                                     .select(\"id\")\n                                     .limit(1)\n                                     .single()\n                                     .order(\"id\", false)\n                                     .execAsyncFuture(clientPtr);\n        try\n        {\n            const Row r = users.get();\n            MANDATE(r[\"id\"].as<int32_t>() == 2);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\n                \"mysql - ORM QueryBuilder asynchronous interface(3) \"\n                \"what():\",\n                e.base().what());\n        }\n    }\n\n#ifdef __cpp_impl_coroutine\n    auto coro_test = [clientPtr, TEST_CTX]() -> drogon::Task<> {\n        /// 7 Test coroutines.\n        /// This is by no means comprehensive. But coroutine API is essentially\n        /// a wrapper around callbacks. The purpose is to test the interface\n        /// works 7.1 Basic queries\n        try\n        {\n            auto result =\n                co_await clientPtr->execSqlCoro(\"select * from users;\");\n            MANDATE(result.size() != 0);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - DbClient coroutine interface(0) what():\",\n                  e.base().what());\n        }\n        /// 7.2 Parameter binding\n        try\n        {\n            auto result = co_await clientPtr->execSqlCoro(\n                \"select * from users where 1=?;\", 1);\n            MANDATE(result.size() != 0);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - DbClient coroutine interface(1) what():\",\n                  e.base().what());\n        }\n    };\n    drogon::sync_wait(coro_test());\n\n#endif\n\n    /// 8 Test ORM related query\n    /// 8.1 async\n    /// 8.1.1 one-to-one\n    Mapper<Wallets> walletsMapper(clientPtr);\n\n    /// prepare\n    {\n        Wallets wallet;\n        wallet.setUserId(\"pg\");\n        wallet.setAmount(\"2000.00\");\n        try\n        {\n            walletsMapper.insert(wallet);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - ORM mapper related prepare(0) what():\",\n                  e.base().what());\n        }\n    }\n\n    /// users to wallets\n    {\n        Users user;\n        user.setUserId(\"pg\");\n        user.getWallet(\n            clientPtr,\n            [TEST_CTX](Wallets r) {\n                MANDATE(r.getValueOfAmount() == \"2000.00\");\n            },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\"mysql - ORM mapper related async one-to-one(0) what():\",\n                      e.base().what());\n            });\n    }\n    /// users to wallets without data\n    {\n        Users user;\n        user.setUserId(\"pg1\");\n        user.getWallet(\n            clientPtr,\n            [TEST_CTX](Wallets w) {\n                FAULT(\"mysql - ORM mapper related async one-to-one(1)\");\n            },\n            [TEST_CTX](const DrogonDbException &e) { SUCCESS(); });\n    }\n\n    /// 8.1.2 one-to-many\n    Mapper<Category> categoryMapper(clientPtr);\n    Mapper<Blog> blogMapper(clientPtr);\n\n    /// prepare\n    {\n        Category category;\n        category.setName(\"category1\");\n        try\n        {\n            categoryMapper.insert(category);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - ORM mapper related prepare(1) what():\",\n                  e.base().what());\n        }\n        category.setName(\"category2\");\n        try\n        {\n            categoryMapper.insert(category);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - ORM mapper related prepare(2) what():\",\n                  e.base().what());\n        }\n        Blog blog;\n        blog.setTitle(\"title1\");\n        blog.setCategoryId(1);\n        try\n        {\n            blogMapper.insert(blog);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - ORM mapper related prepare(3) what():\",\n                  e.base().what());\n        }\n        blog.setTitle(\"title2\");\n        blog.setCategoryId(1);\n        try\n        {\n            blogMapper.insert(blog);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - ORM mapper related prepare(4) what():\",\n                  e.base().what());\n        }\n        blog.setTitle(\"title3\");\n        blog.setCategoryId(3);\n        try\n        {\n            blogMapper.insert(blog);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - ORM mapper related prepare(5) what():\",\n                  e.base().what());\n        }\n    }\n\n    /// categories to blogs\n    {\n        Category category;\n        category.setId(1);\n        category.getBlogs(\n            clientPtr,\n            [TEST_CTX](std::vector<Blog> r) { MANDATE(r.size() == 2); },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\"mysql - ORM mapper related async one-to-many(0) what():\",\n                      e.base().what());\n            });\n    }\n    /// categories to blogs without data\n    {\n        Category category;\n        category.setId(2);\n        category.getBlogs(\n            clientPtr,\n            [TEST_CTX](std::vector<Blog> r) { MANDATE(r.size() == 0); },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\"mysql - ORM mapper related async one-to-many(1) what():\",\n                      e.base().what());\n            });\n    }\n    /// blogs to categories\n    {\n        Blog blog;\n        blog.setCategoryId(1);\n        blog.getCategory(\n            clientPtr,\n            [TEST_CTX](Category r) {\n                MANDATE(r.getValueOfName() == \"category1\");\n            },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\"mysql - ORM mapper related async one-to-many(2) what():\",\n                      e.base().what());\n            });\n    }\n    /// blogs to categories without data\n    {\n        Blog blog;\n        blog.setCategoryId(3);\n        blog.getCategory(\n            clientPtr,\n            [TEST_CTX](Category r) {\n                FAULT(\"mysql - ORM mapper related async one-to-many(3)\");\n            },\n            [TEST_CTX](const DrogonDbException &e) { SUCCESS(); });\n    }\n\n    /// 8.1.3 many-to-many\n    Mapper<BlogTag> blogTagMapper(clientPtr);\n    Mapper<Tag> tagMapper(clientPtr);\n\n    /// prepare\n    {\n        BlogTag blogTag;\n        blogTag.setBlogId(1);\n        blogTag.setTagId(1);\n        try\n        {\n            blogTagMapper.insert(blogTag);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - ORM mapper related prepare(6) what():\",\n                  e.base().what());\n        }\n        blogTag.setBlogId(1);\n        blogTag.setTagId(2);\n        try\n        {\n            blogTagMapper.insert(blogTag);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - ORM mapper related prepare(7) what():\",\n                  e.base().what());\n        }\n        Tag tag;\n        tag.setName(\"tag1\");\n        try\n        {\n            tagMapper.insert(tag);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - ORM mapper related prepare(8) what():\",\n                  e.base().what());\n        }\n        tag.setName(\"tag2\");\n        try\n        {\n            tagMapper.insert(tag);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - ORM mapper related prepare(9) what():\",\n                  e.base().what());\n        }\n    }\n\n    /// blogs to tags\n    {\n        Blog blog;\n        blog.setId(1);\n        blog.getTags(\n            clientPtr,\n            [TEST_CTX](std::vector<std::pair<Tag, BlogTag>> r) {\n                MANDATE(r.size() == 2);\n            },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\n                    \"mysql - ORM mapper related async many-to-many(0) what():\",\n                    e.base().what());\n            });\n    }\n\n    /// 8.2 async\n    /// 8.2.1 one-to-one\n    /// users to wallets\n    {\n        Users user;\n        user.setUserId(\"pg\");\n        try\n        {\n            auto r = user.getWallet(clientPtr);\n            MANDATE(r.getValueOfAmount() == \"2000.00\");\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - ORM mapper related sync one-to-one(0) what():\",\n                  e.base().what());\n        }\n    }\n    /// users to wallets without data\n    {\n        Users user;\n        user.setUserId(\"pg1\");\n        try\n        {\n            auto r = user.getWallet(clientPtr);\n            FAULT(\"mysql - ORM mapper related sync one-to-one(1)\");\n        }\n        catch (const DrogonDbException &e)\n        {\n            SUCCESS();\n        }\n    }\n\n    /// 8.2.2 one-to-many\n    /// categories to blogs\n    {\n        Category category;\n        category.setId(1);\n        try\n        {\n            auto r = category.getBlogs(clientPtr);\n            MANDATE(r.size() == 2);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - ORM mapper related sync one-to-many(0) what():\",\n                  e.base().what());\n        }\n    }\n    /// categories to blogs without data\n    {\n        Category category;\n        category.setId(2);\n        try\n        {\n            auto r = category.getBlogs(clientPtr);\n            MANDATE(r.size() == 0);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - ORM mapper related sync one-to-many(1) what():\",\n                  e.base().what());\n        }\n    }\n    /// blogs to categories\n    {\n        Blog blog;\n        blog.setCategoryId(1);\n        try\n        {\n            auto r = blog.getCategory(clientPtr);\n            MANDATE(r.getValueOfName() == \"category1\");\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - ORM mapper related sync one-to-many(2) what():\",\n                  e.base().what());\n        }\n    }\n    /// blogs to categories without data\n    {\n        Blog blog;\n        blog.setCategoryId(3);\n        try\n        {\n            auto r = blog.getCategory(clientPtr);\n            FAULT(\"mysql - ORM mapper related sync one-to-many(3)\");\n        }\n        catch (const DrogonDbException &e)\n        {\n            SUCCESS();\n        }\n    }\n\n    /// 8.2.3 many-to-many\n    /// blogs to tags\n    {\n        Blog blog;\n        blog.setId(1);\n        try\n        {\n            auto r = blog.getTags(clientPtr);\n            MANDATE(r.size() == 2);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"mysql - ORM mapper related sync many-to-many(0) what():\",\n                  e.base().what());\n        }\n    }\n}\n#endif\n\n#if USE_SQLITE3\nDbClientPtr sqlite3Client;\n\nDROGON_TEST(SQLite3Test)\n{\n    auto &clientPtr = sqlite3Client;\n    REQUIRE(clientPtr != nullptr);\n\n    // Prepare the test environment\n    *clientPtr << \"DROP TABLE IF EXISTS users\" >> [TEST_CTX,\n                                                   clientPtr](const Result &r) {\n        SUCCESS();\n        clientPtr->execSqlAsync(\n            \"select 1 as result\",\n            [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },\n            expFunction);\n    } >> [TEST_CTX](const DrogonDbException &e) {\n        FAULT(\"sqlite3 - Prepare the test environment(0):  what():\",\n              e.base().what());\n    };\n\n    *clientPtr << \"CREATE TABLE users \"\n                  \"(\"\n                  \"    id INTEGER PRIMARY KEY autoincrement,\"\n                  \"    user_id varchar(32),\"\n                  \"    user_name varchar(64),\"\n                  \"    password varchar(64),\"\n                  \"    org_name varchar(20),\"\n                  \"    signature varchar(50),\"\n                  \"    avatar_id varchar(32),\"\n                  \"    salt character varchar(20),\"\n                  \"    admin boolean DEFAULT false,\"\n                  \"    create_time datetime,\"\n                  \"    CONSTRAINT user_id_org UNIQUE(user_id, org_name)\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - Prepare the test environment(1) what():\",\n                  e.base().what());\n        };\n    // wallets table one-to-one with users table\n    *clientPtr << \"DROP TABLE IF EXISTS wallets\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - Prepare the test environment(2) what():\",\n                  e.base().what());\n        };\n    *clientPtr << \"CREATE TABLE `wallets` (\"\n                  \"    `id` INTEGER PRIMARY KEY autoincrement,\"\n                  \"    `user_id` varchar(32) DEFAULT NULL,\"\n                  \"    `amount` decimal(16,2) DEFAULT NULL\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - Prepare the test environment(3) what():\",\n                  e.base().what());\n        };\n    // blog\n    *clientPtr << \"DROP TABLE IF EXISTS blog\" >> [TEST_CTX](const Result &r) {\n        SUCCESS();\n    } >> [TEST_CTX](const DrogonDbException &e) {\n        FAULT(\"sqlite3 - Prepare the test environment(4) what():\",\n              e.base().what());\n    };\n    *clientPtr << \"CREATE TABLE `blog` (\"\n                  \"    `id` INTEGER PRIMARY KEY autoincrement,\"\n                  \"    `title` varchar(30) DEFAULT NULL,\"\n                  \"    `category_id` INTEGER DEFAULT NULL\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - Prepare the test environment(5) what():\",\n                  e.base().what());\n        };\n    // category table one-to-many with blog table\n    *clientPtr << \"DROP TABLE IF EXISTS category\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - Prepare the test environment(6) what():\",\n                  e.base().what());\n        };\n    *clientPtr << \"CREATE TABLE `category` (\"\n                  \"    `id` INTEGER PRIMARY KEY autoincrement,\"\n                  \"    `name` varchar(30) DEFAULT NULL\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - Prepare the test environment(7) what():\",\n                  e.base().what());\n        };\n    // tag table many-to-many with blog table\n    *clientPtr << \"DROP TABLE IF EXISTS tag\" >> [TEST_CTX](const Result &r) {\n        SUCCESS();\n    } >> [TEST_CTX](const DrogonDbException &e) {\n        FAULT(\"sqlite3 - Prepare the test environment(8) what():\",\n              e.base().what());\n    };\n    *clientPtr << \"CREATE TABLE `tag` (\"\n                  \"    `id` INTEGER PRIMARY KEY autoincrement,\"\n                  \"    `name` varchar(30) DEFAULT NULL\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - Prepare the test environment(9) what():\",\n                  e.base().what());\n        };\n    // blog_tag table is an intermediate table\n    *clientPtr << \"DROP TABLE IF EXISTS blog_tag\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - Prepare the test environment(10) what():\",\n                  e.base().what());\n        };\n    *clientPtr << \"CREATE TABLE `blog_tag` (\"\n                  \"    `blog_id` INTEGER NOT NULL,\"\n                  \"    `tag_id` INTEGER NOT NULL,\"\n                  \"    PRIMARY KEY (`blog_id`,`tag_id`)\"\n                  \")\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - Prepare the test environment(11) what():\",\n                  e.base().what());\n        };\n    /// Test1:DbClient streaming-type interface\n    /// 1.1 insert,non-blocking\n    *clientPtr << \"insert into users \"\n                  \"(user_id,user_name,password,org_name,create_time) \"\n                  \"values(?,?,?,?,?)\"\n               << \"pg\"\n               << \"postgresql\"\n               << \"123\"\n               << \"default\" << trantor::Date::now() >>\n        [TEST_CTX](const Result &r) { MANDATE(r.insertId() == 1ULL); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient streaming-type interface(0) what():\",\n                  e.base().what());\n        };\n    /// 1.2 insert,blocking\n    *clientPtr << \"insert into users \"\n                  \"(user_id,user_name,password,org_name,create_time) \"\n                  \"values(?,?,?,?,?)\"\n               << \"pg1\"\n               << \"postgresql1\"\n               << \"123\"\n               << \"default\" << trantor::Date::now() << Mode::Blocking >>\n        [TEST_CTX](const Result &r) { MANDATE(r.insertId() == 2ULL); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient streaming-type interface(1) what():\",\n                  e.base().what());\n        };\n    *clientPtr << \"select * from users where user_id=?;\"\n               << \"pg1\" << Mode::Blocking >>\n        [TEST_CTX](const Result &r) {\n            MANDATE(r.size() == 1);\n            MANDATE(r[0][\"user_id\"].as<std::string>() == \"pg1\");\n            using namespace drogon_model::sqlite3;\n            Users user(r[0]);\n            MANDATE(user.getValueOfUserId() == \"pg1\");\n            // LOG_INFO << \"user:\" << user.toJson().toStyledString();\n            MANDATE(trantor::Date::now().secondsSinceEpoch() -\n                        user.getValueOfCreateTime().secondsSinceEpoch() <=\n                    1);\n        } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient streaming-type interface(2) what():\",\n                  e.base().what());\n        };\n\n    /// 1.3 query,no-blocking\n    *clientPtr << \"select * from users where 1 = 1\" << Mode::NonBlocking >>\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 2UL); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient streaming-type interface(2) what():\",\n                  e.base().what());\n        };\n    /// 1.4 query,blocking\n    *clientPtr << \"select * from users where 1 = 1\" << Mode::Blocking >>\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 2UL); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient streaming-type interface(3) what():\",\n                  e.base().what());\n        };\n    /// 1.5 query,blocking\n    int count = 0;\n    *clientPtr << \"select user_name, user_id, id from users where 1 = 1\"\n               << Mode::Blocking >>\n        [&count, TEST_CTX](bool isNull,\n                           const std::string &name,\n                           std::string &&user_id,\n                           int id) {\n            if (!isNull)\n                ++count;\n            else\n            {\n                MANDATE(count == 2);\n            }\n        } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient streaming-type interface(4) what():\",\n                  e.base().what());\n        };\n    /// 1.6 query, parameter binding\n    *clientPtr << \"select * from users where id = ?\" << 1 >>\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1UL); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient streaming-type interface(5) what():\",\n                  e.base().what());\n        };\n    /// 1.7 query, parameter binding\n    *clientPtr << \"select * from users where user_id = ? and user_name = ?\"\n               << \"pg1\"\n               << \"postgresql1\" >>\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1UL); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient streaming-type interface(6) what():\",\n                  e.base().what());\n        };\n    /// 1.8 delete\n    *clientPtr << \"delete from users where user_id = ? and user_name = ?\"\n               << \"pg1\"\n               << \"postgresql1\" >>\n        [TEST_CTX](const Result &r) { MANDATE(r.affectedRows() == 1UL); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient streaming-type interface(7) what():\",\n                  e.base().what());\n        };\n    /// 1.9 update\n    *clientPtr << \"update users set user_id = ?, user_name = ? where user_id \"\n                  \"= ? and user_name = ?\"\n               << \"pg1\"\n               << \"postgresql1\"\n               << \"pg\"\n               << \"postgresql\" >>\n        [TEST_CTX](const Result &r) { MANDATE(r.affectedRows() == 1UL); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient streaming-type interface(8) what():\",\n                  e.base().what());\n        };\n    /// 1.10 query with raw parameter\n    auto rawParamData = std::make_shared<int>(3);\n    auto rawParam = RawParameter{rawParamData,\n                                 reinterpret_cast<char *>(rawParamData.get()),\n                                 0,\n                                 Sqlite3TypeInt};\n    *clientPtr << \"select * from users where length(user_id) = ?\" << rawParam >>\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient streaming-type interface(9) what():\",\n                  e.base().what());\n        };\n    /// 1.11 clean up\n    *clientPtr << \"delete from users\" >> [TEST_CTX](const Result &r) {\n        SUCCESS();\n    } >> [TEST_CTX](const DrogonDbException &e) {\n        FAULT(\"sqlite3 - DbClient streaming-type interface(10.1) what():\",\n              e.base().what());\n    };\n    *clientPtr << \"UPDATE sqlite_sequence SET seq = 0\" >>\n        [TEST_CTX](const Result &r) { SUCCESS(); } >>\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient streaming-type interface(10.2) what():\",\n                  e.base().what());\n        };\n    /// Test asynchronous method\n    /// 2.1 insert\n    clientPtr->execSqlAsync(\n        \"insert into users \"\n        \"(user_id,user_name,password,org_name,create_time) \"\n        \"values(?,?,?,?,?)\",\n        [TEST_CTX](const Result &r) { MANDATE(r.insertId() == 1ULL); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient asynchronous interface(0) what():\",\n                  e.base().what());\n        },\n        \"pg\",\n        \"postgresql\",\n        \"123\",\n        \"default\",\n        trantor::Date::now());\n    /// 2.2 insert\n    clientPtr->execSqlAsync(\n        \"insert into users \"\n        \"(user_id,user_name,password,org_name,create_time) \"\n        \"values(?,?,?,?,?)\",\n        [TEST_CTX](const Result &r) { MANDATE(r.affectedRows() == 1UL); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient asynchronous interface(1) what():\",\n                  e.base().what());\n        },\n        \"pg1\",\n        \"postgresql1\",\n        \"123\",\n        \"default\",\n        trantor::Date::now());\n    /// 2.3 query\n    clientPtr->execSqlAsync(\n        \"select * from users where 1 = 1\",\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 2UL); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient asynchronous interface(2) what():\",\n                  e.base().what());\n        });\n    /// 2.2 query, parameter binding\n    clientPtr->execSqlAsync(\n        \"select * from users where id = ?\",\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1UL); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient asynchronous interface(3) what():\",\n                  e.base().what());\n        },\n        1);\n    /// 2.3 query, parameter binding\n    clientPtr->execSqlAsync(\n        \"select * from users where user_id = ? and user_name = ?\",\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1UL); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient asynchronous interface(4) what():\",\n                  e.base().what());\n        },\n        \"pg1\",\n        \"postgresql1\");\n    /// 2.4 delete\n    clientPtr->execSqlAsync(\n        \"delete from users where user_id = ? and user_name = ?\",\n        [TEST_CTX](const Result &r) { MANDATE(r.affectedRows() == 1UL); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient asynchronous interface(5) what():\",\n                  e.base().what());\n        },\n        \"pg1\",\n        \"postgresql1\");\n    /// 2.5 update\n    clientPtr->execSqlAsync(\n        \"update users set user_id = ?, user_name = ? where user_id \"\n        \"= ? and user_name = ?\",\n        [TEST_CTX](const Result &r) { MANDATE(r.affectedRows() == 1UL); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient asynchronous interface(6) what():\",\n                  e.base().what());\n        },\n        \"pg1\",\n        \"postgresql1\",\n        \"pg\",\n        \"postgresql\");\n    /// 2.6 query with raw parameter\n    clientPtr->execSqlAsync(\n        \"select * from users where length(user_id) = ?\",\n        [TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient asynchronous interface(7) what():\",\n                  e.base().what());\n        },\n        rawParam);\n    /// 2.7 clean up\n    clientPtr->execSqlAsync(\n        \"delete from users\",\n        [TEST_CTX](const Result &r) { SUCCESS(); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient asynchronous interface(8.1) what():\",\n                  e.base().what());\n        });\n    clientPtr->execSqlAsync(\n        \"UPDATE sqlite_sequence SET seq = 0\",\n        [TEST_CTX](const Result &r) { SUCCESS(); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - DbClient asynchronous interface(8.2) what():\",\n                  e.base().what());\n        });\n\n    /// Test synchronous method\n    /// 3.1 insert\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"insert into users  \"\n            \"(user_id,user_name,password,org_name,create_time) \"\n            \"values(?,?,?,?,?)\",\n            \"pg\",\n            \"postgresql\",\n            \"123\",\n            \"default\",\n            trantor::Date::now());\n        MANDATE(r.insertId() == 1ULL);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - DbClient asynchronous interface(0) what():\",\n              e.base().what());\n    }\n    /// 3.2 insert,(std::string_view)\n    try\n    {\n        std::string_view sv(\"pg1\");\n        std::string_view sv1(\"postgresql1\");\n        std::string_view sv2(\"123\");\n        auto r = clientPtr->execSqlSync(\n            \"insert into users  \"\n            \"(user_id,user_name,password,org_name,create_time) \"\n            \"values(?,?,?,?,?)\",\n            sv,\n            (const std::string_view &)sv1,\n            std::move(sv2),\n            std::string_view(\"default\"),\n            trantor::Date::now());\n        MANDATE(r.affectedRows() == 1UL);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - DbClient asynchronous interface(1) what():\",\n              e.base().what());\n    }\n    /// 3.3 query\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"select * from users where user_id=? and user_name=?\",\n            \"pg1\",\n            \"postgresql1\");\n        MANDATE(r.size() == 1UL);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - DbClient asynchronous interface(2) what():\",\n              e.base().what());\n    }\n    /// 3.4 query for none\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"select * from users where user_id=? and user_name=?\",\n            \"pg111\",\n            \"postgresql1\");\n        MANDATE(r.size() == 0UL);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - DbClient asynchronous interface(3) what():\",\n              e.base().what());\n    }\n    /// 3.5 bad sql\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"select * from users where user_id=? and user_name='1234'\",\n            \"pg111\",\n            \"postgresql1\");\n        MANDATE(r.size() == 0UL);\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n    /// 3.6 query with raw parameter\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"select * from users where length(user_id) = ?\", rawParam);\n        MANDATE(r.size() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - DbClient asynchronous interface(4) what():\",\n              e.base().what());\n    }\n    /// 3.7 clean up\n    try\n    {\n        auto r = clientPtr->execSqlSync(\"delete from users\");\n        SUCCESS();\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n    try\n    {\n        auto r = clientPtr->execSqlSync(\"UPDATE sqlite_sequence SET seq = 0\");\n        SUCCESS();\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n\n    /// Test future interface\n    /// 4.1 insert\n    auto f = clientPtr->execSqlAsyncFuture(\n        \"insert into users  (user_id,user_name,password,org_name,create_time) \"\n        \"values(?,?,?,?,?) \",\n        \"pg\",\n        \"postgresql\",\n        \"123\",\n        \"default\",\n        trantor::Date::now());\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.insertId() == 1ULL);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - DbClient future interface(0) what():\",\n              e.base().what());\n    }\n    /// 4.2 insert\n    f = clientPtr->execSqlAsyncFuture(\n        \"insert into users  (user_id,user_name,password,org_name,create_time) \"\n        \"values(?,?,?,?,?)\",\n        \"pg1\",\n        \"postgresql1\",\n        \"123\",\n        \"default\",\n        trantor::Date::now());\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.affectedRows() == 1UL);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - DbClient future interface(1) what():\",\n              e.base().what());\n    }\n    /// 4.3 query\n    f = clientPtr->execSqlAsyncFuture(\n        \"select * from users where user_id=? and user_name=?\",\n        \"pg1\",\n        \"postgresql1\");\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.size() == 1UL);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - DbClient future interface(2) what():\",\n              e.base().what());\n    }\n    /// 4.4 query for none\n    f = clientPtr->execSqlAsyncFuture(\n        \"select * from users where user_id=? and user_name=?\",\n        \"pg111\",\n        \"postgresql1\");\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.size() == 0UL);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - DbClient future interface(3) what():\",\n              e.base().what());\n    }\n    /// 4.5 bad sql\n    f = clientPtr->execSqlAsyncFuture(\n        \"select * from users where user_id=? and user_name='12'\",\n        \"pg111\",\n        \"postgresql1\");\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.size() == 0UL);\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n    /// 4.6 query with raw parameter\n    f = clientPtr->execSqlAsyncFuture(\n        \"select * from users where length(user_id)=?\", rawParam);\n    try\n    {\n        auto r = f.get();\n        MANDATE(r.size() == 1);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - DbClient future interface(4) what():\",\n              e.base().what());\n    }\n    /// 4.6 clean up\n    f = clientPtr->execSqlAsyncFuture(\"delete from users\");\n    try\n    {\n        auto r = f.get();\n        SUCCESS();\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - DbClient future interface(5.1) what():\",\n              e.base().what());\n    }\n    f = clientPtr->execSqlAsyncFuture(\"UPDATE sqlite_sequence SET seq = 0\");\n    try\n    {\n        auto r = f.get();\n        SUCCESS();\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - DbClient future interface(5.2) what():\",\n              e.base().what());\n    }\n\n    /// 5 Test Result and Row exception throwing\n    // 5.1 query for none and try to access\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"select * from users where user_id=? and user_name=?\",\n            \"pg111\",\n            \"postgresql1\");\n        r.at(0);\n        FAULT(\"sqlite3 - Result throwing exceptions(0)\");\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n    // 5.2 insert one just for setup\n    try\n    {\n        auto r = clientPtr->execSqlSync(\n            \"insert into users  \"\n            \"(user_id,user_name,password,org_name,create_time) \"\n            \"values(?,?,?,?,?)\",\n            \"pg\",\n            \"postgresql\",\n            \"123\",\n            \"default\",\n            trantor::Date::now());\n        MANDATE(r.insertId() == 1ULL);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - Row throwing exceptions(0) what():\", e.base().what());\n    }\n\n    // 5.3 try to access nonexistent column by name\n    try\n    {\n        auto r = clientPtr->execSqlSync(\"select * from users\");\n        auto row = r.at(0);\n        row[\"imaginary_column\"];\n        FAULT(\"sqlite3 - Row throwing exceptions(1)\");\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n\n    // 5.4 try to access nonexistent column by index\n    try\n    {\n        auto r = clientPtr->execSqlSync(\"select * from users\");\n        auto row = r.at(0);\n        row.at(420);\n        FAULT(\"sqlite3 - Row throwing exceptions(2)\");\n    }\n    catch (const DrogonDbException &e)\n    {\n        SUCCESS();\n    }\n\n    // 5.5 cleanup\n    try\n    {\n        auto r = clientPtr->execSqlSync(\"delete from users\");\n        SUCCESS();\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 -  Row throwing exceptions(3) what():\", e.base().what());\n    }\n    try\n    {\n        auto r = clientPtr->execSqlSync(\"UPDATE sqlite_sequence SET seq = 0\");\n        SUCCESS();\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 -  Row throwing exceptions(3) what():\", e.base().what());\n    }\n\n    /// Test ORM mapper TODO\n    /// 5.1 insert, noneblocking\n    using namespace drogon_model::sqlite3;\n    Mapper<Users> mapper(clientPtr);\n    Users user;\n    user.setUserId(\"pg\");\n    user.setUserName(\"postgres\");\n    user.setPassword(\"123\");\n    user.setOrgName(\"default\");\n    user.setCreateTime(trantor::Date::now());\n    mapper.insert(\n        user,\n        [TEST_CTX](Users ret) { MANDATE(ret.getPrimaryKey() == 1UL); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - ORM mapper asynchronous interface(0) what():\",\n                  e.base().what());\n        });\n    /// 5.2 insert\n    user.setUserId(\"pg1\");\n    user.setUserName(\"postgres1\");\n    mapper.insert(\n        user,\n        [TEST_CTX](Users ret) { MANDATE(ret.getPrimaryKey() == 2UL); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - ORM mapper asynchronous interface(1) what():\",\n                  e.base().what());\n        });\n    /// 5.3 select where in\n    mapper.findBy(\n        Criteria(Users::Cols::_id,\n                 CompareOperator::In,\n                 std::vector<int32_t>{2, 200}),\n        [TEST_CTX](std::vector<Users> users) { MANDATE(users.size() == 1UL); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - ORM mapper asynchronous interface(2) what():\",\n                  e.base().what());\n        });\n    /// 5.3.5 count\n    mapper.count(\n        Criteria(Users::Cols::_id, CompareOperator::EQ, 2),\n        [TEST_CTX](const size_t c) { MANDATE(c == 1UL); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - ORM mapper asynchronous interface(3) what():\",\n                  e.base().what());\n        });\n    /// 5.3.6 custom where query\n    mapper.findBy(\n        Criteria(\"password is not null\"_sql),\n        [TEST_CTX](std::vector<Users> users) { MANDATE(users.size() == 2); },\n        [TEST_CTX](const DrogonDbException &e) {\n            FAULT(\"sqlite3 - ORM mapper asynchronous interface(4) what():\",\n                  e.base().what());\n        });\n    /// 5.4 find by primary key. blocking\n    try\n    {\n        auto user = mapper.findByPrimaryKey(1);\n        SUCCESS();\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - ORM mapper synchronous interface(0) what():\",\n              e.base().what());\n    }\n\n    /// Test ORM QueryBuilder\n    /// execSync\n    try\n    {\n        const std::vector<Users> users =\n            QueryBuilder<Users>{}.from(\"users\").selectAll().execSync(clientPtr);\n        MANDATE(users.size() == 2);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - ORM QueryBuilder synchronous interface(0) what():\",\n              e.base().what());\n    }\n    try\n    {\n        const Result users =\n            QueryBuilder<Users>{}.from(\"users\").select(\"id\").execSync(\n                clientPtr);\n        MANDATE(users.size() == 2);\n        for (const Row &u : users)\n        {\n            MANDATE(!u[\"id\"].isNull());\n        }\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - ORM QueryBuilder synchronous interface(1) what():\",\n              e.base().what());\n    }\n    try\n    {\n        const Users user = QueryBuilder<Users>{}\n                               .from(\"users\")\n                               .selectAll()\n                               .eq(\"id\", \"2\")\n                               .limit(1)\n                               .single()\n                               .order(\"id\", false)\n                               .execSync(clientPtr);\n        MANDATE(user.getPrimaryKey() == 2);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - ORM QueryBuilder synchronous interface(2) what():\",\n              e.base().what());\n    }\n    try\n    {\n        const Row user = QueryBuilder<Users>{}\n                             .from(\"users\")\n                             .select(\"id\")\n                             .limit(1)\n                             .single()\n                             .order(\"id\", false)\n                             .execSync(clientPtr);\n        MANDATE(user[\"id\"].as<int32_t>() == 2);\n    }\n    catch (const DrogonDbException &e)\n    {\n        FAULT(\"sqlite3 - ORM QueryBuilder synchronous interface(3) what():\",\n              e.base().what());\n    }\n\n    /// execAsyncFuture\n    {\n        std::future<std::vector<Users>> users =\n            QueryBuilder<Users>{}.from(\"users\").selectAll().execAsyncFuture(\n                clientPtr);\n        try\n        {\n            const std::vector<Users> r = users.get();\n            MANDATE(r.size() == 2);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\n                \"sqlite3 - ORM QueryBuilder asynchronous interface(0) \"\n                \"what():\",\n                e.base().what());\n        }\n    }\n    {\n        std::future<Result> users =\n            QueryBuilder<Users>{}.from(\"users\").select(\"id\").execAsyncFuture(\n                clientPtr);\n        try\n        {\n            const Result r = users.get();\n            MANDATE(r.size() == 2);\n            for (const Row &u : r)\n            {\n                MANDATE(!u[\"id\"].isNull());\n            }\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\n                \"sqlite3 - ORM QueryBuilder asynchronous interface(1) \"\n                \"what():\",\n                e.base().what());\n        }\n    }\n    {\n        std::future<Users> user = QueryBuilder<Users>{}\n                                      .from(\"users\")\n                                      .selectAll()\n                                      .eq(\"id\", \"2\")\n                                      .limit(1)\n                                      .single()\n                                      .order(\"id\", false)\n                                      .execAsyncFuture(clientPtr);\n        try\n        {\n            const Users r = user.get();\n            MANDATE(r.getPrimaryKey() == 2);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\n                \"sqlite3 - ORM QueryBuilder asynchronous interface(2) \"\n                \"what():\",\n                e.base().what());\n        }\n    }\n    {\n        std::future<Row> users = QueryBuilder<Users>{}\n                                     .from(\"users\")\n                                     .select(\"id\")\n                                     .limit(1)\n                                     .single()\n                                     .order(\"id\", false)\n                                     .execAsyncFuture(clientPtr);\n        try\n        {\n            const Row r = users.get();\n            MANDATE(r[\"id\"].as<int32_t>() == 2);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\n                \"sqlite3 - ORM QueryBuilder asynchronous interface(3) \"\n                \"what():\",\n                e.base().what());\n        }\n    }\n\n#ifdef __cpp_impl_coroutine\n    auto coro_test = [clientPtr, TEST_CTX]() -> drogon::Task<> {\n        /// 7 Test coroutines.\n        /// This is by no means comprehensive. But coroutine API is essentially\n        /// a wrapper around callbacks. The purpose is to test the interface\n        /// works 7.1 Basic queries\n        try\n        {\n            auto result =\n                co_await clientPtr->execSqlCoro(\"select * from users;\");\n            MANDATE(result.size() != 0UL);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - DbClient coroutine interface(0) what():\",\n                  e.base().what());\n        }\n        /// 7.2 Parameter binding\n        try\n        {\n            auto result = co_await clientPtr->execSqlCoro(\n                \"select * from users where 1=?;\", 1);\n            MANDATE(result.size() != 0UL);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - DbClient coroutine interface(1) what():\",\n                  e.base().what());\n        }\n        /// 7.3 ORM CoroMapper\n        try\n        {\n            auto mapper = CoroMapper<Users>(clientPtr);\n            auto user = co_await mapper.findOne(\n                Criteria(Users::Cols::_id, CompareOperator::EQ, 1));\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - CoroMapper coroutine interface(0) what():\",\n                  e.base().what());\n        }\n        try\n        {\n            auto mapper = CoroMapper<Users>(clientPtr);\n            auto users = co_await mapper.findBy(\n                Criteria(Users::Cols::_id, CompareOperator::EQ, 1));\n            MANDATE(users.size() == 1UL);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - CoroMapper coroutine interface(1) what():\",\n                  e.base().what());\n        }\n        try\n        {\n            auto mapper = CoroMapper<Users>(clientPtr);\n            auto n = co_await mapper.deleteByPrimaryKey(1);\n            MANDATE(n == 1);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - CoroMapper coroutine interface(2) what():\",\n                  e.base().what());\n        }\n        co_await drogon::sleepCoro(\n            trantor::EventLoop::getEventLoopOfCurrentThread(), 1.0s);\n    };\n    drogon::sync_wait(coro_test());\n\n#endif\n\n    /// 8 Test ORM related query\n    /// 8.1 async\n    /// 8.1.1 one-to-one\n    Mapper<Wallets> walletsMapper(clientPtr);\n\n    /// prepare\n    {\n        Wallets wallet;\n        wallet.setUserId(\"pg\");\n        wallet.setAmount(\"2000.00\");\n        try\n        {\n            walletsMapper.insert(wallet);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - ORM mapper related prepare(0) what():\",\n                  e.base().what());\n        }\n    }\n\n    /// users to wallets\n    {\n        Users user;\n        user.setUserId(\"pg\");\n        user.getWallet(\n            clientPtr,\n            [TEST_CTX](Wallets r) { MANDATE(r.getValueOfAmount() == \"2000\"); },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\n                    \"sqlite3 - ORM mapper related async one-to-one(0) what(): \",\n                    e.base().what());\n            });\n    }\n    /// users to wallets without data\n    {\n        Users user;\n        user.setUserId(\"pg1\");\n        user.getWallet(\n            clientPtr,\n            [TEST_CTX](Wallets w) {\n                FAULT(\"sqlite3 - ORM mapper related async one-to-one(1)\");\n            },\n            [TEST_CTX](const DrogonDbException &e) { SUCCESS(); });\n    }\n\n    /// 8.1.2 one-to-many\n    Mapper<Category> categoryMapper(clientPtr);\n    Mapper<Blog> blogMapper(clientPtr);\n\n    /// prepare\n    {\n        Category category;\n        category.setName(\"category1\");\n        try\n        {\n            categoryMapper.insert(category);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - ORM mapper related prepare(1) what():\",\n                  e.base().what());\n        }\n        category.setName(\"category2\");\n        try\n        {\n            categoryMapper.insert(category);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - ORM mapper related prepare(2) what():\",\n                  e.base().what());\n        }\n        Blog blog;\n        blog.setTitle(\"title1\");\n        blog.setCategoryId(1);\n        try\n        {\n            blogMapper.insert(blog);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - ORM mapper related prepare(3) what():\",\n                  e.base().what());\n        }\n        blog.setTitle(\"title2\");\n        blog.setCategoryId(1);\n        try\n        {\n            blogMapper.insert(blog);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - ORM mapper related prepare(4) what():\",\n                  e.base().what());\n        }\n        blog.setTitle(\"title3\");\n        blog.setCategoryId(3);\n        try\n        {\n            blogMapper.insert(blog);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - ORM mapper related prepare(5) what():\",\n                  e.base().what());\n        }\n    }\n\n    /// categories to blogs\n    {\n        Category category;\n        category.setId(1);\n        category.getBlogs(\n            clientPtr,\n            [TEST_CTX](std::vector<Blog> r) { MANDATE(r.size() == 2); },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\n                    \"sqlite3 - ORM mapper related async one-to-many(0) \"\n                    \"what(): \",\n                    e.base().what());\n            });\n    }\n    /// categories to blogs without data\n    {\n        Category category;\n        category.setId(2);\n        category.getBlogs(\n            clientPtr,\n            [TEST_CTX](std::vector<Blog> r) { MANDATE(r.size() == 0); },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\n                    \"sqlite3 - ORM mapper related async one-to-many(1) \"\n                    \"what(): \",\n                    e.base().what());\n            });\n    }\n    /// blogs to categories\n    {\n        Blog blog;\n        blog.setCategoryId(1);\n        blog.getCategory(\n            clientPtr,\n            [TEST_CTX](Category r) {\n                MANDATE(r.getValueOfName() == \"category1\");\n            },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\n                    \"sqlite3 - ORM mapper related async one-to-many(2) \"\n                    \"what(): \",\n                    e.base().what());\n            });\n    }\n    /// blogs to categories without data\n    {\n        Blog blog;\n        blog.setCategoryId(3);\n        blog.getCategory(\n            clientPtr,\n            [TEST_CTX](Category r) {\n                FAULT(\"sqlite3 - ORM mapper related async one-to-many(3)\");\n            },\n            [TEST_CTX](const DrogonDbException &e) { SUCCESS(); });\n    }\n\n    /// 8.1.3 many-to-many\n    Mapper<BlogTag> blogTagMapper(clientPtr);\n    Mapper<Tag> tagMapper(clientPtr);\n\n    /// prepare\n    {\n        BlogTag blogTag;\n        blogTag.setBlogId(1);\n        blogTag.setTagId(1);\n        try\n        {\n            blogTagMapper.insert(blogTag);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - ORM mapper related prepare(6) what():\",\n                  e.base().what());\n        }\n        blogTag.setBlogId(1);\n        blogTag.setTagId(2);\n        try\n        {\n            blogTagMapper.insert(blogTag);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - ORM mapper related prepare(7) what():\",\n                  e.base().what());\n        }\n        Tag tag;\n        tag.setName(\"tag1\");\n        try\n        {\n            tagMapper.insert(tag);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - ORM mapper related prepare(8) what():\",\n                  e.base().what());\n        }\n        tag.setName(\"tag2\");\n        try\n        {\n            tagMapper.insert(tag);\n            SUCCESS();\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - ORM mapper related prepare(9) what():\",\n                  e.base().what());\n        }\n    }\n\n    /// blogs to tags\n    {\n        Blog blog;\n        blog.setId(1);\n        blog.getTags(\n            clientPtr,\n            [TEST_CTX](std::vector<std::pair<Tag, BlogTag>> r) {\n                MANDATE(r.size() == 2);\n            },\n            [TEST_CTX](const DrogonDbException &e) {\n                FAULT(\n                    \"sqlite3 - ORM mapper related async many-to-many(0) \"\n                    \"what():\",\n                    e.base().what());\n            });\n    }\n\n    /// 8.2 async\n    /// 8.2.1 one-to-one\n    /// users to wallets\n    {\n        Users user;\n        user.setUserId(\"pg\");\n        try\n        {\n            auto r = user.getWallet(clientPtr);\n            MANDATE(r.getValueOfAmount() == \"2000\");\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - ORM mapper related sync one-to-one(0) what():\",\n                  e.base().what());\n        }\n    }\n    /// users to wallets without data\n    {\n        Users user;\n        user.setUserId(\"pg1\");\n        try\n        {\n            auto r = user.getWallet(clientPtr);\n            FAULT(\"sqlite3 - ORM mapper related sync one-to-one(1)\");\n        }\n        catch (const DrogonDbException &e)\n        {\n            SUCCESS();\n        }\n    }\n\n    /// 8.2.2 one-to-many\n    /// categories to blogs\n    {\n        Category category;\n        category.setId(1);\n        try\n        {\n            auto r = category.getBlogs(clientPtr);\n            MANDATE(r.size() == 2);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - ORM mapper related sync one-to-many(0) what():\",\n                  e.base().what());\n        }\n    }\n    /// categories to blogs without data\n    {\n        Category category;\n        category.setId(2);\n        try\n        {\n            auto r = category.getBlogs(clientPtr);\n            MANDATE(r.size() == 0);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - ORM mapper related sync one-to-many(1) what():\",\n                  e.base().what());\n        }\n    }\n    /// blogs to categories\n    {\n        Blog blog;\n        blog.setCategoryId(1);\n        try\n        {\n            auto r = blog.getCategory(clientPtr);\n            MANDATE(r.getValueOfName() == \"category1\");\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - ORM mapper related sync one-to-many(2) what():\",\n                  e.base().what());\n        }\n    }\n    /// blogs to categories without data\n    {\n        Blog blog;\n        blog.setCategoryId(3);\n        try\n        {\n            auto r = blog.getCategory(clientPtr);\n            FAULT(\"sqlite3 - ORM mapper related sync one-to-many(3)\");\n        }\n        catch (const DrogonDbException &e)\n        {\n            SUCCESS();\n        }\n    }\n\n    /// 8.2.3 many-to-many\n    /// blogs to tags\n    {\n        Blog blog;\n        blog.setId(1);\n        try\n        {\n            auto r = blog.getTags(clientPtr);\n            MANDATE(r.size() == 2);\n        }\n        catch (const DrogonDbException &e)\n        {\n            FAULT(\"sqlite3 - ORM mapper related sync many-to-many(0) what():\",\n                  e.base().what());\n        }\n    }\n}\n#endif\n\nusing namespace drogon;\n\nint main(int argc, char **argv)\n{\n    trantor::Logger::setLogLevel(trantor::Logger::LogLevel::kDebug);\n\n#if USE_MYSQL\n    mysqlClient = DbClient::newMysqlClient(\n        \"host=127.0.0.1 port=3306 user=root client_encoding=utf8mb4\", 1);\n#endif\n#if USE_POSTGRESQL\n    postgreClient = DbClient::newPgClient(\n        \"host=127.0.0.1 port=5432 dbname=postgres user=postgres password=12345 \"\n        \"client_encoding=utf8\",\n        1,\n        true);\n#endif\n#if USE_SQLITE3\n    sqlite3Client = DbClient::newSqlite3Client(\"filename=:memory:\", 1);\n#endif\n    const int testStatus = test::run(argc, argv);\n    return testStatus;\n}\n"
  },
  {
    "path": "orm_lib/tests/mysql/Blog.cc",
    "content": "/**\n *\n *  Blog.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Blog.h\"\n#include \"BlogTag.h\"\n#include \"Category.h\"\n#include \"Tag.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::drogonTestMysql;\n\nconst std::string Blog::Cols::_id = \"id\";\nconst std::string Blog::Cols::_title = \"title\";\nconst std::string Blog::Cols::_category_id = \"category_id\";\nconst std::string Blog::primaryKeyName = \"id\";\nconst bool Blog::hasPrimaryKey = true;\nconst std::string Blog::tableName = \"blog\";\n\nconst std::vector<typename Blog::MetaData> Blog::metaData_ = {\n    {\"id\", \"int32_t\", \"int(11)\", 4, 1, 1, 1},\n    {\"title\", \"std::string\", \"varchar(30)\", 30, 0, 0, 0},\n    {\"category_id\", \"int32_t\", \"int(11)\", 4, 0, 0, 0}};\n\nconst std::string &Blog::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nBlog::Blog(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[\"id\"].as<int32_t>());\n        }\n        if (!r[\"title\"].isNull())\n        {\n            title_ =\n                std::make_shared<std::string>(r[\"title\"].as<std::string>());\n        }\n        if (!r[\"category_id\"].isNull())\n        {\n            categoryId_ =\n                std::make_shared<int32_t>(r[\"category_id\"].as<int32_t>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 3 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            title_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 2;\n        if (!r[index].isNull())\n        {\n            categoryId_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n    }\n}\n\nBlog::Blog(const Json::Value &pJson,\n           const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            title_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            categoryId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[2]].asInt64());\n        }\n    }\n}\n\nBlog::Blog(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"title\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"title\"].isNull())\n        {\n            title_ = std::make_shared<std::string>(pJson[\"title\"].asString());\n        }\n    }\n    if (pJson.isMember(\"category_id\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"category_id\"].isNull())\n        {\n            categoryId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[\"category_id\"].asInt64());\n        }\n    }\n}\n\nvoid Blog::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            title_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            categoryId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[2]].asInt64());\n        }\n    }\n}\n\nvoid Blog::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"title\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"title\"].isNull())\n        {\n            title_ = std::make_shared<std::string>(pJson[\"title\"].asString());\n        }\n    }\n    if (pJson.isMember(\"category_id\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"category_id\"].isNull())\n        {\n            categoryId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[\"category_id\"].asInt64());\n        }\n    }\n}\n\nconst int32_t &Blog::getValueOfId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (id_)\n        return *id_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &Blog::getId() const noexcept\n{\n    return id_;\n}\n\nvoid Blog::setId(const int32_t &pId) noexcept\n{\n    id_ = std::make_shared<int32_t>(pId);\n    dirtyFlag_[0] = true;\n}\n\nconst typename Blog::PrimaryKeyType &Blog::getPrimaryKey() const\n{\n    assert(id_);\n    return *id_;\n}\n\nconst std::string &Blog::getValueOfTitle() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (title_)\n        return *title_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Blog::getTitle() const noexcept\n{\n    return title_;\n}\n\nvoid Blog::setTitle(const std::string &pTitle) noexcept\n{\n    title_ = std::make_shared<std::string>(pTitle);\n    dirtyFlag_[1] = true;\n}\n\nvoid Blog::setTitle(std::string &&pTitle) noexcept\n{\n    title_ = std::make_shared<std::string>(std::move(pTitle));\n    dirtyFlag_[1] = true;\n}\n\nvoid Blog::setTitleToNull() noexcept\n{\n    title_.reset();\n    dirtyFlag_[1] = true;\n}\n\nconst int32_t &Blog::getValueOfCategoryId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (categoryId_)\n        return *categoryId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &Blog::getCategoryId() const noexcept\n{\n    return categoryId_;\n}\n\nvoid Blog::setCategoryId(const int32_t &pCategoryId) noexcept\n{\n    categoryId_ = std::make_shared<int32_t>(pCategoryId);\n    dirtyFlag_[2] = true;\n}\n\nvoid Blog::setCategoryIdToNull() noexcept\n{\n    categoryId_.reset();\n    dirtyFlag_[2] = true;\n}\n\nvoid Blog::updateId(const uint64_t id)\n{\n    id_ = std::make_shared<int32_t>(static_cast<int32_t>(id));\n}\n\nconst std::vector<std::string> &Blog::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"title\", \"category_id\"};\n    return inCols;\n}\n\nvoid Blog::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getTitle())\n        {\n            binder << getValueOfTitle();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getCategoryId())\n        {\n            binder << getValueOfCategoryId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Blog::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    if (dirtyFlag_[2])\n    {\n        ret.push_back(getColumnName(2));\n    }\n    return ret;\n}\n\nvoid Blog::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getTitle())\n        {\n            binder << getValueOfTitle();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getCategoryId())\n        {\n            binder << getValueOfCategoryId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Blog::toJson() const\n{\n    Json::Value ret;\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getTitle())\n    {\n        ret[\"title\"] = getValueOfTitle();\n    }\n    else\n    {\n        ret[\"title\"] = Json::Value();\n    }\n    if (getCategoryId())\n    {\n        ret[\"category_id\"] = getValueOfCategoryId();\n    }\n    else\n    {\n        ret[\"category_id\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Blog::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 3)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getId())\n            {\n                ret[pMasqueradingVector[0]] = getValueOfId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getTitle())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfTitle();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (getCategoryId())\n            {\n                ret[pMasqueradingVector[2]] = getValueOfCategoryId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[2]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getTitle())\n    {\n        ret[\"title\"] = getValueOfTitle();\n    }\n    else\n    {\n        ret[\"title\"] = Json::Value();\n    }\n    if (getCategoryId())\n    {\n        ret[\"category_id\"] = getValueOfCategoryId();\n    }\n    else\n    {\n        ret[\"category_id\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Blog::validateJsonForCreation(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"title\"))\n    {\n        if (!validJsonOfField(1, \"title\", pJson[\"title\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"category_id\"))\n    {\n        if (!validJsonOfField(\n                2, \"category_id\", pJson[\"category_id\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Blog::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[2]))\n            {\n                if (!validJsonOfField(2,\n                                      pMasqueradingVector[2],\n                                      pJson[pMasqueradingVector[2]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Blog::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"title\"))\n    {\n        if (!validJsonOfField(1, \"title\", pJson[\"title\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"category_id\"))\n    {\n        if (!validJsonOfField(\n                2, \"category_id\", pJson[\"category_id\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Blog::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[2].empty() &&\n            pJson.isMember(pMasqueradingVector[2]))\n        {\n            if (!validJsonOfField(2,\n                                  pMasqueradingVector[2],\n                                  pJson[pMasqueradingVector[2]],\n                                  err,\n                                  false))\n                return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Blog::validJsonOfField(size_t index,\n                            const std::string &fieldName,\n                            const Json::Value &pJson,\n                            std::string &err,\n                            bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (isForCreation)\n            {\n                err = \"The automatic primary key cannot be set\";\n                return false;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 30)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 30)\";\n                return false;\n            }\n\n            break;\n        case 2:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n\nCategory Blog::getCategory(const DbClientPtr &clientPtr) const\n{\n    static const std::string sql = \"select * from category where id = ?\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *categoryId_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    if (r.size() == 0)\n    {\n        throw UnexpectedRows(\"0 rows found\");\n    }\n    else if (r.size() > 1)\n    {\n        throw UnexpectedRows(\"Found more than one row\");\n    }\n    return Category(r[0]);\n}\n\nvoid Blog::getCategory(const DbClientPtr &clientPtr,\n                       const std::function<void(Category)> &rcb,\n                       const ExceptionCallback &ecb) const\n{\n    static const std::string sql = \"select * from category where id = ?\";\n    *clientPtr << sql << *categoryId_ >> [rcb = std::move(rcb),\n                                          ecb](const Result &r) {\n        if (r.size() == 0)\n        {\n            ecb(UnexpectedRows(\"0 rows found\"));\n        }\n        else if (r.size() > 1)\n        {\n            ecb(UnexpectedRows(\"Found more than one row\"));\n        }\n        else\n        {\n            rcb(Category(r[0]));\n        }\n    } >> ecb;\n}\n\nstd::vector<std::pair<Tag, BlogTag>> Blog::getTags(\n    const DbClientPtr &clientPtr) const\n{\n    static const std::string sql =\n        \"select * from tag,blog_tag where blog_tag.blog_id = ? and \"\n        \"blog_tag.tag_id = tag.id\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *id_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    std::vector<std::pair<Tag, BlogTag>> ret;\n    ret.reserve(r.size());\n    for (auto const &row : r)\n    {\n        ret.emplace_back(\n            std::pair<Tag, BlogTag>(Tag(row),\n                                    BlogTag(row, Tag::getColumnNumber())));\n    }\n    return ret;\n}\n\nvoid Blog::getTags(\n    const DbClientPtr &clientPtr,\n    const std::function<void(std::vector<std::pair<Tag, BlogTag>>)> &rcb,\n    const ExceptionCallback &ecb) const\n{\n    static const std::string sql =\n        \"select * from tag,blog_tag where blog_tag.blog_id = ? and \"\n        \"blog_tag.tag_id = tag.id\";\n    *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) {\n        std::vector<std::pair<Tag, BlogTag>> ret;\n        ret.reserve(r.size());\n        for (auto const &row : r)\n        {\n            ret.emplace_back(\n                std::pair<Tag, BlogTag>(Tag(row),\n                                        BlogTag(row, Tag::getColumnNumber())));\n        }\n        rcb(ret);\n    } >> ecb;\n}\n"
  },
  {
    "path": "orm_lib/tests/mysql/Blog.h",
    "content": "/**\n *\n *  Blog.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace drogonTestMysql\n{\nclass BlogTag;\nclass Category;\nclass Tag;\n\nclass Blog\n{\n  public:\n    struct Cols\n    {\n        static const std::string _id;\n        static const std::string _title;\n        static const std::string _category_id;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = int32_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Blog(const drogon::orm::Row &r,\n                  const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Blog(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Blog(const Json::Value &pJson,\n         const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Blog() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column id  */\n    /// Get the value of the column id, returns the default value if the column\n    /// is null\n    const int32_t &getValueOfId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getId() const noexcept;\n    /// Set the value of the column id\n    void setId(const int32_t &pId) noexcept;\n\n    /**  For column title  */\n    /// Get the value of the column title, returns the default value if the\n    /// column is null\n    const std::string &getValueOfTitle() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getTitle() const noexcept;\n    /// Set the value of the column title\n    void setTitle(const std::string &pTitle) noexcept;\n    void setTitle(std::string &&pTitle) noexcept;\n    void setTitleToNull() noexcept;\n\n    /**  For column category_id  */\n    /// Get the value of the column category_id, returns the default value if\n    /// the column is null\n    const int32_t &getValueOfCategoryId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getCategoryId() const noexcept;\n    /// Set the value of the column category_id\n    void setCategoryId(const int32_t &pCategoryId) noexcept;\n    void setCategoryIdToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 3;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n    Category getCategory(const drogon::orm::DbClientPtr &clientPtr) const;\n    void getCategory(const drogon::orm::DbClientPtr &clientPtr,\n                     const std::function<void(Category)> &rcb,\n                     const drogon::orm::ExceptionCallback &ecb) const;\n    std::vector<std::pair<Tag, BlogTag>> getTags(\n        const drogon::orm::DbClientPtr &clientPtr) const;\n    void getTags(\n        const drogon::orm::DbClientPtr &clientPtr,\n        const std::function<void(std::vector<std::pair<Tag, BlogTag>>)> &rcb,\n        const drogon::orm::ExceptionCallback &ecb) const;\n\n  private:\n    friend drogon::orm::Mapper<Blog>;\n    friend drogon::orm::BaseBuilder<Blog, true, true>;\n    friend drogon::orm::BaseBuilder<Blog, true, false>;\n    friend drogon::orm::BaseBuilder<Blog, false, true>;\n    friend drogon::orm::BaseBuilder<Blog, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<Blog>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int32_t> id_;\n    std::shared_ptr<std::string> title_;\n    std::shared_ptr<int32_t> categoryId_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[3] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        sql += \"id,\";\n        ++parametersCount;\n        if (dirtyFlag_[1])\n        {\n            sql += \"title,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[2])\n        {\n            sql += \"category_id,\";\n            ++parametersCount;\n        }\n        needSelection = true;\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        sql += \"default,\";\n        if (dirtyFlag_[1])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[2])\n        {\n            sql.append(\"?,\");\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        sql.append(1, ')');\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace drogonTestMysql\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/mysql/BlogTag.cc",
    "content": "/**\n *\n *  BlogTag.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"BlogTag.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::drogonTestMysql;\n\nconst std::string BlogTag::Cols::_blog_id = \"blog_id\";\nconst std::string BlogTag::Cols::_tag_id = \"tag_id\";\nconst std::vector<std::string> BlogTag::primaryKeyName = {\"blog_id\", \"tag_id\"};\nconst bool BlogTag::hasPrimaryKey = true;\nconst std::string BlogTag::tableName = \"blog_tag\";\n\nconst std::vector<typename BlogTag::MetaData> BlogTag::metaData_ = {\n    {\"blog_id\", \"int32_t\", \"int(11)\", 4, 0, 1, 1},\n    {\"tag_id\", \"int32_t\", \"int(11)\", 4, 0, 1, 1}};\n\nconst std::string &BlogTag::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nBlogTag::BlogTag(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"blog_id\"].isNull())\n        {\n            blogId_ = std::make_shared<int32_t>(r[\"blog_id\"].as<int32_t>());\n        }\n        if (!r[\"tag_id\"].isNull())\n        {\n            tagId_ = std::make_shared<int32_t>(r[\"tag_id\"].as<int32_t>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 2 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            blogId_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            tagId_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n    }\n}\n\nBlogTag::BlogTag(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            blogId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            tagId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[1]].asInt64());\n        }\n    }\n}\n\nBlogTag::BlogTag(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"blog_id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"blog_id\"].isNull())\n        {\n            blogId_ =\n                std::make_shared<int32_t>((int32_t)pJson[\"blog_id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"tag_id\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"tag_id\"].isNull())\n        {\n            tagId_ =\n                std::make_shared<int32_t>((int32_t)pJson[\"tag_id\"].asInt64());\n        }\n    }\n}\n\nvoid BlogTag::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            blogId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            tagId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[1]].asInt64());\n        }\n    }\n}\n\nvoid BlogTag::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"blog_id\"))\n    {\n        if (!pJson[\"blog_id\"].isNull())\n        {\n            blogId_ =\n                std::make_shared<int32_t>((int32_t)pJson[\"blog_id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"tag_id\"))\n    {\n        if (!pJson[\"tag_id\"].isNull())\n        {\n            tagId_ =\n                std::make_shared<int32_t>((int32_t)pJson[\"tag_id\"].asInt64());\n        }\n    }\n}\n\nconst int32_t &BlogTag::getValueOfBlogId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (blogId_)\n        return *blogId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &BlogTag::getBlogId() const noexcept\n{\n    return blogId_;\n}\n\nvoid BlogTag::setBlogId(const int32_t &pBlogId) noexcept\n{\n    blogId_ = std::make_shared<int32_t>(pBlogId);\n    dirtyFlag_[0] = true;\n}\n\nconst int32_t &BlogTag::getValueOfTagId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (tagId_)\n        return *tagId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &BlogTag::getTagId() const noexcept\n{\n    return tagId_;\n}\n\nvoid BlogTag::setTagId(const int32_t &pTagId) noexcept\n{\n    tagId_ = std::make_shared<int32_t>(pTagId);\n    dirtyFlag_[1] = true;\n}\n\nvoid BlogTag::updateId(const uint64_t id)\n{\n}\n\ntypename BlogTag::PrimaryKeyType BlogTag::getPrimaryKey() const\n{\n    return std::make_tuple(*blogId_, *tagId_);\n}\n\nconst std::vector<std::string> &BlogTag::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"blog_id\", \"tag_id\"};\n    return inCols;\n}\n\nvoid BlogTag::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[0])\n    {\n        if (getBlogId())\n        {\n            binder << getValueOfBlogId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[1])\n    {\n        if (getTagId())\n        {\n            binder << getValueOfTagId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> BlogTag::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[0])\n    {\n        ret.push_back(getColumnName(0));\n    }\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    return ret;\n}\n\nvoid BlogTag::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[0])\n    {\n        if (getBlogId())\n        {\n            binder << getValueOfBlogId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[1])\n    {\n        if (getTagId())\n        {\n            binder << getValueOfTagId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value BlogTag::toJson() const\n{\n    Json::Value ret;\n    if (getBlogId())\n    {\n        ret[\"blog_id\"] = getValueOfBlogId();\n    }\n    else\n    {\n        ret[\"blog_id\"] = Json::Value();\n    }\n    if (getTagId())\n    {\n        ret[\"tag_id\"] = getValueOfTagId();\n    }\n    else\n    {\n        ret[\"tag_id\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value BlogTag::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 2)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getBlogId())\n            {\n                ret[pMasqueradingVector[0]] = getValueOfBlogId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getTagId())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfTagId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getBlogId())\n    {\n        ret[\"blog_id\"] = getValueOfBlogId();\n    }\n    else\n    {\n        ret[\"blog_id\"] = Json::Value();\n    }\n    if (getTagId())\n    {\n        ret[\"tag_id\"] = getValueOfTagId();\n    }\n    else\n    {\n        ret[\"tag_id\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool BlogTag::validateJsonForCreation(const Json::Value &pJson,\n                                      std::string &err)\n{\n    if (pJson.isMember(\"blog_id\"))\n    {\n        if (!validJsonOfField(0, \"blog_id\", pJson[\"blog_id\"], err, true))\n            return false;\n    }\n    else\n    {\n        err = \"The blog_id column cannot be null\";\n        return false;\n    }\n    if (pJson.isMember(\"tag_id\"))\n    {\n        if (!validJsonOfField(1, \"tag_id\", pJson[\"tag_id\"], err, true))\n            return false;\n    }\n    else\n    {\n        err = \"The tag_id column cannot be null\";\n        return false;\n    }\n    return true;\n}\n\nbool BlogTag::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n            else\n            {\n                err =\n                    \"The \" + pMasqueradingVector[0] + \" column cannot be null\";\n                return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n            else\n            {\n                err =\n                    \"The \" + pMasqueradingVector[1] + \" column cannot be null\";\n                return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool BlogTag::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"blog_id\"))\n    {\n        if (!validJsonOfField(0, \"blog_id\", pJson[\"blog_id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"tag_id\"))\n    {\n        if (!validJsonOfField(1, \"tag_id\", pJson[\"tag_id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    return true;\n}\n\nbool BlogTag::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool BlogTag::validJsonOfField(size_t index,\n                               const std::string &fieldName,\n                               const Json::Value &pJson,\n                               std::string &err,\n                               bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n"
  },
  {
    "path": "orm_lib/tests/mysql/BlogTag.h",
    "content": "/**\n *\n *  BlogTag.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace drogonTestMysql\n{\n\nclass BlogTag\n{\n  public:\n    struct Cols\n    {\n        static const std::string _blog_id;\n        static const std::string _tag_id;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::vector<std::string> primaryKeyName;\n    using PrimaryKeyType = std::tuple<int32_t, int32_t>;  // blog_id,tag_id\n    PrimaryKeyType getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit BlogTag(const drogon::orm::Row &r,\n                     const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit BlogTag(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    BlogTag(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    BlogTag() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column blog_id  */\n    /// Get the value of the column blog_id, returns the default value if the\n    /// column is null\n    const int32_t &getValueOfBlogId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getBlogId() const noexcept;\n    /// Set the value of the column blog_id\n    void setBlogId(const int32_t &pBlogId) noexcept;\n\n    /**  For column tag_id  */\n    /// Get the value of the column tag_id, returns the default value if the\n    /// column is null\n    const int32_t &getValueOfTagId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getTagId() const noexcept;\n    /// Set the value of the column tag_id\n    void setTagId(const int32_t &pTagId) noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 2;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n  private:\n    friend drogon::orm::Mapper<BlogTag>;\n    friend drogon::orm::BaseBuilder<BlogTag, true, true>;\n    friend drogon::orm::BaseBuilder<BlogTag, true, false>;\n    friend drogon::orm::BaseBuilder<BlogTag, false, true>;\n    friend drogon::orm::BaseBuilder<BlogTag, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<BlogTag>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int32_t> blogId_;\n    std::shared_ptr<int32_t> tagId_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[2] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where blog_id = ? and tag_id = ?\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where blog_id = ? and tag_id = ?\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        if (dirtyFlag_[0])\n        {\n            sql += \"blog_id,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[1])\n        {\n            sql += \"tag_id,\";\n            ++parametersCount;\n        }\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        if (dirtyFlag_[0])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[1])\n        {\n            sql.append(\"?,\");\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        sql.append(1, ')');\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace drogonTestMysql\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/mysql/Category.cc",
    "content": "/**\n *\n *  Category.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Category.h\"\n#include \"Blog.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::drogonTestMysql;\n\nconst std::string Category::Cols::_id = \"id\";\nconst std::string Category::Cols::_name = \"name\";\nconst std::string Category::primaryKeyName = \"id\";\nconst bool Category::hasPrimaryKey = true;\nconst std::string Category::tableName = \"category\";\n\nconst std::vector<typename Category::MetaData> Category::metaData_ = {\n    {\"id\", \"int32_t\", \"int(11)\", 4, 1, 1, 1},\n    {\"name\", \"std::string\", \"varchar(30)\", 30, 0, 0, 0}};\n\nconst std::string &Category::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nCategory::Category(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[\"id\"].as<int32_t>());\n        }\n        if (!r[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(r[\"name\"].as<std::string>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 2 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            name_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n    }\n}\n\nCategory::Category(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            name_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n}\n\nCategory::Category(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(pJson[\"name\"].asString());\n        }\n    }\n}\n\nvoid Category::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            name_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n}\n\nvoid Category::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(pJson[\"name\"].asString());\n        }\n    }\n}\n\nconst int32_t &Category::getValueOfId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (id_)\n        return *id_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &Category::getId() const noexcept\n{\n    return id_;\n}\n\nvoid Category::setId(const int32_t &pId) noexcept\n{\n    id_ = std::make_shared<int32_t>(pId);\n    dirtyFlag_[0] = true;\n}\n\nconst typename Category::PrimaryKeyType &Category::getPrimaryKey() const\n{\n    assert(id_);\n    return *id_;\n}\n\nconst std::string &Category::getValueOfName() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (name_)\n        return *name_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Category::getName() const noexcept\n{\n    return name_;\n}\n\nvoid Category::setName(const std::string &pName) noexcept\n{\n    name_ = std::make_shared<std::string>(pName);\n    dirtyFlag_[1] = true;\n}\n\nvoid Category::setName(std::string &&pName) noexcept\n{\n    name_ = std::make_shared<std::string>(std::move(pName));\n    dirtyFlag_[1] = true;\n}\n\nvoid Category::setNameToNull() noexcept\n{\n    name_.reset();\n    dirtyFlag_[1] = true;\n}\n\nvoid Category::updateId(const uint64_t id)\n{\n    id_ = std::make_shared<int32_t>(static_cast<int32_t>(id));\n}\n\nconst std::vector<std::string> &Category::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"name\"};\n    return inCols;\n}\n\nvoid Category::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getName())\n        {\n            binder << getValueOfName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Category::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    return ret;\n}\n\nvoid Category::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getName())\n        {\n            binder << getValueOfName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Category::toJson() const\n{\n    Json::Value ret;\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getName())\n    {\n        ret[\"name\"] = getValueOfName();\n    }\n    else\n    {\n        ret[\"name\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Category::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 2)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getId())\n            {\n                ret[pMasqueradingVector[0]] = getValueOfId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getName())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfName();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getName())\n    {\n        ret[\"name\"] = getValueOfName();\n    }\n    else\n    {\n        ret[\"name\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Category::validateJsonForCreation(const Json::Value &pJson,\n                                       std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        if (!validJsonOfField(1, \"name\", pJson[\"name\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Category::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Category::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        if (!validJsonOfField(1, \"name\", pJson[\"name\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Category::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Category::validJsonOfField(size_t index,\n                                const std::string &fieldName,\n                                const Json::Value &pJson,\n                                std::string &err,\n                                bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (isForCreation)\n            {\n                err = \"The automatic primary key cannot be set\";\n                return false;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 30)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 30)\";\n                return false;\n            }\n\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n\nstd::vector<Blog> Category::getBlogs(const DbClientPtr &clientPtr) const\n{\n    static const std::string sql = \"select * from blog where category_id = ?\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *id_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    std::vector<Blog> ret;\n    ret.reserve(r.size());\n    for (auto const &row : r)\n    {\n        ret.emplace_back(Blog(row));\n    }\n    return ret;\n}\n\nvoid Category::getBlogs(const DbClientPtr &clientPtr,\n                        const std::function<void(std::vector<Blog>)> &rcb,\n                        const ExceptionCallback &ecb) const\n{\n    static const std::string sql = \"select * from blog where category_id = ?\";\n    *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) {\n        std::vector<Blog> ret;\n        ret.reserve(r.size());\n        for (auto const &row : r)\n        {\n            ret.emplace_back(Blog(row));\n        }\n        rcb(ret);\n    } >> ecb;\n}\n"
  },
  {
    "path": "orm_lib/tests/mysql/Category.h",
    "content": "/**\n *\n *  Category.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace drogonTestMysql\n{\nclass Blog;\n\nclass Category\n{\n  public:\n    struct Cols\n    {\n        static const std::string _id;\n        static const std::string _name;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = int32_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Category(const drogon::orm::Row &r,\n                      const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Category(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Category(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Category() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column id  */\n    /// Get the value of the column id, returns the default value if the column\n    /// is null\n    const int32_t &getValueOfId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getId() const noexcept;\n    /// Set the value of the column id\n    void setId(const int32_t &pId) noexcept;\n\n    /**  For column name  */\n    /// Get the value of the column name, returns the default value if the\n    /// column is null\n    const std::string &getValueOfName() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getName() const noexcept;\n    /// Set the value of the column name\n    void setName(const std::string &pName) noexcept;\n    void setName(std::string &&pName) noexcept;\n    void setNameToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 2;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n    std::vector<Blog> getBlogs(const drogon::orm::DbClientPtr &clientPtr) const;\n    void getBlogs(const drogon::orm::DbClientPtr &clientPtr,\n                  const std::function<void(std::vector<Blog>)> &rcb,\n                  const drogon::orm::ExceptionCallback &ecb) const;\n\n  private:\n    friend drogon::orm::Mapper<Category>;\n    friend drogon::orm::BaseBuilder<Category, true, true>;\n    friend drogon::orm::BaseBuilder<Category, true, false>;\n    friend drogon::orm::BaseBuilder<Category, false, true>;\n    friend drogon::orm::BaseBuilder<Category, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<Category>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int32_t> id_;\n    std::shared_ptr<std::string> name_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[2] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        sql += \"id,\";\n        ++parametersCount;\n        if (dirtyFlag_[1])\n        {\n            sql += \"name,\";\n            ++parametersCount;\n        }\n        needSelection = true;\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        sql += \"default,\";\n        if (dirtyFlag_[1])\n        {\n            sql.append(\"?,\");\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        sql.append(1, ')');\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace drogonTestMysql\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/mysql/Tag.cc",
    "content": "/**\n *\n *  Tag.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Tag.h\"\n#include \"Blog.h\"\n#include \"BlogTag.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::drogonTestMysql;\n\nconst std::string Tag::Cols::_id = \"id\";\nconst std::string Tag::Cols::_name = \"name\";\nconst std::string Tag::primaryKeyName = \"id\";\nconst bool Tag::hasPrimaryKey = true;\nconst std::string Tag::tableName = \"tag\";\n\nconst std::vector<typename Tag::MetaData> Tag::metaData_ = {\n    {\"id\", \"int32_t\", \"int(11)\", 4, 1, 1, 1},\n    {\"name\", \"std::string\", \"varchar(30)\", 30, 0, 0, 0}};\n\nconst std::string &Tag::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nTag::Tag(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[\"id\"].as<int32_t>());\n        }\n        if (!r[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(r[\"name\"].as<std::string>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 2 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            name_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n    }\n}\n\nTag::Tag(const Json::Value &pJson,\n         const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            name_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n}\n\nTag::Tag(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(pJson[\"name\"].asString());\n        }\n    }\n}\n\nvoid Tag::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            name_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n}\n\nvoid Tag::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(pJson[\"name\"].asString());\n        }\n    }\n}\n\nconst int32_t &Tag::getValueOfId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (id_)\n        return *id_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &Tag::getId() const noexcept\n{\n    return id_;\n}\n\nvoid Tag::setId(const int32_t &pId) noexcept\n{\n    id_ = std::make_shared<int32_t>(pId);\n    dirtyFlag_[0] = true;\n}\n\nconst typename Tag::PrimaryKeyType &Tag::getPrimaryKey() const\n{\n    assert(id_);\n    return *id_;\n}\n\nconst std::string &Tag::getValueOfName() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (name_)\n        return *name_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Tag::getName() const noexcept\n{\n    return name_;\n}\n\nvoid Tag::setName(const std::string &pName) noexcept\n{\n    name_ = std::make_shared<std::string>(pName);\n    dirtyFlag_[1] = true;\n}\n\nvoid Tag::setName(std::string &&pName) noexcept\n{\n    name_ = std::make_shared<std::string>(std::move(pName));\n    dirtyFlag_[1] = true;\n}\n\nvoid Tag::setNameToNull() noexcept\n{\n    name_.reset();\n    dirtyFlag_[1] = true;\n}\n\nvoid Tag::updateId(const uint64_t id)\n{\n    id_ = std::make_shared<int32_t>(static_cast<int32_t>(id));\n}\n\nconst std::vector<std::string> &Tag::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"name\"};\n    return inCols;\n}\n\nvoid Tag::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getName())\n        {\n            binder << getValueOfName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Tag::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    return ret;\n}\n\nvoid Tag::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getName())\n        {\n            binder << getValueOfName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Tag::toJson() const\n{\n    Json::Value ret;\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getName())\n    {\n        ret[\"name\"] = getValueOfName();\n    }\n    else\n    {\n        ret[\"name\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Tag::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 2)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getId())\n            {\n                ret[pMasqueradingVector[0]] = getValueOfId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getName())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfName();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getName())\n    {\n        ret[\"name\"] = getValueOfName();\n    }\n    else\n    {\n        ret[\"name\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Tag::validateJsonForCreation(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        if (!validJsonOfField(1, \"name\", pJson[\"name\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Tag::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Tag::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        if (!validJsonOfField(1, \"name\", pJson[\"name\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Tag::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Tag::validJsonOfField(size_t index,\n                           const std::string &fieldName,\n                           const Json::Value &pJson,\n                           std::string &err,\n                           bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (isForCreation)\n            {\n                err = \"The automatic primary key cannot be set\";\n                return false;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 30)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 30)\";\n                return false;\n            }\n\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n\nstd::vector<std::pair<Blog, BlogTag>> Tag::getBlogs(\n    const DbClientPtr &clientPtr) const\n{\n    static const std::string sql =\n        \"select * from blog,blog_tag where blog_tag.tag_id = ? and \"\n        \"blog_tag.blog_id = blog.id\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *id_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    std::vector<std::pair<Blog, BlogTag>> ret;\n    ret.reserve(r.size());\n    for (auto const &row : r)\n    {\n        ret.emplace_back(\n            std::pair<Blog, BlogTag>(Blog(row),\n                                     BlogTag(row, Blog::getColumnNumber())));\n    }\n    return ret;\n}\n\nvoid Tag::getBlogs(\n    const DbClientPtr &clientPtr,\n    const std::function<void(std::vector<std::pair<Blog, BlogTag>>)> &rcb,\n    const ExceptionCallback &ecb) const\n{\n    static const std::string sql =\n        \"select * from blog,blog_tag where blog_tag.tag_id = ? and \"\n        \"blog_tag.blog_id = blog.id\";\n    *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) {\n        std::vector<std::pair<Blog, BlogTag>> ret;\n        ret.reserve(r.size());\n        for (auto const &row : r)\n        {\n            ret.emplace_back(std::pair<Blog, BlogTag>(\n                Blog(row), BlogTag(row, Blog::getColumnNumber())));\n        }\n        rcb(ret);\n    } >> ecb;\n}\n"
  },
  {
    "path": "orm_lib/tests/mysql/Tag.h",
    "content": "/**\n *\n *  Tag.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace drogonTestMysql\n{\nclass Blog;\nclass BlogTag;\n\nclass Tag\n{\n  public:\n    struct Cols\n    {\n        static const std::string _id;\n        static const std::string _name;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = int32_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Tag(const drogon::orm::Row &r,\n                 const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Tag(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Tag(const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Tag() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column id  */\n    /// Get the value of the column id, returns the default value if the column\n    /// is null\n    const int32_t &getValueOfId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getId() const noexcept;\n    /// Set the value of the column id\n    void setId(const int32_t &pId) noexcept;\n\n    /**  For column name  */\n    /// Get the value of the column name, returns the default value if the\n    /// column is null\n    const std::string &getValueOfName() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getName() const noexcept;\n    /// Set the value of the column name\n    void setName(const std::string &pName) noexcept;\n    void setName(std::string &&pName) noexcept;\n    void setNameToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 2;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n    std::vector<std::pair<Blog, BlogTag>> getBlogs(\n        const drogon::orm::DbClientPtr &clientPtr) const;\n    void getBlogs(\n        const drogon::orm::DbClientPtr &clientPtr,\n        const std::function<void(std::vector<std::pair<Blog, BlogTag>>)> &rcb,\n        const drogon::orm::ExceptionCallback &ecb) const;\n\n  private:\n    friend drogon::orm::Mapper<Tag>;\n    friend drogon::orm::BaseBuilder<Tag, true, true>;\n    friend drogon::orm::BaseBuilder<Tag, true, false>;\n    friend drogon::orm::BaseBuilder<Tag, false, true>;\n    friend drogon::orm::BaseBuilder<Tag, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<Tag>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int32_t> id_;\n    std::shared_ptr<std::string> name_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[2] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        sql += \"id,\";\n        ++parametersCount;\n        if (dirtyFlag_[1])\n        {\n            sql += \"name,\";\n            ++parametersCount;\n        }\n        needSelection = true;\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        sql += \"default,\";\n        if (dirtyFlag_[1])\n        {\n            sql.append(\"?,\");\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        sql.append(1, ')');\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace drogonTestMysql\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/mysql/Users.cc",
    "content": "/**\n *\n *  Users.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Users.h\"\n#include \"Wallets.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::drogonTestMysql;\n\nconst std::string Users::Cols::_id = \"id\";\nconst std::string Users::Cols::_user_id = \"user_id\";\nconst std::string Users::Cols::_user_name = \"user_name\";\nconst std::string Users::Cols::_password = \"password\";\nconst std::string Users::Cols::_org_name = \"org_name\";\nconst std::string Users::Cols::_signature = \"signature\";\nconst std::string Users::Cols::_avatar_id = \"avatar_id\";\nconst std::string Users::Cols::_salt = \"salt\";\nconst std::string Users::Cols::_admin = \"admin\";\nconst std::string Users::primaryKeyName = \"id\";\nconst bool Users::hasPrimaryKey = true;\nconst std::string Users::tableName = \"users\";\n\nconst std::vector<typename Users::MetaData> Users::metaData_ = {\n    {\"id\", \"int32_t\", \"int(11)\", 4, 1, 1, 1},\n    {\"user_id\", \"std::string\", \"varchar(32)\", 32, 0, 0, 0},\n    {\"user_name\", \"std::string\", \"varchar(64)\", 64, 0, 0, 0},\n    {\"password\", \"std::string\", \"varchar(64)\", 64, 0, 0, 0},\n    {\"org_name\", \"std::string\", \"varchar(20)\", 20, 0, 0, 0},\n    {\"signature\", \"std::string\", \"varchar(50)\", 50, 0, 0, 0},\n    {\"avatar_id\", \"std::string\", \"varchar(32)\", 32, 0, 0, 0},\n    {\"salt\", \"std::string\", \"varchar(20)\", 20, 0, 0, 0},\n    {\"admin\", \"int8_t\", \"tinyint(1)\", 1, 0, 0, 0}};\n\nconst std::string &Users::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nUsers::Users(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[\"id\"].as<int32_t>());\n        }\n        if (!r[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(r[\"user_id\"].as<std::string>());\n        }\n        if (!r[\"user_name\"].isNull())\n        {\n            userName_ =\n                std::make_shared<std::string>(r[\"user_name\"].as<std::string>());\n        }\n        if (!r[\"password\"].isNull())\n        {\n            password_ =\n                std::make_shared<std::string>(r[\"password\"].as<std::string>());\n        }\n        if (!r[\"org_name\"].isNull())\n        {\n            orgName_ =\n                std::make_shared<std::string>(r[\"org_name\"].as<std::string>());\n        }\n        if (!r[\"signature\"].isNull())\n        {\n            signature_ =\n                std::make_shared<std::string>(r[\"signature\"].as<std::string>());\n        }\n        if (!r[\"avatar_id\"].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(r[\"avatar_id\"].as<std::string>());\n        }\n        if (!r[\"salt\"].isNull())\n        {\n            salt_ = std::make_shared<std::string>(r[\"salt\"].as<std::string>());\n        }\n        if (!r[\"admin\"].isNull())\n        {\n            admin_ = std::make_shared<int8_t>(r[\"admin\"].as<int8_t>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 9 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            userId_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 2;\n        if (!r[index].isNull())\n        {\n            userName_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 3;\n        if (!r[index].isNull())\n        {\n            password_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 4;\n        if (!r[index].isNull())\n        {\n            orgName_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 5;\n        if (!r[index].isNull())\n        {\n            signature_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 6;\n        if (!r[index].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 7;\n        if (!r[index].isNull())\n        {\n            salt_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 8;\n        if (!r[index].isNull())\n        {\n            admin_ = std::make_shared<int8_t>(r[index].as<int8_t>());\n        }\n    }\n}\n\nUsers::Users(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 9)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            userId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            userName_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[2]].asString());\n        }\n    }\n    if (!pMasqueradingVector[3].empty() &&\n        pJson.isMember(pMasqueradingVector[3]))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[pMasqueradingVector[3]].isNull())\n        {\n            password_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[3]].asString());\n        }\n    }\n    if (!pMasqueradingVector[4].empty() &&\n        pJson.isMember(pMasqueradingVector[4]))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[pMasqueradingVector[4]].isNull())\n        {\n            orgName_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[4]].asString());\n        }\n    }\n    if (!pMasqueradingVector[5].empty() &&\n        pJson.isMember(pMasqueradingVector[5]))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[pMasqueradingVector[5]].isNull())\n        {\n            signature_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[5]].asString());\n        }\n    }\n    if (!pMasqueradingVector[6].empty() &&\n        pJson.isMember(pMasqueradingVector[6]))\n    {\n        dirtyFlag_[6] = true;\n        if (!pJson[pMasqueradingVector[6]].isNull())\n        {\n            avatarId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[6]].asString());\n        }\n    }\n    if (!pMasqueradingVector[7].empty() &&\n        pJson.isMember(pMasqueradingVector[7]))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[pMasqueradingVector[7]].isNull())\n        {\n            salt_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[7]].asString());\n        }\n    }\n    if (!pMasqueradingVector[8].empty() &&\n        pJson.isMember(pMasqueradingVector[8]))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[pMasqueradingVector[8]].isNull())\n        {\n            admin_ = std::make_shared<int8_t>(\n                (int8_t)pJson[pMasqueradingVector[8]].asInt64());\n        }\n    }\n}\n\nUsers::Users(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(pJson[\"user_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"user_name\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"user_name\"].isNull())\n        {\n            userName_ =\n                std::make_shared<std::string>(pJson[\"user_name\"].asString());\n        }\n    }\n    if (pJson.isMember(\"password\"))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[\"password\"].isNull())\n        {\n            password_ =\n                std::make_shared<std::string>(pJson[\"password\"].asString());\n        }\n    }\n    if (pJson.isMember(\"org_name\"))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[\"org_name\"].isNull())\n        {\n            orgName_ =\n                std::make_shared<std::string>(pJson[\"org_name\"].asString());\n        }\n    }\n    if (pJson.isMember(\"signature\"))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[\"signature\"].isNull())\n        {\n            signature_ =\n                std::make_shared<std::string>(pJson[\"signature\"].asString());\n        }\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        dirtyFlag_[6] = true;\n        if (!pJson[\"avatar_id\"].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(pJson[\"avatar_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"salt\"))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[\"salt\"].isNull())\n        {\n            salt_ = std::make_shared<std::string>(pJson[\"salt\"].asString());\n        }\n    }\n    if (pJson.isMember(\"admin\"))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[\"admin\"].isNull())\n        {\n            admin_ = std::make_shared<int8_t>((int8_t)pJson[\"admin\"].asInt64());\n        }\n    }\n}\n\nvoid Users::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 9)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            userId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            userName_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[2]].asString());\n        }\n    }\n    if (!pMasqueradingVector[3].empty() &&\n        pJson.isMember(pMasqueradingVector[3]))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[pMasqueradingVector[3]].isNull())\n        {\n            password_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[3]].asString());\n        }\n    }\n    if (!pMasqueradingVector[4].empty() &&\n        pJson.isMember(pMasqueradingVector[4]))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[pMasqueradingVector[4]].isNull())\n        {\n            orgName_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[4]].asString());\n        }\n    }\n    if (!pMasqueradingVector[5].empty() &&\n        pJson.isMember(pMasqueradingVector[5]))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[pMasqueradingVector[5]].isNull())\n        {\n            signature_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[5]].asString());\n        }\n    }\n    if (!pMasqueradingVector[6].empty() &&\n        pJson.isMember(pMasqueradingVector[6]))\n    {\n        dirtyFlag_[6] = true;\n        if (!pJson[pMasqueradingVector[6]].isNull())\n        {\n            avatarId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[6]].asString());\n        }\n    }\n    if (!pMasqueradingVector[7].empty() &&\n        pJson.isMember(pMasqueradingVector[7]))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[pMasqueradingVector[7]].isNull())\n        {\n            salt_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[7]].asString());\n        }\n    }\n    if (!pMasqueradingVector[8].empty() &&\n        pJson.isMember(pMasqueradingVector[8]))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[pMasqueradingVector[8]].isNull())\n        {\n            admin_ = std::make_shared<int8_t>(\n                (int8_t)pJson[pMasqueradingVector[8]].asInt64());\n        }\n    }\n}\n\nvoid Users::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(pJson[\"user_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"user_name\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"user_name\"].isNull())\n        {\n            userName_ =\n                std::make_shared<std::string>(pJson[\"user_name\"].asString());\n        }\n    }\n    if (pJson.isMember(\"password\"))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[\"password\"].isNull())\n        {\n            password_ =\n                std::make_shared<std::string>(pJson[\"password\"].asString());\n        }\n    }\n    if (pJson.isMember(\"org_name\"))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[\"org_name\"].isNull())\n        {\n            orgName_ =\n                std::make_shared<std::string>(pJson[\"org_name\"].asString());\n        }\n    }\n    if (pJson.isMember(\"signature\"))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[\"signature\"].isNull())\n        {\n            signature_ =\n                std::make_shared<std::string>(pJson[\"signature\"].asString());\n        }\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        dirtyFlag_[6] = true;\n        if (!pJson[\"avatar_id\"].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(pJson[\"avatar_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"salt\"))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[\"salt\"].isNull())\n        {\n            salt_ = std::make_shared<std::string>(pJson[\"salt\"].asString());\n        }\n    }\n    if (pJson.isMember(\"admin\"))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[\"admin\"].isNull())\n        {\n            admin_ = std::make_shared<int8_t>((int8_t)pJson[\"admin\"].asInt64());\n        }\n    }\n}\n\nconst int32_t &Users::getValueOfId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (id_)\n        return *id_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &Users::getId() const noexcept\n{\n    return id_;\n}\n\nvoid Users::setId(const int32_t &pId) noexcept\n{\n    id_ = std::make_shared<int32_t>(pId);\n    dirtyFlag_[0] = true;\n}\n\nconst typename Users::PrimaryKeyType &Users::getPrimaryKey() const\n{\n    assert(id_);\n    return *id_;\n}\n\nconst std::string &Users::getValueOfUserId() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (userId_)\n        return *userId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getUserId() const noexcept\n{\n    return userId_;\n}\n\nvoid Users::setUserId(const std::string &pUserId) noexcept\n{\n    userId_ = std::make_shared<std::string>(pUserId);\n    dirtyFlag_[1] = true;\n}\n\nvoid Users::setUserId(std::string &&pUserId) noexcept\n{\n    userId_ = std::make_shared<std::string>(std::move(pUserId));\n    dirtyFlag_[1] = true;\n}\n\nvoid Users::setUserIdToNull() noexcept\n{\n    userId_.reset();\n    dirtyFlag_[1] = true;\n}\n\nconst std::string &Users::getValueOfUserName() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (userName_)\n        return *userName_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getUserName() const noexcept\n{\n    return userName_;\n}\n\nvoid Users::setUserName(const std::string &pUserName) noexcept\n{\n    userName_ = std::make_shared<std::string>(pUserName);\n    dirtyFlag_[2] = true;\n}\n\nvoid Users::setUserName(std::string &&pUserName) noexcept\n{\n    userName_ = std::make_shared<std::string>(std::move(pUserName));\n    dirtyFlag_[2] = true;\n}\n\nvoid Users::setUserNameToNull() noexcept\n{\n    userName_.reset();\n    dirtyFlag_[2] = true;\n}\n\nconst std::string &Users::getValueOfPassword() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (password_)\n        return *password_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getPassword() const noexcept\n{\n    return password_;\n}\n\nvoid Users::setPassword(const std::string &pPassword) noexcept\n{\n    password_ = std::make_shared<std::string>(pPassword);\n    dirtyFlag_[3] = true;\n}\n\nvoid Users::setPassword(std::string &&pPassword) noexcept\n{\n    password_ = std::make_shared<std::string>(std::move(pPassword));\n    dirtyFlag_[3] = true;\n}\n\nvoid Users::setPasswordToNull() noexcept\n{\n    password_.reset();\n    dirtyFlag_[3] = true;\n}\n\nconst std::string &Users::getValueOfOrgName() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (orgName_)\n        return *orgName_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getOrgName() const noexcept\n{\n    return orgName_;\n}\n\nvoid Users::setOrgName(const std::string &pOrgName) noexcept\n{\n    orgName_ = std::make_shared<std::string>(pOrgName);\n    dirtyFlag_[4] = true;\n}\n\nvoid Users::setOrgName(std::string &&pOrgName) noexcept\n{\n    orgName_ = std::make_shared<std::string>(std::move(pOrgName));\n    dirtyFlag_[4] = true;\n}\n\nvoid Users::setOrgNameToNull() noexcept\n{\n    orgName_.reset();\n    dirtyFlag_[4] = true;\n}\n\nconst std::string &Users::getValueOfSignature() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (signature_)\n        return *signature_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getSignature() const noexcept\n{\n    return signature_;\n}\n\nvoid Users::setSignature(const std::string &pSignature) noexcept\n{\n    signature_ = std::make_shared<std::string>(pSignature);\n    dirtyFlag_[5] = true;\n}\n\nvoid Users::setSignature(std::string &&pSignature) noexcept\n{\n    signature_ = std::make_shared<std::string>(std::move(pSignature));\n    dirtyFlag_[5] = true;\n}\n\nvoid Users::setSignatureToNull() noexcept\n{\n    signature_.reset();\n    dirtyFlag_[5] = true;\n}\n\nconst std::string &Users::getValueOfAvatarId() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (avatarId_)\n        return *avatarId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getAvatarId() const noexcept\n{\n    return avatarId_;\n}\n\nvoid Users::setAvatarId(const std::string &pAvatarId) noexcept\n{\n    avatarId_ = std::make_shared<std::string>(pAvatarId);\n    dirtyFlag_[6] = true;\n}\n\nvoid Users::setAvatarId(std::string &&pAvatarId) noexcept\n{\n    avatarId_ = std::make_shared<std::string>(std::move(pAvatarId));\n    dirtyFlag_[6] = true;\n}\n\nvoid Users::setAvatarIdToNull() noexcept\n{\n    avatarId_.reset();\n    dirtyFlag_[6] = true;\n}\n\nconst std::string &Users::getValueOfSalt() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (salt_)\n        return *salt_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getSalt() const noexcept\n{\n    return salt_;\n}\n\nvoid Users::setSalt(const std::string &pSalt) noexcept\n{\n    salt_ = std::make_shared<std::string>(pSalt);\n    dirtyFlag_[7] = true;\n}\n\nvoid Users::setSalt(std::string &&pSalt) noexcept\n{\n    salt_ = std::make_shared<std::string>(std::move(pSalt));\n    dirtyFlag_[7] = true;\n}\n\nvoid Users::setSaltToNull() noexcept\n{\n    salt_.reset();\n    dirtyFlag_[7] = true;\n}\n\nconst int8_t &Users::getValueOfAdmin() const noexcept\n{\n    static const int8_t defaultValue = int8_t();\n    if (admin_)\n        return *admin_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int8_t> &Users::getAdmin() const noexcept\n{\n    return admin_;\n}\n\nvoid Users::setAdmin(const int8_t &pAdmin) noexcept\n{\n    admin_ = std::make_shared<int8_t>(pAdmin);\n    dirtyFlag_[8] = true;\n}\n\nvoid Users::setAdminToNull() noexcept\n{\n    admin_.reset();\n    dirtyFlag_[8] = true;\n}\n\nvoid Users::updateId(const uint64_t id)\n{\n    id_ = std::make_shared<int32_t>(static_cast<int32_t>(id));\n}\n\nconst std::vector<std::string> &Users::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"user_id\",\n                                                    \"user_name\",\n                                                    \"password\",\n                                                    \"org_name\",\n                                                    \"signature\",\n                                                    \"avatar_id\",\n                                                    \"salt\",\n                                                    \"admin\"};\n    return inCols;\n}\n\nvoid Users::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getUserId())\n        {\n            binder << getValueOfUserId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getUserName())\n        {\n            binder << getValueOfUserName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[3])\n    {\n        if (getPassword())\n        {\n            binder << getValueOfPassword();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[4])\n    {\n        if (getOrgName())\n        {\n            binder << getValueOfOrgName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[5])\n    {\n        if (getSignature())\n        {\n            binder << getValueOfSignature();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[6])\n    {\n        if (getAvatarId())\n        {\n            binder << getValueOfAvatarId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[7])\n    {\n        if (getSalt())\n        {\n            binder << getValueOfSalt();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[8])\n    {\n        if (getAdmin())\n        {\n            binder << getValueOfAdmin();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Users::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    if (dirtyFlag_[2])\n    {\n        ret.push_back(getColumnName(2));\n    }\n    if (dirtyFlag_[3])\n    {\n        ret.push_back(getColumnName(3));\n    }\n    if (dirtyFlag_[4])\n    {\n        ret.push_back(getColumnName(4));\n    }\n    if (dirtyFlag_[5])\n    {\n        ret.push_back(getColumnName(5));\n    }\n    if (dirtyFlag_[6])\n    {\n        ret.push_back(getColumnName(6));\n    }\n    if (dirtyFlag_[7])\n    {\n        ret.push_back(getColumnName(7));\n    }\n    if (dirtyFlag_[8])\n    {\n        ret.push_back(getColumnName(8));\n    }\n    return ret;\n}\n\nvoid Users::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getUserId())\n        {\n            binder << getValueOfUserId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getUserName())\n        {\n            binder << getValueOfUserName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[3])\n    {\n        if (getPassword())\n        {\n            binder << getValueOfPassword();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[4])\n    {\n        if (getOrgName())\n        {\n            binder << getValueOfOrgName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[5])\n    {\n        if (getSignature())\n        {\n            binder << getValueOfSignature();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[6])\n    {\n        if (getAvatarId())\n        {\n            binder << getValueOfAvatarId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[7])\n    {\n        if (getSalt())\n        {\n            binder << getValueOfSalt();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[8])\n    {\n        if (getAdmin())\n        {\n            binder << getValueOfAdmin();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Users::toJson() const\n{\n    Json::Value ret;\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getUserId())\n    {\n        ret[\"user_id\"] = getValueOfUserId();\n    }\n    else\n    {\n        ret[\"user_id\"] = Json::Value();\n    }\n    if (getUserName())\n    {\n        ret[\"user_name\"] = getValueOfUserName();\n    }\n    else\n    {\n        ret[\"user_name\"] = Json::Value();\n    }\n    if (getPassword())\n    {\n        ret[\"password\"] = getValueOfPassword();\n    }\n    else\n    {\n        ret[\"password\"] = Json::Value();\n    }\n    if (getOrgName())\n    {\n        ret[\"org_name\"] = getValueOfOrgName();\n    }\n    else\n    {\n        ret[\"org_name\"] = Json::Value();\n    }\n    if (getSignature())\n    {\n        ret[\"signature\"] = getValueOfSignature();\n    }\n    else\n    {\n        ret[\"signature\"] = Json::Value();\n    }\n    if (getAvatarId())\n    {\n        ret[\"avatar_id\"] = getValueOfAvatarId();\n    }\n    else\n    {\n        ret[\"avatar_id\"] = Json::Value();\n    }\n    if (getSalt())\n    {\n        ret[\"salt\"] = getValueOfSalt();\n    }\n    else\n    {\n        ret[\"salt\"] = Json::Value();\n    }\n    if (getAdmin())\n    {\n        ret[\"admin\"] = getValueOfAdmin();\n    }\n    else\n    {\n        ret[\"admin\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Users::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 9)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getId())\n            {\n                ret[pMasqueradingVector[0]] = getValueOfId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getUserId())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfUserId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (getUserName())\n            {\n                ret[pMasqueradingVector[2]] = getValueOfUserName();\n            }\n            else\n            {\n                ret[pMasqueradingVector[2]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[3].empty())\n        {\n            if (getPassword())\n            {\n                ret[pMasqueradingVector[3]] = getValueOfPassword();\n            }\n            else\n            {\n                ret[pMasqueradingVector[3]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[4].empty())\n        {\n            if (getOrgName())\n            {\n                ret[pMasqueradingVector[4]] = getValueOfOrgName();\n            }\n            else\n            {\n                ret[pMasqueradingVector[4]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[5].empty())\n        {\n            if (getSignature())\n            {\n                ret[pMasqueradingVector[5]] = getValueOfSignature();\n            }\n            else\n            {\n                ret[pMasqueradingVector[5]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[6].empty())\n        {\n            if (getAvatarId())\n            {\n                ret[pMasqueradingVector[6]] = getValueOfAvatarId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[6]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[7].empty())\n        {\n            if (getSalt())\n            {\n                ret[pMasqueradingVector[7]] = getValueOfSalt();\n            }\n            else\n            {\n                ret[pMasqueradingVector[7]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[8].empty())\n        {\n            if (getAdmin())\n            {\n                ret[pMasqueradingVector[8]] = getValueOfAdmin();\n            }\n            else\n            {\n                ret[pMasqueradingVector[8]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getUserId())\n    {\n        ret[\"user_id\"] = getValueOfUserId();\n    }\n    else\n    {\n        ret[\"user_id\"] = Json::Value();\n    }\n    if (getUserName())\n    {\n        ret[\"user_name\"] = getValueOfUserName();\n    }\n    else\n    {\n        ret[\"user_name\"] = Json::Value();\n    }\n    if (getPassword())\n    {\n        ret[\"password\"] = getValueOfPassword();\n    }\n    else\n    {\n        ret[\"password\"] = Json::Value();\n    }\n    if (getOrgName())\n    {\n        ret[\"org_name\"] = getValueOfOrgName();\n    }\n    else\n    {\n        ret[\"org_name\"] = Json::Value();\n    }\n    if (getSignature())\n    {\n        ret[\"signature\"] = getValueOfSignature();\n    }\n    else\n    {\n        ret[\"signature\"] = Json::Value();\n    }\n    if (getAvatarId())\n    {\n        ret[\"avatar_id\"] = getValueOfAvatarId();\n    }\n    else\n    {\n        ret[\"avatar_id\"] = Json::Value();\n    }\n    if (getSalt())\n    {\n        ret[\"salt\"] = getValueOfSalt();\n    }\n    else\n    {\n        ret[\"salt\"] = Json::Value();\n    }\n    if (getAdmin())\n    {\n        ret[\"admin\"] = getValueOfAdmin();\n    }\n    else\n    {\n        ret[\"admin\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Users::validateJsonForCreation(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        if (!validJsonOfField(1, \"user_id\", pJson[\"user_id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"user_name\"))\n    {\n        if (!validJsonOfField(2, \"user_name\", pJson[\"user_name\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"password\"))\n    {\n        if (!validJsonOfField(3, \"password\", pJson[\"password\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"org_name\"))\n    {\n        if (!validJsonOfField(4, \"org_name\", pJson[\"org_name\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"signature\"))\n    {\n        if (!validJsonOfField(5, \"signature\", pJson[\"signature\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        if (!validJsonOfField(6, \"avatar_id\", pJson[\"avatar_id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"salt\"))\n    {\n        if (!validJsonOfField(7, \"salt\", pJson[\"salt\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"admin\"))\n    {\n        if (!validJsonOfField(8, \"admin\", pJson[\"admin\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Users::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 9)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[2]))\n            {\n                if (!validJsonOfField(2,\n                                      pMasqueradingVector[2],\n                                      pJson[pMasqueradingVector[2]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[3].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[3]))\n            {\n                if (!validJsonOfField(3,\n                                      pMasqueradingVector[3],\n                                      pJson[pMasqueradingVector[3]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[4].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[4]))\n            {\n                if (!validJsonOfField(4,\n                                      pMasqueradingVector[4],\n                                      pJson[pMasqueradingVector[4]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[5].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[5]))\n            {\n                if (!validJsonOfField(5,\n                                      pMasqueradingVector[5],\n                                      pJson[pMasqueradingVector[5]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[6].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[6]))\n            {\n                if (!validJsonOfField(6,\n                                      pMasqueradingVector[6],\n                                      pJson[pMasqueradingVector[6]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[7].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[7]))\n            {\n                if (!validJsonOfField(7,\n                                      pMasqueradingVector[7],\n                                      pJson[pMasqueradingVector[7]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[8].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[8]))\n            {\n                if (!validJsonOfField(8,\n                                      pMasqueradingVector[8],\n                                      pJson[pMasqueradingVector[8]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Users::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        if (!validJsonOfField(1, \"user_id\", pJson[\"user_id\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"user_name\"))\n    {\n        if (!validJsonOfField(2, \"user_name\", pJson[\"user_name\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"password\"))\n    {\n        if (!validJsonOfField(3, \"password\", pJson[\"password\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"org_name\"))\n    {\n        if (!validJsonOfField(4, \"org_name\", pJson[\"org_name\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"signature\"))\n    {\n        if (!validJsonOfField(5, \"signature\", pJson[\"signature\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        if (!validJsonOfField(6, \"avatar_id\", pJson[\"avatar_id\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"salt\"))\n    {\n        if (!validJsonOfField(7, \"salt\", pJson[\"salt\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"admin\"))\n    {\n        if (!validJsonOfField(8, \"admin\", pJson[\"admin\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Users::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 9)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[2].empty() &&\n            pJson.isMember(pMasqueradingVector[2]))\n        {\n            if (!validJsonOfField(2,\n                                  pMasqueradingVector[2],\n                                  pJson[pMasqueradingVector[2]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[3].empty() &&\n            pJson.isMember(pMasqueradingVector[3]))\n        {\n            if (!validJsonOfField(3,\n                                  pMasqueradingVector[3],\n                                  pJson[pMasqueradingVector[3]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[4].empty() &&\n            pJson.isMember(pMasqueradingVector[4]))\n        {\n            if (!validJsonOfField(4,\n                                  pMasqueradingVector[4],\n                                  pJson[pMasqueradingVector[4]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[5].empty() &&\n            pJson.isMember(pMasqueradingVector[5]))\n        {\n            if (!validJsonOfField(5,\n                                  pMasqueradingVector[5],\n                                  pJson[pMasqueradingVector[5]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[6].empty() &&\n            pJson.isMember(pMasqueradingVector[6]))\n        {\n            if (!validJsonOfField(6,\n                                  pMasqueradingVector[6],\n                                  pJson[pMasqueradingVector[6]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[7].empty() &&\n            pJson.isMember(pMasqueradingVector[7]))\n        {\n            if (!validJsonOfField(7,\n                                  pMasqueradingVector[7],\n                                  pJson[pMasqueradingVector[7]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[8].empty() &&\n            pJson.isMember(pMasqueradingVector[8]))\n        {\n            if (!validJsonOfField(8,\n                                  pMasqueradingVector[8],\n                                  pJson[pMasqueradingVector[8]],\n                                  err,\n                                  false))\n                return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Users::validJsonOfField(size_t index,\n                             const std::string &fieldName,\n                             const Json::Value &pJson,\n                             std::string &err,\n                             bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (isForCreation)\n            {\n                err = \"The automatic primary key cannot be set\";\n                return false;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 32)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 32)\";\n                return false;\n            }\n\n            break;\n        case 2:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 64)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 64)\";\n                return false;\n            }\n\n            break;\n        case 3:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 64)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 64)\";\n                return false;\n            }\n\n            break;\n        case 4:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 20)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 20)\";\n                return false;\n            }\n\n            break;\n        case 5:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 50)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 50)\";\n                return false;\n            }\n\n            break;\n        case 6:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 32)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 32)\";\n                return false;\n            }\n\n            break;\n        case 7:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 20)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 20)\";\n                return false;\n            }\n\n            break;\n        case 8:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n\nWallets Users::getWallet(const DbClientPtr &clientPtr) const\n{\n    static const std::string sql = \"select * from wallets where user_id = ?\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *userId_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    if (r.size() == 0)\n    {\n        throw UnexpectedRows(\"0 rows found\");\n    }\n    else if (r.size() > 1)\n    {\n        throw UnexpectedRows(\"Found more than one row\");\n    }\n    return Wallets(r[0]);\n}\n\nvoid Users::getWallet(const DbClientPtr &clientPtr,\n                      const std::function<void(Wallets)> &rcb,\n                      const ExceptionCallback &ecb) const\n{\n    static const std::string sql = \"select * from wallets where user_id = ?\";\n    *clientPtr << sql << *userId_ >> [rcb = std::move(rcb),\n                                      ecb](const Result &r) {\n        if (r.size() == 0)\n        {\n            ecb(UnexpectedRows(\"0 rows found\"));\n        }\n        else if (r.size() > 1)\n        {\n            ecb(UnexpectedRows(\"Found more than one row\"));\n        }\n        else\n        {\n            rcb(Wallets(r[0]));\n        }\n    } >> ecb;\n}\n"
  },
  {
    "path": "orm_lib/tests/mysql/Users.h",
    "content": "/**\n *\n *  Users.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace drogonTestMysql\n{\nclass Wallets;\n\nclass Users\n{\n  public:\n    struct Cols\n    {\n        static const std::string _id;\n        static const std::string _user_id;\n        static const std::string _user_name;\n        static const std::string _password;\n        static const std::string _org_name;\n        static const std::string _signature;\n        static const std::string _avatar_id;\n        static const std::string _salt;\n        static const std::string _admin;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = int32_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Users(const drogon::orm::Row &r,\n                   const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Users(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Users(const Json::Value &pJson,\n          const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Users() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column id  */\n    /// Get the value of the column id, returns the default value if the column\n    /// is null\n    const int32_t &getValueOfId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getId() const noexcept;\n    /// Set the value of the column id\n    void setId(const int32_t &pId) noexcept;\n\n    /**  For column user_id  */\n    /// Get the value of the column user_id, returns the default value if the\n    /// column is null\n    const std::string &getValueOfUserId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getUserId() const noexcept;\n    /// Set the value of the column user_id\n    void setUserId(const std::string &pUserId) noexcept;\n    void setUserId(std::string &&pUserId) noexcept;\n    void setUserIdToNull() noexcept;\n\n    /**  For column user_name  */\n    /// Get the value of the column user_name, returns the default value if the\n    /// column is null\n    const std::string &getValueOfUserName() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getUserName() const noexcept;\n    /// Set the value of the column user_name\n    void setUserName(const std::string &pUserName) noexcept;\n    void setUserName(std::string &&pUserName) noexcept;\n    void setUserNameToNull() noexcept;\n\n    /**  For column password  */\n    /// Get the value of the column password, returns the default value if the\n    /// column is null\n    const std::string &getValueOfPassword() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getPassword() const noexcept;\n    /// Set the value of the column password\n    void setPassword(const std::string &pPassword) noexcept;\n    void setPassword(std::string &&pPassword) noexcept;\n    void setPasswordToNull() noexcept;\n\n    /**  For column org_name  */\n    /// Get the value of the column org_name, returns the default value if the\n    /// column is null\n    const std::string &getValueOfOrgName() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getOrgName() const noexcept;\n    /// Set the value of the column org_name\n    void setOrgName(const std::string &pOrgName) noexcept;\n    void setOrgName(std::string &&pOrgName) noexcept;\n    void setOrgNameToNull() noexcept;\n\n    /**  For column signature  */\n    /// Get the value of the column signature, returns the default value if the\n    /// column is null\n    const std::string &getValueOfSignature() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getSignature() const noexcept;\n    /// Set the value of the column signature\n    void setSignature(const std::string &pSignature) noexcept;\n    void setSignature(std::string &&pSignature) noexcept;\n    void setSignatureToNull() noexcept;\n\n    /**  For column avatar_id  */\n    /// Get the value of the column avatar_id, returns the default value if the\n    /// column is null\n    const std::string &getValueOfAvatarId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getAvatarId() const noexcept;\n    /// Set the value of the column avatar_id\n    void setAvatarId(const std::string &pAvatarId) noexcept;\n    void setAvatarId(std::string &&pAvatarId) noexcept;\n    void setAvatarIdToNull() noexcept;\n\n    /**  For column salt  */\n    /// Get the value of the column salt, returns the default value if the\n    /// column is null\n    const std::string &getValueOfSalt() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getSalt() const noexcept;\n    /// Set the value of the column salt\n    void setSalt(const std::string &pSalt) noexcept;\n    void setSalt(std::string &&pSalt) noexcept;\n    void setSaltToNull() noexcept;\n\n    /**  For column admin  */\n    /// Get the value of the column admin, returns the default value if the\n    /// column is null\n    const int8_t &getValueOfAdmin() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int8_t> &getAdmin() const noexcept;\n    /// Set the value of the column admin\n    void setAdmin(const int8_t &pAdmin) noexcept;\n    void setAdminToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 9;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n    Wallets getWallet(const drogon::orm::DbClientPtr &clientPtr) const;\n    void getWallet(const drogon::orm::DbClientPtr &clientPtr,\n                   const std::function<void(Wallets)> &rcb,\n                   const drogon::orm::ExceptionCallback &ecb) const;\n\n  private:\n    friend drogon::orm::Mapper<Users>;\n    friend drogon::orm::BaseBuilder<Users, true, true>;\n    friend drogon::orm::BaseBuilder<Users, true, false>;\n    friend drogon::orm::BaseBuilder<Users, false, true>;\n    friend drogon::orm::BaseBuilder<Users, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<Users>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int32_t> id_;\n    std::shared_ptr<std::string> userId_;\n    std::shared_ptr<std::string> userName_;\n    std::shared_ptr<std::string> password_;\n    std::shared_ptr<std::string> orgName_;\n    std::shared_ptr<std::string> signature_;\n    std::shared_ptr<std::string> avatarId_;\n    std::shared_ptr<std::string> salt_;\n    std::shared_ptr<int8_t> admin_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[9] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        sql += \"id,\";\n        ++parametersCount;\n        if (dirtyFlag_[1])\n        {\n            sql += \"user_id,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[2])\n        {\n            sql += \"user_name,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[3])\n        {\n            sql += \"password,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[4])\n        {\n            sql += \"org_name,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[5])\n        {\n            sql += \"signature,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[6])\n        {\n            sql += \"avatar_id,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[7])\n        {\n            sql += \"salt,\";\n            ++parametersCount;\n        }\n        sql += \"admin,\";\n        ++parametersCount;\n        if (!dirtyFlag_[8])\n        {\n            needSelection = true;\n        }\n        needSelection = true;\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        sql += \"default,\";\n        if (dirtyFlag_[1])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[2])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[3])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[4])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[5])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[6])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[7])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[8])\n        {\n            sql.append(\"?,\");\n        }\n        else\n        {\n            sql += \"default,\";\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        sql.append(1, ')');\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace drogonTestMysql\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/mysql/Wallets.cc",
    "content": "/**\n *\n *  Wallets.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Wallets.h\"\n#include \"Users.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::drogonTestMysql;\n\nconst std::string Wallets::Cols::_id = \"id\";\nconst std::string Wallets::Cols::_user_id = \"user_id\";\nconst std::string Wallets::Cols::_amount = \"amount\";\nconst std::string Wallets::primaryKeyName = \"id\";\nconst bool Wallets::hasPrimaryKey = true;\nconst std::string Wallets::tableName = \"wallets\";\n\nconst std::vector<typename Wallets::MetaData> Wallets::metaData_ = {\n    {\"id\", \"int32_t\", \"int(11)\", 4, 1, 1, 1},\n    {\"user_id\", \"std::string\", \"varchar(32)\", 32, 0, 0, 0},\n    {\"amount\", \"std::string\", \"decimal(16,2)\", 0, 0, 0, 0}};\n\nconst std::string &Wallets::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nWallets::Wallets(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[\"id\"].as<int32_t>());\n        }\n        if (!r[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(r[\"user_id\"].as<std::string>());\n        }\n        if (!r[\"amount\"].isNull())\n        {\n            amount_ =\n                std::make_shared<std::string>(r[\"amount\"].as<std::string>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 3 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            userId_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 2;\n        if (!r[index].isNull())\n        {\n            amount_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n    }\n}\n\nWallets::Wallets(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            userId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            amount_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[2]].asString());\n        }\n    }\n}\n\nWallets::Wallets(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(pJson[\"user_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"amount\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"amount\"].isNull())\n        {\n            amount_ = std::make_shared<std::string>(pJson[\"amount\"].asString());\n        }\n    }\n}\n\nvoid Wallets::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            userId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            amount_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[2]].asString());\n        }\n    }\n}\n\nvoid Wallets::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(pJson[\"user_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"amount\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"amount\"].isNull())\n        {\n            amount_ = std::make_shared<std::string>(pJson[\"amount\"].asString());\n        }\n    }\n}\n\nconst int32_t &Wallets::getValueOfId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (id_)\n        return *id_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &Wallets::getId() const noexcept\n{\n    return id_;\n}\n\nvoid Wallets::setId(const int32_t &pId) noexcept\n{\n    id_ = std::make_shared<int32_t>(pId);\n    dirtyFlag_[0] = true;\n}\n\nconst typename Wallets::PrimaryKeyType &Wallets::getPrimaryKey() const\n{\n    assert(id_);\n    return *id_;\n}\n\nconst std::string &Wallets::getValueOfUserId() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (userId_)\n        return *userId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Wallets::getUserId() const noexcept\n{\n    return userId_;\n}\n\nvoid Wallets::setUserId(const std::string &pUserId) noexcept\n{\n    userId_ = std::make_shared<std::string>(pUserId);\n    dirtyFlag_[1] = true;\n}\n\nvoid Wallets::setUserId(std::string &&pUserId) noexcept\n{\n    userId_ = std::make_shared<std::string>(std::move(pUserId));\n    dirtyFlag_[1] = true;\n}\n\nvoid Wallets::setUserIdToNull() noexcept\n{\n    userId_.reset();\n    dirtyFlag_[1] = true;\n}\n\nconst std::string &Wallets::getValueOfAmount() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (amount_)\n        return *amount_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Wallets::getAmount() const noexcept\n{\n    return amount_;\n}\n\nvoid Wallets::setAmount(const std::string &pAmount) noexcept\n{\n    amount_ = std::make_shared<std::string>(pAmount);\n    dirtyFlag_[2] = true;\n}\n\nvoid Wallets::setAmount(std::string &&pAmount) noexcept\n{\n    amount_ = std::make_shared<std::string>(std::move(pAmount));\n    dirtyFlag_[2] = true;\n}\n\nvoid Wallets::setAmountToNull() noexcept\n{\n    amount_.reset();\n    dirtyFlag_[2] = true;\n}\n\nvoid Wallets::updateId(const uint64_t id)\n{\n    id_ = std::make_shared<int32_t>(static_cast<int32_t>(id));\n}\n\nconst std::vector<std::string> &Wallets::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"user_id\", \"amount\"};\n    return inCols;\n}\n\nvoid Wallets::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getUserId())\n        {\n            binder << getValueOfUserId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getAmount())\n        {\n            binder << getValueOfAmount();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Wallets::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    if (dirtyFlag_[2])\n    {\n        ret.push_back(getColumnName(2));\n    }\n    return ret;\n}\n\nvoid Wallets::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getUserId())\n        {\n            binder << getValueOfUserId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getAmount())\n        {\n            binder << getValueOfAmount();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Wallets::toJson() const\n{\n    Json::Value ret;\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getUserId())\n    {\n        ret[\"user_id\"] = getValueOfUserId();\n    }\n    else\n    {\n        ret[\"user_id\"] = Json::Value();\n    }\n    if (getAmount())\n    {\n        ret[\"amount\"] = getValueOfAmount();\n    }\n    else\n    {\n        ret[\"amount\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Wallets::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 3)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getId())\n            {\n                ret[pMasqueradingVector[0]] = getValueOfId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getUserId())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfUserId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (getAmount())\n            {\n                ret[pMasqueradingVector[2]] = getValueOfAmount();\n            }\n            else\n            {\n                ret[pMasqueradingVector[2]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getUserId())\n    {\n        ret[\"user_id\"] = getValueOfUserId();\n    }\n    else\n    {\n        ret[\"user_id\"] = Json::Value();\n    }\n    if (getAmount())\n    {\n        ret[\"amount\"] = getValueOfAmount();\n    }\n    else\n    {\n        ret[\"amount\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Wallets::validateJsonForCreation(const Json::Value &pJson,\n                                      std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        if (!validJsonOfField(1, \"user_id\", pJson[\"user_id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"amount\"))\n    {\n        if (!validJsonOfField(2, \"amount\", pJson[\"amount\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Wallets::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[2]))\n            {\n                if (!validJsonOfField(2,\n                                      pMasqueradingVector[2],\n                                      pJson[pMasqueradingVector[2]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Wallets::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        if (!validJsonOfField(1, \"user_id\", pJson[\"user_id\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"amount\"))\n    {\n        if (!validJsonOfField(2, \"amount\", pJson[\"amount\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Wallets::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[2].empty() &&\n            pJson.isMember(pMasqueradingVector[2]))\n        {\n            if (!validJsonOfField(2,\n                                  pMasqueradingVector[2],\n                                  pJson[pMasqueradingVector[2]],\n                                  err,\n                                  false))\n                return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Wallets::validJsonOfField(size_t index,\n                               const std::string &fieldName,\n                               const Json::Value &pJson,\n                               std::string &err,\n                               bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (isForCreation)\n            {\n                err = \"The automatic primary key cannot be set\";\n                return false;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 32)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 32)\";\n                return false;\n            }\n\n            break;\n        case 2:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n\nUsers Wallets::getUser(const DbClientPtr &clientPtr) const\n{\n    static const std::string sql = \"select * from users where user_id = ?\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *userId_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    if (r.size() == 0)\n    {\n        throw UnexpectedRows(\"0 rows found\");\n    }\n    else if (r.size() > 1)\n    {\n        throw UnexpectedRows(\"Found more than one row\");\n    }\n    return Users(r[0]);\n}\n\nvoid Wallets::getUser(const DbClientPtr &clientPtr,\n                      const std::function<void(Users)> &rcb,\n                      const ExceptionCallback &ecb) const\n{\n    static const std::string sql = \"select * from users where user_id = ?\";\n    *clientPtr << sql << *userId_ >> [rcb = std::move(rcb),\n                                      ecb](const Result &r) {\n        if (r.size() == 0)\n        {\n            ecb(UnexpectedRows(\"0 rows found\"));\n        }\n        else if (r.size() > 1)\n        {\n            ecb(UnexpectedRows(\"Found more than one row\"));\n        }\n        else\n        {\n            rcb(Users(r[0]));\n        }\n    } >> ecb;\n}\n"
  },
  {
    "path": "orm_lib/tests/mysql/Wallets.h",
    "content": "/**\n *\n *  Wallets.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace drogonTestMysql\n{\nclass Users;\n\nclass Wallets\n{\n  public:\n    struct Cols\n    {\n        static const std::string _id;\n        static const std::string _user_id;\n        static const std::string _amount;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = int32_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Wallets(const drogon::orm::Row &r,\n                     const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Wallets(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Wallets(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Wallets() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column id  */\n    /// Get the value of the column id, returns the default value if the column\n    /// is null\n    const int32_t &getValueOfId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getId() const noexcept;\n    /// Set the value of the column id\n    void setId(const int32_t &pId) noexcept;\n\n    /**  For column user_id  */\n    /// Get the value of the column user_id, returns the default value if the\n    /// column is null\n    const std::string &getValueOfUserId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getUserId() const noexcept;\n    /// Set the value of the column user_id\n    void setUserId(const std::string &pUserId) noexcept;\n    void setUserId(std::string &&pUserId) noexcept;\n    void setUserIdToNull() noexcept;\n\n    /**  For column amount  */\n    /// Get the value of the column amount, returns the default value if the\n    /// column is null\n    const std::string &getValueOfAmount() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getAmount() const noexcept;\n    /// Set the value of the column amount\n    void setAmount(const std::string &pAmount) noexcept;\n    void setAmount(std::string &&pAmount) noexcept;\n    void setAmountToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 3;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n    Users getUser(const drogon::orm::DbClientPtr &clientPtr) const;\n    void getUser(const drogon::orm::DbClientPtr &clientPtr,\n                 const std::function<void(Users)> &rcb,\n                 const drogon::orm::ExceptionCallback &ecb) const;\n\n  private:\n    friend drogon::orm::Mapper<Wallets>;\n    friend drogon::orm::BaseBuilder<Wallets, true, true>;\n    friend drogon::orm::BaseBuilder<Wallets, true, false>;\n    friend drogon::orm::BaseBuilder<Wallets, false, true>;\n    friend drogon::orm::BaseBuilder<Wallets, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<Wallets>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int32_t> id_;\n    std::shared_ptr<std::string> userId_;\n    std::shared_ptr<std::string> amount_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[3] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        sql += \"id,\";\n        ++parametersCount;\n        if (dirtyFlag_[1])\n        {\n            sql += \"user_id,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[2])\n        {\n            sql += \"amount,\";\n            ++parametersCount;\n        }\n        needSelection = true;\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        sql += \"default,\";\n        if (dirtyFlag_[1])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[2])\n        {\n            sql.append(\"?,\");\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        sql.append(1, ')');\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace drogonTestMysql\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/mysql/model.json",
    "content": "{\n    \"rdbms\": \"mysql\",\n    \"host\": \"127.0.0.1\",\n    \"port\": 3306,\n    \"dbname\": \"drogonTestMysql\",\n    \"user\": \"root\",\n    \"tables\": [\n        \"users\",\n        \"wallets\",\n        \"blog\",\n        \"category\",\n        \"blog_tag\",\n        \"tag\"\n    ],\n    \"relationships\": {\n        \"enabled\": true,\n        \"items\": [\n            {\n                \"type\": \"has one\",\n                \"original_table_name\": \"users\",\n                \"original_table_alias\": \"user\",\n                \"original_key\": \"user_id\",\n                \"target_table_name\": \"wallets\",\n                \"target_table_alias\": \"wallet\",\n                \"target_key\": \"user_id\",\n                \"enable_reverse\": true\n            },\n            {\n                \"type\": \"has many\",\n                \"original_table_name\": \"category\",\n                \"original_table_alias\": \"category\",\n                \"original_key\": \"id\",\n                \"target_table_name\": \"blog\",\n                \"target_table_alias\": \"blogs\",\n                \"target_key\": \"category_id\",\n                \"enable_reverse\": true\n            },\n            {\n                \"type\": \"many to many\",\n                \"original_table_name\": \"blog\",\n                \"original_table_alias\": \"blogs\",\n                \"original_key\": \"id\",\n                \"pivot_table\": {\n                    \"table_name\": \"blog_tag\",\n                    \"original_key\": \"blog_id\",\n                    \"target_key\": \"tag_id\"\n                },\n                \"target_table_name\": \"tag\",\n                \"target_table_alias\": \"tags\",\n                \"target_key\": \"id\",\n                \"enable_reverse\": true\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "orm_lib/tests/pipeline_test.cpp",
    "content": "#define DROGON_TEST_MAIN\n#include <drogon/drogon_test.h>\n#include <drogon/HttpAppFramework.h>\n#include <drogon/config.h>\n\nusing namespace drogon;\nusing namespace trantor;\n\n#if LIBPQ_SUPPORTS_BATCH_MODE\n\nstatic int step = 0;\nstatic std::vector<std::function<void()>> testSteps;\n\nstatic auto runNextStep = [] {\n    LOG_INFO << \"Step = \" << step;\n    if (step < testSteps.size())\n    {\n        app().getIOLoop(0)->runAfter(0.5, testSteps[step++]);\n    }\n    else\n    {\n        testSteps.clear();\n    }\n};\n\nDROGON_TEST(PgPipelineTest)\n{\n    auto testStep_init = [TEST_CTX]() {\n        auto clientPtr = app().getFastDbClient();\n        clientPtr->execSqlAsync(\n            \"drop table if exists drogon_test_pipeline;\",\n            [TEST_CTX](const drogon::orm::Result &r) {\n                LOG_INFO << \"Create table drogon_test_pipeline.\";\n            },\n            [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n                FAULT(\"PgPipelineTest_init(0) what():\" +\n                      std::string(e.base().what()));\n            });\n        clientPtr->execSqlAsync(\n            \"drop function if exists fn_drogon_test_pipeline;\",\n            [TEST_CTX](const drogon::orm::Result &r) {\n                LOG_INFO << \"Drop function fn_drogon_test_pipeline.\";\n            },\n            [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n                FAULT(\"PgPipelineTest_init(1) what():\" +\n                      std::string(e.base().what()));\n            });\n\n        clientPtr->execSqlAsync(\n            \"create table drogon_test_pipeline\"\n            \"(\"\n            \"   id      int     primary key,\"\n            \"   name    text    unique\"\n            \");\",\n            [TEST_CTX](const drogon::orm::Result &r) {\n                LOG_INFO << \"Create table drogon_test_pipeline.\";\n            },\n            [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n                FAULT(\"PgPipelineTest_init(2) what():\" +\n                      std::string(e.base().what()));\n            });\n\n        clientPtr->execSqlAsync(\n            \"insert into\"\n            \"   drogon_test_pipeline \"\n            \"values\"\n            \"   (1, 'trantor'),\"\n            \"   (2, 'drogon');\",\n            [TEST_CTX](const drogon::orm::Result &r) {\n                LOG_INFO << \"Insert data into drogon_test_pipeline.\";\n            },\n            [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n                FAULT(\"PgPipelineTest_init(3) what():\" +\n                      std::string(e.base().what()));\n            });\n\n        clientPtr->execSqlAsync(\n            \"create function fn_drogon_test_pipeline(id int, name text) \"\n            \"returns void \"\n            \"language plpgsql \"\n            \"as $$ \"\n            \"begin \"\n            \"   update drogon_test_pipeline t set name = $2 where t.id = $1;\"\n            \"end $$;\",\n            [TEST_CTX](const drogon::orm::Result &r) {\n                LOG_INFO << \"Insert data into drogon_test_pipeline.\";\n            },\n            [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n                FAULT(\"PgPipelineTest_init(4) what():\" +\n                      std::string(e.base().what()));\n            });\n\n        runNextStep();\n    };\n\n    auto testStep_allSuccess = [TEST_CTX]() {\n        auto clientPtr = app().getFastDbClient();\n        clientPtr->execSqlAsync(\n            \"select 1\",\n            [TEST_CTX](const drogon::orm::Result &r) {\n                MANDATE(r.size() == 1);\n            },\n            [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n                FAULT(\"PgPipelineTest_allSuccess(0) what():\" +\n                      std::string(e.base().what()));\n            });\n        clientPtr->execSqlAsync(\n            \"select * from drogon_test_pipeline\",\n            [TEST_CTX](const drogon::orm::Result &r) {\n                MANDATE(r.size() == 2);\n            },\n            [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n                FAULT(\"PgPipelineTest_allSuccess(1) what():\" +\n                      std::string(e.base().what()));\n            });\n\n        runNextStep();\n    };\n\n    auto testStep_testAbort = [TEST_CTX]() {\n        auto clientPtr = app().getFastDbClient();\n        clientPtr->execSqlAsync(\n            \"select name from drogon_test_pipeline where id = $1\",\n            [TEST_CTX](const drogon::orm::Result &r) {\n                MANDATE(r.size() == 1);\n                MANDATE(r[0][0].as<std::string>() == \"trantor\");\n            },\n            [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n                FAULT(\"PgPipelineTest_testAbort(0) what():\" +\n                      std::string(e.base().what()));\n            },\n            1);\n\n        // Update name\n        // NOTICE: update in a function to bypass auto sync\n        // DO NOT USE like this in production\n        clientPtr->execSqlAsync(\n            \"select * from fn_drogon_test_pipeline($1, $2)\",\n            [TEST_CTX](const drogon::orm::Result &r) { SUCCESS(); },\n            [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n                FAULT(\"PgPipelineTest_testAbort(1) what():\" +\n                      std::string(e.base().what()));\n            },\n            1,\n            \"terminus\");\n\n        // Check name\n        clientPtr->execSqlAsync(\n            \"select name from drogon_test_pipeline where id = $1\",\n            [TEST_CTX](const drogon::orm::Result &r) {\n                MANDATE(r.size() == 1);\n                MANDATE(r[0][0].as<std::string>() == \"terminus\");\n            },\n            [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n                FAULT(\"PgPipelineTest_testAbort(2) what():\" +\n                      std::string(e.base().what()));\n            },\n            1);\n\n        // Cause an error\n        clientPtr->execSqlAsync(\n            \"select 'abc'::int\",\n            [TEST_CTX](const drogon::orm::Result &r) {\n                FAULT(\"PgPipelineTest_testAbort(3) should not pass\");\n            },\n            [TEST_CTX](const drogon::orm::DrogonDbException &e) { SUCCESS(); },\n            1);\n\n        clientPtr->execSqlAsync(\n            \"select 1\",\n            [TEST_CTX](const drogon::orm::Result &r) {\n                FAULT(\"PgPipelineTest_testAbort(3) should not pass\");\n            },\n            [TEST_CTX](const drogon::orm::DrogonDbException &e) { SUCCESS(); },\n            1);\n\n        runNextStep();\n    };\n\n    auto testStep_afterAbort = [TEST_CTX]() {\n        auto clientPtr = app().getFastDbClient();\n        // Should remain unchanged, because last sync point was rollback.\n        clientPtr->execSqlAsync(\n            \"select name from drogon_test_pipeline where id = $1\",\n            [TEST_CTX](const drogon::orm::Result &r) {\n                MANDATE(r.size() == 1);\n                MANDATE(r[0][0].as<std::string>() == \"trantor\");\n            },\n            [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n                FAULT(\"PgPipelineTest_testAbort(0) what():\" +\n                      std::string(e.base().what()));\n            },\n            1);\n\n        runNextStep();\n    };\n\n    auto testStep_cleanup = [TEST_CTX]() {\n        auto clientPtr = app().getFastDbClient();\n        clientPtr->execSqlAsync(\n            \"drop table drogon_test_pipeline;\",\n            [TEST_CTX](const drogon::orm::Result &r) {\n                LOG_INFO << \"Drop table drogon_test_pipeline.\";\n            },\n            [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n                FAULT(\"PgPipelineTest_cleanup(0) what():\" +\n                      std::string(e.base().what()));\n            });\n\n        clientPtr->execSqlAsync(\n            \"drop function fn_drogon_test_pipeline;\",\n            [TEST_CTX](const drogon::orm::Result &r) {\n                LOG_INFO << \"Drop function fn_drogon_test_pipeline.\";\n            },\n            [TEST_CTX](const drogon::orm::DrogonDbException &e) {\n                FAULT(\"PgPipelineTest_cleanup(1) what():\" +\n                      std::string(e.base().what()));\n            });\n        runNextStep();\n    };\n\n    testSteps = {std::move(testStep_init),\n                 std::move(testStep_allSuccess),\n                 std::move(testStep_testAbort),\n                 std::move(testStep_afterAbort),\n                 std::move(testStep_cleanup)};\n\n    runNextStep();\n}\n\n#endif\n\nint main(int argc, char **argv)\n{\n    std::promise<void> p1;\n    std::future<void> f1 = p1.get_future();\n    app().setThreadNum(1);\n#if LIBPQ_SUPPORTS_BATCH_MODE\n    app().addDbClient(orm::PostgresConfig{\"127.0.0.1\",  // host\n                                          5432,         // port\n                                          \"postgres\",   // dbname\n                                          \"postgres\",   // username\n                                          \"12345\",      // password\n                                          1,            // connectionNum\n                                          \"default\",    // name\n                                          true,         // isFast\n                                          \"\",           // charset\n                                          10,           // timeout\n                                          true,         // autobatch\n                                          {}});\n#endif\n    std::thread thr([&]() {\n        app().getLoop()->queueInLoop([&]() { p1.set_value(); });\n        app().run();\n    });\n\n    f1.get();\n    int testStatus = test::run(argc, argv);\n    app().getLoop()->queueInLoop([]() { app().quit(); });\n    thr.join();\n    return testStatus;\n}\n"
  },
  {
    "path": "orm_lib/tests/postgresql/Blog.cc",
    "content": "/**\n *\n *  Blog.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Blog.h\"\n#include \"BlogTag.h\"\n#include \"Category.h\"\n#include \"Tag.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::postgres;\n\nconst std::string Blog::Cols::_id = \"id\";\nconst std::string Blog::Cols::_title = \"title\";\nconst std::string Blog::Cols::_category_id = \"category_id\";\nconst std::string Blog::primaryKeyName = \"id\";\nconst bool Blog::hasPrimaryKey = true;\nconst std::string Blog::tableName = \"blog\";\n\nconst std::vector<typename Blog::MetaData> Blog::metaData_ = {\n    {\"id\", \"int32_t\", \"integer\", 4, 1, 1, 1},\n    {\"title\", \"std::string\", \"character varying\", 30, 0, 0, 0},\n    {\"category_id\", \"int32_t\", \"integer\", 4, 0, 0, 0}};\n\nconst std::string &Blog::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nBlog::Blog(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[\"id\"].as<int32_t>());\n        }\n        if (!r[\"title\"].isNull())\n        {\n            title_ =\n                std::make_shared<std::string>(r[\"title\"].as<std::string>());\n        }\n        if (!r[\"category_id\"].isNull())\n        {\n            categoryId_ =\n                std::make_shared<int32_t>(r[\"category_id\"].as<int32_t>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 3 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            title_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 2;\n        if (!r[index].isNull())\n        {\n            categoryId_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n    }\n}\n\nBlog::Blog(const Json::Value &pJson,\n           const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            title_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            categoryId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[2]].asInt64());\n        }\n    }\n}\n\nBlog::Blog(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"title\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"title\"].isNull())\n        {\n            title_ = std::make_shared<std::string>(pJson[\"title\"].asString());\n        }\n    }\n    if (pJson.isMember(\"category_id\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"category_id\"].isNull())\n        {\n            categoryId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[\"category_id\"].asInt64());\n        }\n    }\n}\n\nvoid Blog::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            title_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            categoryId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[2]].asInt64());\n        }\n    }\n}\n\nvoid Blog::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"title\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"title\"].isNull())\n        {\n            title_ = std::make_shared<std::string>(pJson[\"title\"].asString());\n        }\n    }\n    if (pJson.isMember(\"category_id\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"category_id\"].isNull())\n        {\n            categoryId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[\"category_id\"].asInt64());\n        }\n    }\n}\n\nconst int32_t &Blog::getValueOfId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (id_)\n        return *id_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &Blog::getId() const noexcept\n{\n    return id_;\n}\n\nvoid Blog::setId(const int32_t &pId) noexcept\n{\n    id_ = std::make_shared<int32_t>(pId);\n    dirtyFlag_[0] = true;\n}\n\nconst typename Blog::PrimaryKeyType &Blog::getPrimaryKey() const\n{\n    assert(id_);\n    return *id_;\n}\n\nconst std::string &Blog::getValueOfTitle() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (title_)\n        return *title_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Blog::getTitle() const noexcept\n{\n    return title_;\n}\n\nvoid Blog::setTitle(const std::string &pTitle) noexcept\n{\n    title_ = std::make_shared<std::string>(pTitle);\n    dirtyFlag_[1] = true;\n}\n\nvoid Blog::setTitle(std::string &&pTitle) noexcept\n{\n    title_ = std::make_shared<std::string>(std::move(pTitle));\n    dirtyFlag_[1] = true;\n}\n\nvoid Blog::setTitleToNull() noexcept\n{\n    title_.reset();\n    dirtyFlag_[1] = true;\n}\n\nconst int32_t &Blog::getValueOfCategoryId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (categoryId_)\n        return *categoryId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &Blog::getCategoryId() const noexcept\n{\n    return categoryId_;\n}\n\nvoid Blog::setCategoryId(const int32_t &pCategoryId) noexcept\n{\n    categoryId_ = std::make_shared<int32_t>(pCategoryId);\n    dirtyFlag_[2] = true;\n}\n\nvoid Blog::setCategoryIdToNull() noexcept\n{\n    categoryId_.reset();\n    dirtyFlag_[2] = true;\n}\n\nvoid Blog::updateId(const uint64_t id)\n{\n}\n\nconst std::vector<std::string> &Blog::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"title\", \"category_id\"};\n    return inCols;\n}\n\nvoid Blog::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getTitle())\n        {\n            binder << getValueOfTitle();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getCategoryId())\n        {\n            binder << getValueOfCategoryId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Blog::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    if (dirtyFlag_[2])\n    {\n        ret.push_back(getColumnName(2));\n    }\n    return ret;\n}\n\nvoid Blog::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getTitle())\n        {\n            binder << getValueOfTitle();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getCategoryId())\n        {\n            binder << getValueOfCategoryId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Blog::toJson() const\n{\n    Json::Value ret;\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getTitle())\n    {\n        ret[\"title\"] = getValueOfTitle();\n    }\n    else\n    {\n        ret[\"title\"] = Json::Value();\n    }\n    if (getCategoryId())\n    {\n        ret[\"category_id\"] = getValueOfCategoryId();\n    }\n    else\n    {\n        ret[\"category_id\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Blog::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 3)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getId())\n            {\n                ret[pMasqueradingVector[0]] = getValueOfId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getTitle())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfTitle();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (getCategoryId())\n            {\n                ret[pMasqueradingVector[2]] = getValueOfCategoryId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[2]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getTitle())\n    {\n        ret[\"title\"] = getValueOfTitle();\n    }\n    else\n    {\n        ret[\"title\"] = Json::Value();\n    }\n    if (getCategoryId())\n    {\n        ret[\"category_id\"] = getValueOfCategoryId();\n    }\n    else\n    {\n        ret[\"category_id\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Blog::validateJsonForCreation(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"title\"))\n    {\n        if (!validJsonOfField(1, \"title\", pJson[\"title\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"category_id\"))\n    {\n        if (!validJsonOfField(\n                2, \"category_id\", pJson[\"category_id\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Blog::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[2]))\n            {\n                if (!validJsonOfField(2,\n                                      pMasqueradingVector[2],\n                                      pJson[pMasqueradingVector[2]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Blog::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"title\"))\n    {\n        if (!validJsonOfField(1, \"title\", pJson[\"title\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"category_id\"))\n    {\n        if (!validJsonOfField(\n                2, \"category_id\", pJson[\"category_id\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Blog::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[2].empty() &&\n            pJson.isMember(pMasqueradingVector[2]))\n        {\n            if (!validJsonOfField(2,\n                                  pMasqueradingVector[2],\n                                  pJson[pMasqueradingVector[2]],\n                                  err,\n                                  false))\n                return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Blog::validJsonOfField(size_t index,\n                            const std::string &fieldName,\n                            const Json::Value &pJson,\n                            std::string &err,\n                            bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (isForCreation)\n            {\n                err = \"The automatic primary key cannot be set\";\n                return false;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 30)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 30)\";\n                return false;\n            }\n\n            break;\n        case 2:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n\nCategory Blog::getCategory(const DbClientPtr &clientPtr) const\n{\n    static const std::string sql = \"select * from category where id = $1\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *categoryId_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    if (r.size() == 0)\n    {\n        throw UnexpectedRows(\"0 rows found\");\n    }\n    else if (r.size() > 1)\n    {\n        throw UnexpectedRows(\"Found more than one row\");\n    }\n    return Category(r[0]);\n}\n\nvoid Blog::getCategory(const DbClientPtr &clientPtr,\n                       const std::function<void(Category)> &rcb,\n                       const ExceptionCallback &ecb) const\n{\n    static const std::string sql = \"select * from category where id = $1\";\n    *clientPtr << sql << *categoryId_ >> [rcb = std::move(rcb),\n                                          ecb](const Result &r) {\n        if (r.size() == 0)\n        {\n            ecb(UnexpectedRows(\"0 rows found\"));\n        }\n        else if (r.size() > 1)\n        {\n            ecb(UnexpectedRows(\"Found more than one row\"));\n        }\n        else\n        {\n            rcb(Category(r[0]));\n        }\n    } >> ecb;\n}\n\nstd::vector<std::pair<Tag, BlogTag>> Blog::getTags(\n    const DbClientPtr &clientPtr) const\n{\n    static const std::string sql =\n        \"select * from tag,blog_tag where blog_tag.blog_id = $1 and \"\n        \"blog_tag.tag_id = tag.id\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *id_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    std::vector<std::pair<Tag, BlogTag>> ret;\n    ret.reserve(r.size());\n    for (auto const &row : r)\n    {\n        ret.emplace_back(\n            std::pair<Tag, BlogTag>(Tag(row),\n                                    BlogTag(row, Tag::getColumnNumber())));\n    }\n    return ret;\n}\n\nvoid Blog::getTags(\n    const DbClientPtr &clientPtr,\n    const std::function<void(std::vector<std::pair<Tag, BlogTag>>)> &rcb,\n    const ExceptionCallback &ecb) const\n{\n    static const std::string sql =\n        \"select * from tag,blog_tag where blog_tag.blog_id = $1 and \"\n        \"blog_tag.tag_id = tag.id\";\n    *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) {\n        std::vector<std::pair<Tag, BlogTag>> ret;\n        ret.reserve(r.size());\n        for (auto const &row : r)\n        {\n            ret.emplace_back(\n                std::pair<Tag, BlogTag>(Tag(row),\n                                        BlogTag(row, Tag::getColumnNumber())));\n        }\n        rcb(ret);\n    } >> ecb;\n}\n"
  },
  {
    "path": "orm_lib/tests/postgresql/Blog.h",
    "content": "/**\n *\n *  Blog.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace postgres\n{\nclass BlogTag;\nclass Category;\nclass Tag;\n\nclass Blog\n{\n  public:\n    struct Cols\n    {\n        static const std::string _id;\n        static const std::string _title;\n        static const std::string _category_id;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = int32_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Blog(const drogon::orm::Row &r,\n                  const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Blog(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Blog(const Json::Value &pJson,\n         const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Blog() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column id  */\n    /// Get the value of the column id, returns the default value if the column\n    /// is null\n    const int32_t &getValueOfId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getId() const noexcept;\n    /// Set the value of the column id\n    void setId(const int32_t &pId) noexcept;\n\n    /**  For column title  */\n    /// Get the value of the column title, returns the default value if the\n    /// column is null\n    const std::string &getValueOfTitle() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getTitle() const noexcept;\n    /// Set the value of the column title\n    void setTitle(const std::string &pTitle) noexcept;\n    void setTitle(std::string &&pTitle) noexcept;\n    void setTitleToNull() noexcept;\n\n    /**  For column category_id  */\n    /// Get the value of the column category_id, returns the default value if\n    /// the column is null\n    const int32_t &getValueOfCategoryId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getCategoryId() const noexcept;\n    /// Set the value of the column category_id\n    void setCategoryId(const int32_t &pCategoryId) noexcept;\n    void setCategoryIdToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 3;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n    Category getCategory(const drogon::orm::DbClientPtr &clientPtr) const;\n    void getCategory(const drogon::orm::DbClientPtr &clientPtr,\n                     const std::function<void(Category)> &rcb,\n                     const drogon::orm::ExceptionCallback &ecb) const;\n    std::vector<std::pair<Tag, BlogTag>> getTags(\n        const drogon::orm::DbClientPtr &clientPtr) const;\n    void getTags(\n        const drogon::orm::DbClientPtr &clientPtr,\n        const std::function<void(std::vector<std::pair<Tag, BlogTag>>)> &rcb,\n        const drogon::orm::ExceptionCallback &ecb) const;\n\n  private:\n    friend drogon::orm::Mapper<Blog>;\n    friend drogon::orm::BaseBuilder<Blog, true, true>;\n    friend drogon::orm::BaseBuilder<Blog, true, false>;\n    friend drogon::orm::BaseBuilder<Blog, false, true>;\n    friend drogon::orm::BaseBuilder<Blog, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<Blog>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int32_t> id_;\n    std::shared_ptr<std::string> title_;\n    std::shared_ptr<int32_t> categoryId_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[3] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where id = $1\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where id = $1\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        sql += \"id,\";\n        ++parametersCount;\n        sql += \"title,\";\n        ++parametersCount;\n        if (!dirtyFlag_[1])\n        {\n            needSelection = true;\n        }\n        if (dirtyFlag_[2])\n        {\n            sql += \"category_id,\";\n            ++parametersCount;\n        }\n        needSelection = true;\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        int placeholder = 1;\n        char placeholderStr[64];\n        size_t n = 0;\n        sql += \"default,\";\n        if (dirtyFlag_[1])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        else\n        {\n            sql += \"default,\";\n        }\n        if (dirtyFlag_[2])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        if (needSelection)\n        {\n            sql.append(\") returning *\");\n        }\n        else\n        {\n            sql.append(1, ')');\n        }\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace postgres\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/postgresql/BlogTag.cc",
    "content": "/**\n *\n *  BlogTag.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"BlogTag.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::postgres;\n\nconst std::string BlogTag::Cols::_blog_id = \"blog_id\";\nconst std::string BlogTag::Cols::_tag_id = \"tag_id\";\nconst std::vector<std::string> BlogTag::primaryKeyName = {\"blog_id\", \"tag_id\"};\nconst bool BlogTag::hasPrimaryKey = true;\nconst std::string BlogTag::tableName = \"blog_tag\";\n\nconst std::vector<typename BlogTag::MetaData> BlogTag::metaData_ = {\n    {\"blog_id\", \"int32_t\", \"integer\", 4, 0, 1, 1},\n    {\"tag_id\", \"int32_t\", \"integer\", 4, 0, 1, 1}};\n\nconst std::string &BlogTag::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nBlogTag::BlogTag(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"blog_id\"].isNull())\n        {\n            blogId_ = std::make_shared<int32_t>(r[\"blog_id\"].as<int32_t>());\n        }\n        if (!r[\"tag_id\"].isNull())\n        {\n            tagId_ = std::make_shared<int32_t>(r[\"tag_id\"].as<int32_t>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 2 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            blogId_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            tagId_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n    }\n}\n\nBlogTag::BlogTag(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            blogId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            tagId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[1]].asInt64());\n        }\n    }\n}\n\nBlogTag::BlogTag(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"blog_id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"blog_id\"].isNull())\n        {\n            blogId_ =\n                std::make_shared<int32_t>((int32_t)pJson[\"blog_id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"tag_id\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"tag_id\"].isNull())\n        {\n            tagId_ =\n                std::make_shared<int32_t>((int32_t)pJson[\"tag_id\"].asInt64());\n        }\n    }\n}\n\nvoid BlogTag::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            blogId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            tagId_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[1]].asInt64());\n        }\n    }\n}\n\nvoid BlogTag::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"blog_id\"))\n    {\n        if (!pJson[\"blog_id\"].isNull())\n        {\n            blogId_ =\n                std::make_shared<int32_t>((int32_t)pJson[\"blog_id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"tag_id\"))\n    {\n        if (!pJson[\"tag_id\"].isNull())\n        {\n            tagId_ =\n                std::make_shared<int32_t>((int32_t)pJson[\"tag_id\"].asInt64());\n        }\n    }\n}\n\nconst int32_t &BlogTag::getValueOfBlogId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (blogId_)\n        return *blogId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &BlogTag::getBlogId() const noexcept\n{\n    return blogId_;\n}\n\nvoid BlogTag::setBlogId(const int32_t &pBlogId) noexcept\n{\n    blogId_ = std::make_shared<int32_t>(pBlogId);\n    dirtyFlag_[0] = true;\n}\n\nconst int32_t &BlogTag::getValueOfTagId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (tagId_)\n        return *tagId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &BlogTag::getTagId() const noexcept\n{\n    return tagId_;\n}\n\nvoid BlogTag::setTagId(const int32_t &pTagId) noexcept\n{\n    tagId_ = std::make_shared<int32_t>(pTagId);\n    dirtyFlag_[1] = true;\n}\n\nvoid BlogTag::updateId(const uint64_t id)\n{\n}\n\ntypename BlogTag::PrimaryKeyType BlogTag::getPrimaryKey() const\n{\n    return std::make_tuple(*blogId_, *tagId_);\n}\n\nconst std::vector<std::string> &BlogTag::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"blog_id\", \"tag_id\"};\n    return inCols;\n}\n\nvoid BlogTag::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[0])\n    {\n        if (getBlogId())\n        {\n            binder << getValueOfBlogId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[1])\n    {\n        if (getTagId())\n        {\n            binder << getValueOfTagId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> BlogTag::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[0])\n    {\n        ret.push_back(getColumnName(0));\n    }\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    return ret;\n}\n\nvoid BlogTag::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[0])\n    {\n        if (getBlogId())\n        {\n            binder << getValueOfBlogId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[1])\n    {\n        if (getTagId())\n        {\n            binder << getValueOfTagId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value BlogTag::toJson() const\n{\n    Json::Value ret;\n    if (getBlogId())\n    {\n        ret[\"blog_id\"] = getValueOfBlogId();\n    }\n    else\n    {\n        ret[\"blog_id\"] = Json::Value();\n    }\n    if (getTagId())\n    {\n        ret[\"tag_id\"] = getValueOfTagId();\n    }\n    else\n    {\n        ret[\"tag_id\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value BlogTag::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 2)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getBlogId())\n            {\n                ret[pMasqueradingVector[0]] = getValueOfBlogId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getTagId())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfTagId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getBlogId())\n    {\n        ret[\"blog_id\"] = getValueOfBlogId();\n    }\n    else\n    {\n        ret[\"blog_id\"] = Json::Value();\n    }\n    if (getTagId())\n    {\n        ret[\"tag_id\"] = getValueOfTagId();\n    }\n    else\n    {\n        ret[\"tag_id\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool BlogTag::validateJsonForCreation(const Json::Value &pJson,\n                                      std::string &err)\n{\n    if (pJson.isMember(\"blog_id\"))\n    {\n        if (!validJsonOfField(0, \"blog_id\", pJson[\"blog_id\"], err, true))\n            return false;\n    }\n    else\n    {\n        err = \"The blog_id column cannot be null\";\n        return false;\n    }\n    if (pJson.isMember(\"tag_id\"))\n    {\n        if (!validJsonOfField(1, \"tag_id\", pJson[\"tag_id\"], err, true))\n            return false;\n    }\n    else\n    {\n        err = \"The tag_id column cannot be null\";\n        return false;\n    }\n    return true;\n}\n\nbool BlogTag::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n            else\n            {\n                err =\n                    \"The \" + pMasqueradingVector[0] + \" column cannot be null\";\n                return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n            else\n            {\n                err =\n                    \"The \" + pMasqueradingVector[1] + \" column cannot be null\";\n                return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool BlogTag::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"blog_id\"))\n    {\n        if (!validJsonOfField(0, \"blog_id\", pJson[\"blog_id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"tag_id\"))\n    {\n        if (!validJsonOfField(1, \"tag_id\", pJson[\"tag_id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    return true;\n}\n\nbool BlogTag::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool BlogTag::validJsonOfField(size_t index,\n                               const std::string &fieldName,\n                               const Json::Value &pJson,\n                               std::string &err,\n                               bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n"
  },
  {
    "path": "orm_lib/tests/postgresql/BlogTag.h",
    "content": "/**\n *\n *  BlogTag.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace postgres\n{\n\nclass BlogTag\n{\n  public:\n    struct Cols\n    {\n        static const std::string _blog_id;\n        static const std::string _tag_id;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::vector<std::string> primaryKeyName;\n    using PrimaryKeyType = std::tuple<int32_t, int32_t>;  // blog_id,tag_id\n    PrimaryKeyType getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit BlogTag(const drogon::orm::Row &r,\n                     const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit BlogTag(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    BlogTag(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    BlogTag() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column blog_id  */\n    /// Get the value of the column blog_id, returns the default value if the\n    /// column is null\n    const int32_t &getValueOfBlogId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getBlogId() const noexcept;\n    /// Set the value of the column blog_id\n    void setBlogId(const int32_t &pBlogId) noexcept;\n\n    /**  For column tag_id  */\n    /// Get the value of the column tag_id, returns the default value if the\n    /// column is null\n    const int32_t &getValueOfTagId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getTagId() const noexcept;\n    /// Set the value of the column tag_id\n    void setTagId(const int32_t &pTagId) noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 2;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n  private:\n    friend drogon::orm::Mapper<BlogTag>;\n    friend drogon::orm::BaseBuilder<BlogTag, true, true>;\n    friend drogon::orm::BaseBuilder<BlogTag, true, false>;\n    friend drogon::orm::BaseBuilder<BlogTag, false, true>;\n    friend drogon::orm::BaseBuilder<BlogTag, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<BlogTag>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int32_t> blogId_;\n    std::shared_ptr<int32_t> tagId_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[2] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql = \"select * from \" + tableName +\n                                       \" where blog_id = $1 and tag_id = $2\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where blog_id = $1 and tag_id = $2\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        if (dirtyFlag_[0])\n        {\n            sql += \"blog_id,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[1])\n        {\n            sql += \"tag_id,\";\n            ++parametersCount;\n        }\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        int placeholder = 1;\n        char placeholderStr[64];\n        size_t n = 0;\n        if (dirtyFlag_[0])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        if (dirtyFlag_[1])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        if (needSelection)\n        {\n            sql.append(\") returning *\");\n        }\n        else\n        {\n            sql.append(1, ')');\n        }\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace postgres\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/postgresql/Category.cc",
    "content": "/**\n *\n *  Category.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Category.h\"\n#include \"Blog.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::postgres;\n\nconst std::string Category::Cols::_id = \"id\";\nconst std::string Category::Cols::_name = \"name\";\nconst std::string Category::primaryKeyName = \"id\";\nconst bool Category::hasPrimaryKey = true;\nconst std::string Category::tableName = \"category\";\n\nconst std::vector<typename Category::MetaData> Category::metaData_ = {\n    {\"id\", \"int32_t\", \"integer\", 4, 1, 1, 1},\n    {\"name\", \"std::string\", \"character varying\", 30, 0, 0, 0}};\n\nconst std::string &Category::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nCategory::Category(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[\"id\"].as<int32_t>());\n        }\n        if (!r[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(r[\"name\"].as<std::string>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 2 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            name_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n    }\n}\n\nCategory::Category(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            name_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n}\n\nCategory::Category(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(pJson[\"name\"].asString());\n        }\n    }\n}\n\nvoid Category::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            name_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n}\n\nvoid Category::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(pJson[\"name\"].asString());\n        }\n    }\n}\n\nconst int32_t &Category::getValueOfId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (id_)\n        return *id_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &Category::getId() const noexcept\n{\n    return id_;\n}\n\nvoid Category::setId(const int32_t &pId) noexcept\n{\n    id_ = std::make_shared<int32_t>(pId);\n    dirtyFlag_[0] = true;\n}\n\nconst typename Category::PrimaryKeyType &Category::getPrimaryKey() const\n{\n    assert(id_);\n    return *id_;\n}\n\nconst std::string &Category::getValueOfName() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (name_)\n        return *name_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Category::getName() const noexcept\n{\n    return name_;\n}\n\nvoid Category::setName(const std::string &pName) noexcept\n{\n    name_ = std::make_shared<std::string>(pName);\n    dirtyFlag_[1] = true;\n}\n\nvoid Category::setName(std::string &&pName) noexcept\n{\n    name_ = std::make_shared<std::string>(std::move(pName));\n    dirtyFlag_[1] = true;\n}\n\nvoid Category::setNameToNull() noexcept\n{\n    name_.reset();\n    dirtyFlag_[1] = true;\n}\n\nvoid Category::updateId(const uint64_t id)\n{\n}\n\nconst std::vector<std::string> &Category::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"name\"};\n    return inCols;\n}\n\nvoid Category::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getName())\n        {\n            binder << getValueOfName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Category::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    return ret;\n}\n\nvoid Category::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getName())\n        {\n            binder << getValueOfName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Category::toJson() const\n{\n    Json::Value ret;\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getName())\n    {\n        ret[\"name\"] = getValueOfName();\n    }\n    else\n    {\n        ret[\"name\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Category::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 2)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getId())\n            {\n                ret[pMasqueradingVector[0]] = getValueOfId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getName())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfName();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getName())\n    {\n        ret[\"name\"] = getValueOfName();\n    }\n    else\n    {\n        ret[\"name\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Category::validateJsonForCreation(const Json::Value &pJson,\n                                       std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        if (!validJsonOfField(1, \"name\", pJson[\"name\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Category::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Category::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        if (!validJsonOfField(1, \"name\", pJson[\"name\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Category::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Category::validJsonOfField(size_t index,\n                                const std::string &fieldName,\n                                const Json::Value &pJson,\n                                std::string &err,\n                                bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (isForCreation)\n            {\n                err = \"The automatic primary key cannot be set\";\n                return false;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 30)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 30)\";\n                return false;\n            }\n\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n\nstd::vector<Blog> Category::getBlogs(const DbClientPtr &clientPtr) const\n{\n    static const std::string sql = \"select * from blog where category_id = $1\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *id_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    std::vector<Blog> ret;\n    ret.reserve(r.size());\n    for (auto const &row : r)\n    {\n        ret.emplace_back(Blog(row));\n    }\n    return ret;\n}\n\nvoid Category::getBlogs(const DbClientPtr &clientPtr,\n                        const std::function<void(std::vector<Blog>)> &rcb,\n                        const ExceptionCallback &ecb) const\n{\n    static const std::string sql = \"select * from blog where category_id = $1\";\n    *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) {\n        std::vector<Blog> ret;\n        ret.reserve(r.size());\n        for (auto const &row : r)\n        {\n            ret.emplace_back(Blog(row));\n        }\n        rcb(ret);\n    } >> ecb;\n}\n"
  },
  {
    "path": "orm_lib/tests/postgresql/Category.h",
    "content": "/**\n *\n *  Category.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace postgres\n{\nclass Blog;\n\nclass Category\n{\n  public:\n    struct Cols\n    {\n        static const std::string _id;\n        static const std::string _name;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = int32_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Category(const drogon::orm::Row &r,\n                      const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Category(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Category(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Category() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column id  */\n    /// Get the value of the column id, returns the default value if the column\n    /// is null\n    const int32_t &getValueOfId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getId() const noexcept;\n    /// Set the value of the column id\n    void setId(const int32_t &pId) noexcept;\n\n    /**  For column name  */\n    /// Get the value of the column name, returns the default value if the\n    /// column is null\n    const std::string &getValueOfName() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getName() const noexcept;\n    /// Set the value of the column name\n    void setName(const std::string &pName) noexcept;\n    void setName(std::string &&pName) noexcept;\n    void setNameToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 2;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n    std::vector<Blog> getBlogs(const drogon::orm::DbClientPtr &clientPtr) const;\n    void getBlogs(const drogon::orm::DbClientPtr &clientPtr,\n                  const std::function<void(std::vector<Blog>)> &rcb,\n                  const drogon::orm::ExceptionCallback &ecb) const;\n\n  private:\n    friend drogon::orm::Mapper<Category>;\n    friend drogon::orm::BaseBuilder<Category, true, true>;\n    friend drogon::orm::BaseBuilder<Category, true, false>;\n    friend drogon::orm::BaseBuilder<Category, false, true>;\n    friend drogon::orm::BaseBuilder<Category, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<Category>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int32_t> id_;\n    std::shared_ptr<std::string> name_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[2] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where id = $1\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where id = $1\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        sql += \"id,\";\n        ++parametersCount;\n        sql += \"name,\";\n        ++parametersCount;\n        if (!dirtyFlag_[1])\n        {\n            needSelection = true;\n        }\n        needSelection = true;\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        int placeholder = 1;\n        char placeholderStr[64];\n        size_t n = 0;\n        sql += \"default,\";\n        if (dirtyFlag_[1])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        else\n        {\n            sql += \"default,\";\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        if (needSelection)\n        {\n            sql.append(\") returning *\");\n        }\n        else\n        {\n            sql.append(1, ')');\n        }\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace postgres\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/postgresql/Tag.cc",
    "content": "/**\n *\n *  Tag.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Tag.h\"\n#include \"Blog.h\"\n#include \"BlogTag.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::postgres;\n\nconst std::string Tag::Cols::_id = \"id\";\nconst std::string Tag::Cols::_name = \"name\";\nconst std::string Tag::primaryKeyName = \"id\";\nconst bool Tag::hasPrimaryKey = true;\nconst std::string Tag::tableName = \"tag\";\n\nconst std::vector<typename Tag::MetaData> Tag::metaData_ = {\n    {\"id\", \"int32_t\", \"integer\", 4, 1, 1, 1},\n    {\"name\", \"std::string\", \"character varying\", 30, 0, 0, 0}};\n\nconst std::string &Tag::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nTag::Tag(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[\"id\"].as<int32_t>());\n        }\n        if (!r[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(r[\"name\"].as<std::string>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 2 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            name_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n    }\n}\n\nTag::Tag(const Json::Value &pJson,\n         const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            name_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n}\n\nTag::Tag(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(pJson[\"name\"].asString());\n        }\n    }\n}\n\nvoid Tag::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            name_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n}\n\nvoid Tag::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(pJson[\"name\"].asString());\n        }\n    }\n}\n\nconst int32_t &Tag::getValueOfId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (id_)\n        return *id_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &Tag::getId() const noexcept\n{\n    return id_;\n}\n\nvoid Tag::setId(const int32_t &pId) noexcept\n{\n    id_ = std::make_shared<int32_t>(pId);\n    dirtyFlag_[0] = true;\n}\n\nconst typename Tag::PrimaryKeyType &Tag::getPrimaryKey() const\n{\n    assert(id_);\n    return *id_;\n}\n\nconst std::string &Tag::getValueOfName() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (name_)\n        return *name_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Tag::getName() const noexcept\n{\n    return name_;\n}\n\nvoid Tag::setName(const std::string &pName) noexcept\n{\n    name_ = std::make_shared<std::string>(pName);\n    dirtyFlag_[1] = true;\n}\n\nvoid Tag::setName(std::string &&pName) noexcept\n{\n    name_ = std::make_shared<std::string>(std::move(pName));\n    dirtyFlag_[1] = true;\n}\n\nvoid Tag::setNameToNull() noexcept\n{\n    name_.reset();\n    dirtyFlag_[1] = true;\n}\n\nvoid Tag::updateId(const uint64_t id)\n{\n}\n\nconst std::vector<std::string> &Tag::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"name\"};\n    return inCols;\n}\n\nvoid Tag::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getName())\n        {\n            binder << getValueOfName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Tag::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    return ret;\n}\n\nvoid Tag::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getName())\n        {\n            binder << getValueOfName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Tag::toJson() const\n{\n    Json::Value ret;\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getName())\n    {\n        ret[\"name\"] = getValueOfName();\n    }\n    else\n    {\n        ret[\"name\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Tag::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 2)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getId())\n            {\n                ret[pMasqueradingVector[0]] = getValueOfId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getName())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfName();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getName())\n    {\n        ret[\"name\"] = getValueOfName();\n    }\n    else\n    {\n        ret[\"name\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Tag::validateJsonForCreation(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        if (!validJsonOfField(1, \"name\", pJson[\"name\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Tag::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Tag::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        if (!validJsonOfField(1, \"name\", pJson[\"name\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Tag::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Tag::validJsonOfField(size_t index,\n                           const std::string &fieldName,\n                           const Json::Value &pJson,\n                           std::string &err,\n                           bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (isForCreation)\n            {\n                err = \"The automatic primary key cannot be set\";\n                return false;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 30)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 30)\";\n                return false;\n            }\n\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n\nstd::vector<std::pair<Blog, BlogTag>> Tag::getBlogs(\n    const DbClientPtr &clientPtr) const\n{\n    static const std::string sql =\n        \"select * from blog,blog_tag where blog_tag.tag_id = $1 and \"\n        \"blog_tag.blog_id = blog.id\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *id_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    std::vector<std::pair<Blog, BlogTag>> ret;\n    ret.reserve(r.size());\n    for (auto const &row : r)\n    {\n        ret.emplace_back(\n            std::pair<Blog, BlogTag>(Blog(row),\n                                     BlogTag(row, Blog::getColumnNumber())));\n    }\n    return ret;\n}\n\nvoid Tag::getBlogs(\n    const DbClientPtr &clientPtr,\n    const std::function<void(std::vector<std::pair<Blog, BlogTag>>)> &rcb,\n    const ExceptionCallback &ecb) const\n{\n    static const std::string sql =\n        \"select * from blog,blog_tag where blog_tag.tag_id = $1 and \"\n        \"blog_tag.blog_id = blog.id\";\n    *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) {\n        std::vector<std::pair<Blog, BlogTag>> ret;\n        ret.reserve(r.size());\n        for (auto const &row : r)\n        {\n            ret.emplace_back(std::pair<Blog, BlogTag>(\n                Blog(row), BlogTag(row, Blog::getColumnNumber())));\n        }\n        rcb(ret);\n    } >> ecb;\n}\n"
  },
  {
    "path": "orm_lib/tests/postgresql/Tag.h",
    "content": "/**\n *\n *  Tag.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace postgres\n{\nclass Blog;\nclass BlogTag;\n\nclass Tag\n{\n  public:\n    struct Cols\n    {\n        static const std::string _id;\n        static const std::string _name;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = int32_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Tag(const drogon::orm::Row &r,\n                 const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Tag(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Tag(const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Tag() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column id  */\n    /// Get the value of the column id, returns the default value if the column\n    /// is null\n    const int32_t &getValueOfId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getId() const noexcept;\n    /// Set the value of the column id\n    void setId(const int32_t &pId) noexcept;\n\n    /**  For column name  */\n    /// Get the value of the column name, returns the default value if the\n    /// column is null\n    const std::string &getValueOfName() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getName() const noexcept;\n    /// Set the value of the column name\n    void setName(const std::string &pName) noexcept;\n    void setName(std::string &&pName) noexcept;\n    void setNameToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 2;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n    std::vector<std::pair<Blog, BlogTag>> getBlogs(\n        const drogon::orm::DbClientPtr &clientPtr) const;\n    void getBlogs(\n        const drogon::orm::DbClientPtr &clientPtr,\n        const std::function<void(std::vector<std::pair<Blog, BlogTag>>)> &rcb,\n        const drogon::orm::ExceptionCallback &ecb) const;\n\n  private:\n    friend drogon::orm::Mapper<Tag>;\n    friend drogon::orm::BaseBuilder<Tag, true, true>;\n    friend drogon::orm::BaseBuilder<Tag, true, false>;\n    friend drogon::orm::BaseBuilder<Tag, false, true>;\n    friend drogon::orm::BaseBuilder<Tag, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<Tag>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int32_t> id_;\n    std::shared_ptr<std::string> name_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[2] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where id = $1\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where id = $1\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        sql += \"id,\";\n        ++parametersCount;\n        sql += \"name,\";\n        ++parametersCount;\n        if (!dirtyFlag_[1])\n        {\n            needSelection = true;\n        }\n        needSelection = true;\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        int placeholder = 1;\n        char placeholderStr[64];\n        size_t n = 0;\n        sql += \"default,\";\n        if (dirtyFlag_[1])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        else\n        {\n            sql += \"default,\";\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        if (needSelection)\n        {\n            sql.append(\") returning *\");\n        }\n        else\n        {\n            sql.append(1, ')');\n        }\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace postgres\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/postgresql/Users.cc",
    "content": "/**\n *\n *  Users.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Users.h\"\n#include \"Wallets.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::postgres;\n\nconst std::string Users::Cols::_user_id = \"user_id\";\nconst std::string Users::Cols::_user_name = \"user_name\";\nconst std::string Users::Cols::_password = \"password\";\nconst std::string Users::Cols::_org_name = \"org_name\";\nconst std::string Users::Cols::_signature = \"signature\";\nconst std::string Users::Cols::_avatar_id = \"avatar_id\";\nconst std::string Users::Cols::_id = \"id\";\nconst std::string Users::Cols::_salt = \"salt\";\nconst std::string Users::Cols::_admin = \"admin\";\nconst std::string Users::primaryKeyName = \"id\";\nconst bool Users::hasPrimaryKey = true;\nconst std::string Users::tableName = \"users\";\n\nconst std::vector<typename Users::MetaData> Users::metaData_ = {\n    {\"user_id\", \"std::string\", \"character varying\", 32, 0, 0, 0},\n    {\"user_name\", \"std::string\", \"character varying\", 64, 0, 0, 0},\n    {\"password\", \"std::string\", \"character varying\", 64, 0, 0, 0},\n    {\"org_name\", \"std::string\", \"character varying\", 20, 0, 0, 0},\n    {\"signature\", \"std::string\", \"character varying\", 50, 0, 0, 0},\n    {\"avatar_id\", \"std::string\", \"character varying\", 32, 0, 0, 0},\n    {\"id\", \"int32_t\", \"integer\", 4, 1, 1, 1},\n    {\"salt\", \"std::string\", \"character varying\", 20, 0, 0, 0},\n    {\"admin\", \"bool\", \"boolean\", 1, 0, 0, 0}};\n\nconst std::string &Users::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nUsers::Users(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(r[\"user_id\"].as<std::string>());\n        }\n        if (!r[\"user_name\"].isNull())\n        {\n            userName_ =\n                std::make_shared<std::string>(r[\"user_name\"].as<std::string>());\n        }\n        if (!r[\"password\"].isNull())\n        {\n            password_ =\n                std::make_shared<std::string>(r[\"password\"].as<std::string>());\n        }\n        if (!r[\"org_name\"].isNull())\n        {\n            orgName_ =\n                std::make_shared<std::string>(r[\"org_name\"].as<std::string>());\n        }\n        if (!r[\"signature\"].isNull())\n        {\n            signature_ =\n                std::make_shared<std::string>(r[\"signature\"].as<std::string>());\n        }\n        if (!r[\"avatar_id\"].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(r[\"avatar_id\"].as<std::string>());\n        }\n        if (!r[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[\"id\"].as<int32_t>());\n        }\n        if (!r[\"salt\"].isNull())\n        {\n            salt_ = std::make_shared<std::string>(r[\"salt\"].as<std::string>());\n        }\n        if (!r[\"admin\"].isNull())\n        {\n            admin_ = std::make_shared<bool>(r[\"admin\"].as<bool>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 9 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            userId_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            userName_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 2;\n        if (!r[index].isNull())\n        {\n            password_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 3;\n        if (!r[index].isNull())\n        {\n            orgName_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 4;\n        if (!r[index].isNull())\n        {\n            signature_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 5;\n        if (!r[index].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 6;\n        if (!r[index].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n        index = offset + 7;\n        if (!r[index].isNull())\n        {\n            salt_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 8;\n        if (!r[index].isNull())\n        {\n            admin_ = std::make_shared<bool>(r[index].as<bool>());\n        }\n    }\n}\n\nUsers::Users(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 9)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            userId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[0]].asString());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            userName_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            password_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[2]].asString());\n        }\n    }\n    if (!pMasqueradingVector[3].empty() &&\n        pJson.isMember(pMasqueradingVector[3]))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[pMasqueradingVector[3]].isNull())\n        {\n            orgName_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[3]].asString());\n        }\n    }\n    if (!pMasqueradingVector[4].empty() &&\n        pJson.isMember(pMasqueradingVector[4]))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[pMasqueradingVector[4]].isNull())\n        {\n            signature_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[4]].asString());\n        }\n    }\n    if (!pMasqueradingVector[5].empty() &&\n        pJson.isMember(pMasqueradingVector[5]))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[pMasqueradingVector[5]].isNull())\n        {\n            avatarId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[5]].asString());\n        }\n    }\n    if (!pMasqueradingVector[6].empty() &&\n        pJson.isMember(pMasqueradingVector[6]))\n    {\n        dirtyFlag_[6] = true;\n        if (!pJson[pMasqueradingVector[6]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[6]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[7].empty() &&\n        pJson.isMember(pMasqueradingVector[7]))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[pMasqueradingVector[7]].isNull())\n        {\n            salt_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[7]].asString());\n        }\n    }\n    if (!pMasqueradingVector[8].empty() &&\n        pJson.isMember(pMasqueradingVector[8]))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[pMasqueradingVector[8]].isNull())\n        {\n            admin_ =\n                std::make_shared<bool>(pJson[pMasqueradingVector[8]].asBool());\n        }\n    }\n}\n\nUsers::Users(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"user_id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(pJson[\"user_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"user_name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"user_name\"].isNull())\n        {\n            userName_ =\n                std::make_shared<std::string>(pJson[\"user_name\"].asString());\n        }\n    }\n    if (pJson.isMember(\"password\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"password\"].isNull())\n        {\n            password_ =\n                std::make_shared<std::string>(pJson[\"password\"].asString());\n        }\n    }\n    if (pJson.isMember(\"org_name\"))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[\"org_name\"].isNull())\n        {\n            orgName_ =\n                std::make_shared<std::string>(pJson[\"org_name\"].asString());\n        }\n    }\n    if (pJson.isMember(\"signature\"))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[\"signature\"].isNull())\n        {\n            signature_ =\n                std::make_shared<std::string>(pJson[\"signature\"].asString());\n        }\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[\"avatar_id\"].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(pJson[\"avatar_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"id\"))\n    {\n        dirtyFlag_[6] = true;\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"salt\"))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[\"salt\"].isNull())\n        {\n            salt_ = std::make_shared<std::string>(pJson[\"salt\"].asString());\n        }\n    }\n    if (pJson.isMember(\"admin\"))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[\"admin\"].isNull())\n        {\n            admin_ = std::make_shared<bool>(pJson[\"admin\"].asBool());\n        }\n    }\n}\n\nvoid Users::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 9)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            userId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[0]].asString());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            userName_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            password_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[2]].asString());\n        }\n    }\n    if (!pMasqueradingVector[3].empty() &&\n        pJson.isMember(pMasqueradingVector[3]))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[pMasqueradingVector[3]].isNull())\n        {\n            orgName_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[3]].asString());\n        }\n    }\n    if (!pMasqueradingVector[4].empty() &&\n        pJson.isMember(pMasqueradingVector[4]))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[pMasqueradingVector[4]].isNull())\n        {\n            signature_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[4]].asString());\n        }\n    }\n    if (!pMasqueradingVector[5].empty() &&\n        pJson.isMember(pMasqueradingVector[5]))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[pMasqueradingVector[5]].isNull())\n        {\n            avatarId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[5]].asString());\n        }\n    }\n    if (!pMasqueradingVector[6].empty() &&\n        pJson.isMember(pMasqueradingVector[6]))\n    {\n        if (!pJson[pMasqueradingVector[6]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[6]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[7].empty() &&\n        pJson.isMember(pMasqueradingVector[7]))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[pMasqueradingVector[7]].isNull())\n        {\n            salt_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[7]].asString());\n        }\n    }\n    if (!pMasqueradingVector[8].empty() &&\n        pJson.isMember(pMasqueradingVector[8]))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[pMasqueradingVector[8]].isNull())\n        {\n            admin_ =\n                std::make_shared<bool>(pJson[pMasqueradingVector[8]].asBool());\n        }\n    }\n}\n\nvoid Users::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"user_id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(pJson[\"user_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"user_name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"user_name\"].isNull())\n        {\n            userName_ =\n                std::make_shared<std::string>(pJson[\"user_name\"].asString());\n        }\n    }\n    if (pJson.isMember(\"password\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"password\"].isNull())\n        {\n            password_ =\n                std::make_shared<std::string>(pJson[\"password\"].asString());\n        }\n    }\n    if (pJson.isMember(\"org_name\"))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[\"org_name\"].isNull())\n        {\n            orgName_ =\n                std::make_shared<std::string>(pJson[\"org_name\"].asString());\n        }\n    }\n    if (pJson.isMember(\"signature\"))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[\"signature\"].isNull())\n        {\n            signature_ =\n                std::make_shared<std::string>(pJson[\"signature\"].asString());\n        }\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[\"avatar_id\"].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(pJson[\"avatar_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"id\"))\n    {\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"salt\"))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[\"salt\"].isNull())\n        {\n            salt_ = std::make_shared<std::string>(pJson[\"salt\"].asString());\n        }\n    }\n    if (pJson.isMember(\"admin\"))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[\"admin\"].isNull())\n        {\n            admin_ = std::make_shared<bool>(pJson[\"admin\"].asBool());\n        }\n    }\n}\n\nconst std::string &Users::getValueOfUserId() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (userId_)\n        return *userId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getUserId() const noexcept\n{\n    return userId_;\n}\n\nvoid Users::setUserId(const std::string &pUserId) noexcept\n{\n    userId_ = std::make_shared<std::string>(pUserId);\n    dirtyFlag_[0] = true;\n}\n\nvoid Users::setUserId(std::string &&pUserId) noexcept\n{\n    userId_ = std::make_shared<std::string>(std::move(pUserId));\n    dirtyFlag_[0] = true;\n}\n\nvoid Users::setUserIdToNull() noexcept\n{\n    userId_.reset();\n    dirtyFlag_[0] = true;\n}\n\nconst std::string &Users::getValueOfUserName() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (userName_)\n        return *userName_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getUserName() const noexcept\n{\n    return userName_;\n}\n\nvoid Users::setUserName(const std::string &pUserName) noexcept\n{\n    userName_ = std::make_shared<std::string>(pUserName);\n    dirtyFlag_[1] = true;\n}\n\nvoid Users::setUserName(std::string &&pUserName) noexcept\n{\n    userName_ = std::make_shared<std::string>(std::move(pUserName));\n    dirtyFlag_[1] = true;\n}\n\nvoid Users::setUserNameToNull() noexcept\n{\n    userName_.reset();\n    dirtyFlag_[1] = true;\n}\n\nconst std::string &Users::getValueOfPassword() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (password_)\n        return *password_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getPassword() const noexcept\n{\n    return password_;\n}\n\nvoid Users::setPassword(const std::string &pPassword) noexcept\n{\n    password_ = std::make_shared<std::string>(pPassword);\n    dirtyFlag_[2] = true;\n}\n\nvoid Users::setPassword(std::string &&pPassword) noexcept\n{\n    password_ = std::make_shared<std::string>(std::move(pPassword));\n    dirtyFlag_[2] = true;\n}\n\nvoid Users::setPasswordToNull() noexcept\n{\n    password_.reset();\n    dirtyFlag_[2] = true;\n}\n\nconst std::string &Users::getValueOfOrgName() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (orgName_)\n        return *orgName_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getOrgName() const noexcept\n{\n    return orgName_;\n}\n\nvoid Users::setOrgName(const std::string &pOrgName) noexcept\n{\n    orgName_ = std::make_shared<std::string>(pOrgName);\n    dirtyFlag_[3] = true;\n}\n\nvoid Users::setOrgName(std::string &&pOrgName) noexcept\n{\n    orgName_ = std::make_shared<std::string>(std::move(pOrgName));\n    dirtyFlag_[3] = true;\n}\n\nvoid Users::setOrgNameToNull() noexcept\n{\n    orgName_.reset();\n    dirtyFlag_[3] = true;\n}\n\nconst std::string &Users::getValueOfSignature() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (signature_)\n        return *signature_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getSignature() const noexcept\n{\n    return signature_;\n}\n\nvoid Users::setSignature(const std::string &pSignature) noexcept\n{\n    signature_ = std::make_shared<std::string>(pSignature);\n    dirtyFlag_[4] = true;\n}\n\nvoid Users::setSignature(std::string &&pSignature) noexcept\n{\n    signature_ = std::make_shared<std::string>(std::move(pSignature));\n    dirtyFlag_[4] = true;\n}\n\nvoid Users::setSignatureToNull() noexcept\n{\n    signature_.reset();\n    dirtyFlag_[4] = true;\n}\n\nconst std::string &Users::getValueOfAvatarId() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (avatarId_)\n        return *avatarId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getAvatarId() const noexcept\n{\n    return avatarId_;\n}\n\nvoid Users::setAvatarId(const std::string &pAvatarId) noexcept\n{\n    avatarId_ = std::make_shared<std::string>(pAvatarId);\n    dirtyFlag_[5] = true;\n}\n\nvoid Users::setAvatarId(std::string &&pAvatarId) noexcept\n{\n    avatarId_ = std::make_shared<std::string>(std::move(pAvatarId));\n    dirtyFlag_[5] = true;\n}\n\nvoid Users::setAvatarIdToNull() noexcept\n{\n    avatarId_.reset();\n    dirtyFlag_[5] = true;\n}\n\nconst int32_t &Users::getValueOfId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (id_)\n        return *id_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &Users::getId() const noexcept\n{\n    return id_;\n}\n\nvoid Users::setId(const int32_t &pId) noexcept\n{\n    id_ = std::make_shared<int32_t>(pId);\n    dirtyFlag_[6] = true;\n}\n\nconst typename Users::PrimaryKeyType &Users::getPrimaryKey() const\n{\n    assert(id_);\n    return *id_;\n}\n\nconst std::string &Users::getValueOfSalt() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (salt_)\n        return *salt_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getSalt() const noexcept\n{\n    return salt_;\n}\n\nvoid Users::setSalt(const std::string &pSalt) noexcept\n{\n    salt_ = std::make_shared<std::string>(pSalt);\n    dirtyFlag_[7] = true;\n}\n\nvoid Users::setSalt(std::string &&pSalt) noexcept\n{\n    salt_ = std::make_shared<std::string>(std::move(pSalt));\n    dirtyFlag_[7] = true;\n}\n\nvoid Users::setSaltToNull() noexcept\n{\n    salt_.reset();\n    dirtyFlag_[7] = true;\n}\n\nconst bool &Users::getValueOfAdmin() const noexcept\n{\n    static const bool defaultValue = bool();\n    if (admin_)\n        return *admin_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<bool> &Users::getAdmin() const noexcept\n{\n    return admin_;\n}\n\nvoid Users::setAdmin(const bool &pAdmin) noexcept\n{\n    admin_ = std::make_shared<bool>(pAdmin);\n    dirtyFlag_[8] = true;\n}\n\nvoid Users::setAdminToNull() noexcept\n{\n    admin_.reset();\n    dirtyFlag_[8] = true;\n}\n\nvoid Users::updateId(const uint64_t id)\n{\n}\n\nconst std::vector<std::string> &Users::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"user_id\",\n                                                    \"user_name\",\n                                                    \"password\",\n                                                    \"org_name\",\n                                                    \"signature\",\n                                                    \"avatar_id\",\n                                                    \"salt\",\n                                                    \"admin\"};\n    return inCols;\n}\n\nvoid Users::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[0])\n    {\n        if (getUserId())\n        {\n            binder << getValueOfUserId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[1])\n    {\n        if (getUserName())\n        {\n            binder << getValueOfUserName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getPassword())\n        {\n            binder << getValueOfPassword();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[3])\n    {\n        if (getOrgName())\n        {\n            binder << getValueOfOrgName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[4])\n    {\n        if (getSignature())\n        {\n            binder << getValueOfSignature();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[5])\n    {\n        if (getAvatarId())\n        {\n            binder << getValueOfAvatarId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[7])\n    {\n        if (getSalt())\n        {\n            binder << getValueOfSalt();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[8])\n    {\n        if (getAdmin())\n        {\n            binder << getValueOfAdmin();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Users::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[0])\n    {\n        ret.push_back(getColumnName(0));\n    }\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    if (dirtyFlag_[2])\n    {\n        ret.push_back(getColumnName(2));\n    }\n    if (dirtyFlag_[3])\n    {\n        ret.push_back(getColumnName(3));\n    }\n    if (dirtyFlag_[4])\n    {\n        ret.push_back(getColumnName(4));\n    }\n    if (dirtyFlag_[5])\n    {\n        ret.push_back(getColumnName(5));\n    }\n    if (dirtyFlag_[7])\n    {\n        ret.push_back(getColumnName(7));\n    }\n    if (dirtyFlag_[8])\n    {\n        ret.push_back(getColumnName(8));\n    }\n    return ret;\n}\n\nvoid Users::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[0])\n    {\n        if (getUserId())\n        {\n            binder << getValueOfUserId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[1])\n    {\n        if (getUserName())\n        {\n            binder << getValueOfUserName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getPassword())\n        {\n            binder << getValueOfPassword();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[3])\n    {\n        if (getOrgName())\n        {\n            binder << getValueOfOrgName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[4])\n    {\n        if (getSignature())\n        {\n            binder << getValueOfSignature();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[5])\n    {\n        if (getAvatarId())\n        {\n            binder << getValueOfAvatarId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[7])\n    {\n        if (getSalt())\n        {\n            binder << getValueOfSalt();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[8])\n    {\n        if (getAdmin())\n        {\n            binder << getValueOfAdmin();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Users::toJson() const\n{\n    Json::Value ret;\n    if (getUserId())\n    {\n        ret[\"user_id\"] = getValueOfUserId();\n    }\n    else\n    {\n        ret[\"user_id\"] = Json::Value();\n    }\n    if (getUserName())\n    {\n        ret[\"user_name\"] = getValueOfUserName();\n    }\n    else\n    {\n        ret[\"user_name\"] = Json::Value();\n    }\n    if (getPassword())\n    {\n        ret[\"password\"] = getValueOfPassword();\n    }\n    else\n    {\n        ret[\"password\"] = Json::Value();\n    }\n    if (getOrgName())\n    {\n        ret[\"org_name\"] = getValueOfOrgName();\n    }\n    else\n    {\n        ret[\"org_name\"] = Json::Value();\n    }\n    if (getSignature())\n    {\n        ret[\"signature\"] = getValueOfSignature();\n    }\n    else\n    {\n        ret[\"signature\"] = Json::Value();\n    }\n    if (getAvatarId())\n    {\n        ret[\"avatar_id\"] = getValueOfAvatarId();\n    }\n    else\n    {\n        ret[\"avatar_id\"] = Json::Value();\n    }\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getSalt())\n    {\n        ret[\"salt\"] = getValueOfSalt();\n    }\n    else\n    {\n        ret[\"salt\"] = Json::Value();\n    }\n    if (getAdmin())\n    {\n        ret[\"admin\"] = getValueOfAdmin();\n    }\n    else\n    {\n        ret[\"admin\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Users::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 9)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getUserId())\n            {\n                ret[pMasqueradingVector[0]] = getValueOfUserId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getUserName())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfUserName();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (getPassword())\n            {\n                ret[pMasqueradingVector[2]] = getValueOfPassword();\n            }\n            else\n            {\n                ret[pMasqueradingVector[2]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[3].empty())\n        {\n            if (getOrgName())\n            {\n                ret[pMasqueradingVector[3]] = getValueOfOrgName();\n            }\n            else\n            {\n                ret[pMasqueradingVector[3]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[4].empty())\n        {\n            if (getSignature())\n            {\n                ret[pMasqueradingVector[4]] = getValueOfSignature();\n            }\n            else\n            {\n                ret[pMasqueradingVector[4]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[5].empty())\n        {\n            if (getAvatarId())\n            {\n                ret[pMasqueradingVector[5]] = getValueOfAvatarId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[5]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[6].empty())\n        {\n            if (getId())\n            {\n                ret[pMasqueradingVector[6]] = getValueOfId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[6]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[7].empty())\n        {\n            if (getSalt())\n            {\n                ret[pMasqueradingVector[7]] = getValueOfSalt();\n            }\n            else\n            {\n                ret[pMasqueradingVector[7]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[8].empty())\n        {\n            if (getAdmin())\n            {\n                ret[pMasqueradingVector[8]] = getValueOfAdmin();\n            }\n            else\n            {\n                ret[pMasqueradingVector[8]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getUserId())\n    {\n        ret[\"user_id\"] = getValueOfUserId();\n    }\n    else\n    {\n        ret[\"user_id\"] = Json::Value();\n    }\n    if (getUserName())\n    {\n        ret[\"user_name\"] = getValueOfUserName();\n    }\n    else\n    {\n        ret[\"user_name\"] = Json::Value();\n    }\n    if (getPassword())\n    {\n        ret[\"password\"] = getValueOfPassword();\n    }\n    else\n    {\n        ret[\"password\"] = Json::Value();\n    }\n    if (getOrgName())\n    {\n        ret[\"org_name\"] = getValueOfOrgName();\n    }\n    else\n    {\n        ret[\"org_name\"] = Json::Value();\n    }\n    if (getSignature())\n    {\n        ret[\"signature\"] = getValueOfSignature();\n    }\n    else\n    {\n        ret[\"signature\"] = Json::Value();\n    }\n    if (getAvatarId())\n    {\n        ret[\"avatar_id\"] = getValueOfAvatarId();\n    }\n    else\n    {\n        ret[\"avatar_id\"] = Json::Value();\n    }\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getSalt())\n    {\n        ret[\"salt\"] = getValueOfSalt();\n    }\n    else\n    {\n        ret[\"salt\"] = Json::Value();\n    }\n    if (getAdmin())\n    {\n        ret[\"admin\"] = getValueOfAdmin();\n    }\n    else\n    {\n        ret[\"admin\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Users::validateJsonForCreation(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"user_id\"))\n    {\n        if (!validJsonOfField(0, \"user_id\", pJson[\"user_id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"user_name\"))\n    {\n        if (!validJsonOfField(1, \"user_name\", pJson[\"user_name\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"password\"))\n    {\n        if (!validJsonOfField(2, \"password\", pJson[\"password\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"org_name\"))\n    {\n        if (!validJsonOfField(3, \"org_name\", pJson[\"org_name\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"signature\"))\n    {\n        if (!validJsonOfField(4, \"signature\", pJson[\"signature\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        if (!validJsonOfField(5, \"avatar_id\", pJson[\"avatar_id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(6, \"id\", pJson[\"id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"salt\"))\n    {\n        if (!validJsonOfField(7, \"salt\", pJson[\"salt\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"admin\"))\n    {\n        if (!validJsonOfField(8, \"admin\", pJson[\"admin\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Users::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 9)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[2]))\n            {\n                if (!validJsonOfField(2,\n                                      pMasqueradingVector[2],\n                                      pJson[pMasqueradingVector[2]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[3].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[3]))\n            {\n                if (!validJsonOfField(3,\n                                      pMasqueradingVector[3],\n                                      pJson[pMasqueradingVector[3]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[4].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[4]))\n            {\n                if (!validJsonOfField(4,\n                                      pMasqueradingVector[4],\n                                      pJson[pMasqueradingVector[4]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[5].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[5]))\n            {\n                if (!validJsonOfField(5,\n                                      pMasqueradingVector[5],\n                                      pJson[pMasqueradingVector[5]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[6].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[6]))\n            {\n                if (!validJsonOfField(6,\n                                      pMasqueradingVector[6],\n                                      pJson[pMasqueradingVector[6]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[7].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[7]))\n            {\n                if (!validJsonOfField(7,\n                                      pMasqueradingVector[7],\n                                      pJson[pMasqueradingVector[7]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[8].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[8]))\n            {\n                if (!validJsonOfField(8,\n                                      pMasqueradingVector[8],\n                                      pJson[pMasqueradingVector[8]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Users::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"user_id\"))\n    {\n        if (!validJsonOfField(0, \"user_id\", pJson[\"user_id\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"user_name\"))\n    {\n        if (!validJsonOfField(1, \"user_name\", pJson[\"user_name\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"password\"))\n    {\n        if (!validJsonOfField(2, \"password\", pJson[\"password\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"org_name\"))\n    {\n        if (!validJsonOfField(3, \"org_name\", pJson[\"org_name\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"signature\"))\n    {\n        if (!validJsonOfField(4, \"signature\", pJson[\"signature\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        if (!validJsonOfField(5, \"avatar_id\", pJson[\"avatar_id\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(6, \"id\", pJson[\"id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"salt\"))\n    {\n        if (!validJsonOfField(7, \"salt\", pJson[\"salt\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"admin\"))\n    {\n        if (!validJsonOfField(8, \"admin\", pJson[\"admin\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Users::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 9)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[2].empty() &&\n            pJson.isMember(pMasqueradingVector[2]))\n        {\n            if (!validJsonOfField(2,\n                                  pMasqueradingVector[2],\n                                  pJson[pMasqueradingVector[2]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[3].empty() &&\n            pJson.isMember(pMasqueradingVector[3]))\n        {\n            if (!validJsonOfField(3,\n                                  pMasqueradingVector[3],\n                                  pJson[pMasqueradingVector[3]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[4].empty() &&\n            pJson.isMember(pMasqueradingVector[4]))\n        {\n            if (!validJsonOfField(4,\n                                  pMasqueradingVector[4],\n                                  pJson[pMasqueradingVector[4]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[5].empty() &&\n            pJson.isMember(pMasqueradingVector[5]))\n        {\n            if (!validJsonOfField(5,\n                                  pMasqueradingVector[5],\n                                  pJson[pMasqueradingVector[5]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[6].empty() &&\n            pJson.isMember(pMasqueradingVector[6]))\n        {\n            if (!validJsonOfField(6,\n                                  pMasqueradingVector[6],\n                                  pJson[pMasqueradingVector[6]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[7].empty() &&\n            pJson.isMember(pMasqueradingVector[7]))\n        {\n            if (!validJsonOfField(7,\n                                  pMasqueradingVector[7],\n                                  pJson[pMasqueradingVector[7]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[8].empty() &&\n            pJson.isMember(pMasqueradingVector[8]))\n        {\n            if (!validJsonOfField(8,\n                                  pMasqueradingVector[8],\n                                  pJson[pMasqueradingVector[8]],\n                                  err,\n                                  false))\n                return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Users::validJsonOfField(size_t index,\n                             const std::string &fieldName,\n                             const Json::Value &pJson,\n                             std::string &err,\n                             bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 32)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 32)\";\n                return false;\n            }\n\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 64)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 64)\";\n                return false;\n            }\n\n            break;\n        case 2:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 64)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 64)\";\n                return false;\n            }\n\n            break;\n        case 3:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 20)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 20)\";\n                return false;\n            }\n\n            break;\n        case 4:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 50)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 50)\";\n                return false;\n            }\n\n            break;\n        case 5:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 32)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 32)\";\n                return false;\n            }\n\n            break;\n        case 6:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (isForCreation)\n            {\n                err = \"The automatic primary key cannot be set\";\n                return false;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 7:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 20)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 20)\";\n                return false;\n            }\n\n            break;\n        case 8:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isBool())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n\nWallets Users::getWallet(const DbClientPtr &clientPtr) const\n{\n    static const std::string sql = \"select * from wallets where user_id = $1\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *userId_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    if (r.size() == 0)\n    {\n        throw UnexpectedRows(\"0 rows found\");\n    }\n    else if (r.size() > 1)\n    {\n        throw UnexpectedRows(\"Found more than one row\");\n    }\n    return Wallets(r[0]);\n}\n\nvoid Users::getWallet(const DbClientPtr &clientPtr,\n                      const std::function<void(Wallets)> &rcb,\n                      const ExceptionCallback &ecb) const\n{\n    static const std::string sql = \"select * from wallets where user_id = $1\";\n    *clientPtr << sql << *userId_ >> [rcb = std::move(rcb),\n                                      ecb](const Result &r) {\n        if (r.size() == 0)\n        {\n            ecb(UnexpectedRows(\"0 rows found\"));\n        }\n        else if (r.size() > 1)\n        {\n            ecb(UnexpectedRows(\"Found more than one row\"));\n        }\n        else\n        {\n            rcb(Wallets(r[0]));\n        }\n    } >> ecb;\n}\n"
  },
  {
    "path": "orm_lib/tests/postgresql/Users.h",
    "content": "/**\n *\n *  Users.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace postgres\n{\nclass Wallets;\n\nclass Users\n{\n  public:\n    struct Cols\n    {\n        static const std::string _user_id;\n        static const std::string _user_name;\n        static const std::string _password;\n        static const std::string _org_name;\n        static const std::string _signature;\n        static const std::string _avatar_id;\n        static const std::string _id;\n        static const std::string _salt;\n        static const std::string _admin;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = int32_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Users(const drogon::orm::Row &r,\n                   const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Users(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Users(const Json::Value &pJson,\n          const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Users() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column user_id  */\n    /// Get the value of the column user_id, returns the default value if the\n    /// column is null\n    const std::string &getValueOfUserId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getUserId() const noexcept;\n    /// Set the value of the column user_id\n    void setUserId(const std::string &pUserId) noexcept;\n    void setUserId(std::string &&pUserId) noexcept;\n    void setUserIdToNull() noexcept;\n\n    /**  For column user_name  */\n    /// Get the value of the column user_name, returns the default value if the\n    /// column is null\n    const std::string &getValueOfUserName() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getUserName() const noexcept;\n    /// Set the value of the column user_name\n    void setUserName(const std::string &pUserName) noexcept;\n    void setUserName(std::string &&pUserName) noexcept;\n    void setUserNameToNull() noexcept;\n\n    /**  For column password  */\n    /// Get the value of the column password, returns the default value if the\n    /// column is null\n    const std::string &getValueOfPassword() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getPassword() const noexcept;\n    /// Set the value of the column password\n    void setPassword(const std::string &pPassword) noexcept;\n    void setPassword(std::string &&pPassword) noexcept;\n    void setPasswordToNull() noexcept;\n\n    /**  For column org_name  */\n    /// Get the value of the column org_name, returns the default value if the\n    /// column is null\n    const std::string &getValueOfOrgName() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getOrgName() const noexcept;\n    /// Set the value of the column org_name\n    void setOrgName(const std::string &pOrgName) noexcept;\n    void setOrgName(std::string &&pOrgName) noexcept;\n    void setOrgNameToNull() noexcept;\n\n    /**  For column signature  */\n    /// Get the value of the column signature, returns the default value if the\n    /// column is null\n    const std::string &getValueOfSignature() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getSignature() const noexcept;\n    /// Set the value of the column signature\n    void setSignature(const std::string &pSignature) noexcept;\n    void setSignature(std::string &&pSignature) noexcept;\n    void setSignatureToNull() noexcept;\n\n    /**  For column avatar_id  */\n    /// Get the value of the column avatar_id, returns the default value if the\n    /// column is null\n    const std::string &getValueOfAvatarId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getAvatarId() const noexcept;\n    /// Set the value of the column avatar_id\n    void setAvatarId(const std::string &pAvatarId) noexcept;\n    void setAvatarId(std::string &&pAvatarId) noexcept;\n    void setAvatarIdToNull() noexcept;\n\n    /**  For column id  */\n    /// Get the value of the column id, returns the default value if the column\n    /// is null\n    const int32_t &getValueOfId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getId() const noexcept;\n    /// Set the value of the column id\n    void setId(const int32_t &pId) noexcept;\n\n    /**  For column salt  */\n    /// Get the value of the column salt, returns the default value if the\n    /// column is null\n    const std::string &getValueOfSalt() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getSalt() const noexcept;\n    /// Set the value of the column salt\n    void setSalt(const std::string &pSalt) noexcept;\n    void setSalt(std::string &&pSalt) noexcept;\n    void setSaltToNull() noexcept;\n\n    /**  For column admin  */\n    /// Get the value of the column admin, returns the default value if the\n    /// column is null\n    const bool &getValueOfAdmin() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<bool> &getAdmin() const noexcept;\n    /// Set the value of the column admin\n    void setAdmin(const bool &pAdmin) noexcept;\n    void setAdminToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 9;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n    Wallets getWallet(const drogon::orm::DbClientPtr &clientPtr) const;\n    void getWallet(const drogon::orm::DbClientPtr &clientPtr,\n                   const std::function<void(Wallets)> &rcb,\n                   const drogon::orm::ExceptionCallback &ecb) const;\n\n  private:\n    friend drogon::orm::Mapper<Users>;\n    friend drogon::orm::BaseBuilder<Users, true, true>;\n    friend drogon::orm::BaseBuilder<Users, true, false>;\n    friend drogon::orm::BaseBuilder<Users, false, true>;\n    friend drogon::orm::BaseBuilder<Users, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<Users>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<std::string> userId_;\n    std::shared_ptr<std::string> userName_;\n    std::shared_ptr<std::string> password_;\n    std::shared_ptr<std::string> orgName_;\n    std::shared_ptr<std::string> signature_;\n    std::shared_ptr<std::string> avatarId_;\n    std::shared_ptr<int32_t> id_;\n    std::shared_ptr<std::string> salt_;\n    std::shared_ptr<bool> admin_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[9] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where id = $1\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where id = $1\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        if (dirtyFlag_[0])\n        {\n            sql += \"user_id,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[1])\n        {\n            sql += \"user_name,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[2])\n        {\n            sql += \"password,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[3])\n        {\n            sql += \"org_name,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[4])\n        {\n            sql += \"signature,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[5])\n        {\n            sql += \"avatar_id,\";\n            ++parametersCount;\n        }\n        sql += \"id,\";\n        ++parametersCount;\n        if (dirtyFlag_[7])\n        {\n            sql += \"salt,\";\n            ++parametersCount;\n        }\n        sql += \"admin,\";\n        ++parametersCount;\n        if (!dirtyFlag_[8])\n        {\n            needSelection = true;\n        }\n        needSelection = true;\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        int placeholder = 1;\n        char placeholderStr[64];\n        size_t n = 0;\n        if (dirtyFlag_[0])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        if (dirtyFlag_[1])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        if (dirtyFlag_[2])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        if (dirtyFlag_[3])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        if (dirtyFlag_[4])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        if (dirtyFlag_[5])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        sql += \"default,\";\n        if (dirtyFlag_[7])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        if (dirtyFlag_[8])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        else\n        {\n            sql += \"default,\";\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        if (needSelection)\n        {\n            sql.append(\") returning *\");\n        }\n        else\n        {\n            sql.append(1, ')');\n        }\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace postgres\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/postgresql/Wallets.cc",
    "content": "/**\n *\n *  Wallets.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Wallets.h\"\n#include \"Users.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::postgres;\n\nconst std::string Wallets::Cols::_id = \"id\";\nconst std::string Wallets::Cols::_user_id = \"user_id\";\nconst std::string Wallets::Cols::_amount = \"amount\";\nconst std::string Wallets::primaryKeyName = \"id\";\nconst bool Wallets::hasPrimaryKey = true;\nconst std::string Wallets::tableName = \"wallets\";\n\nconst std::vector<typename Wallets::MetaData> Wallets::metaData_ = {\n    {\"id\", \"int32_t\", \"integer\", 4, 1, 1, 1},\n    {\"user_id\", \"std::string\", \"character varying\", 32, 0, 0, 0},\n    {\"amount\", \"std::string\", \"numeric\", 0, 0, 0, 0}};\n\nconst std::string &Wallets::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nWallets::Wallets(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[\"id\"].as<int32_t>());\n        }\n        if (!r[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(r[\"user_id\"].as<std::string>());\n        }\n        if (!r[\"amount\"].isNull())\n        {\n            amount_ =\n                std::make_shared<std::string>(r[\"amount\"].as<std::string>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 3 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            id_ = std::make_shared<int32_t>(r[index].as<int32_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            userId_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 2;\n        if (!r[index].isNull())\n        {\n            amount_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n    }\n}\n\nWallets::Wallets(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            userId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            amount_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[2]].asString());\n        }\n    }\n}\n\nWallets::Wallets(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(pJson[\"user_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"amount\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"amount\"].isNull())\n        {\n            amount_ = std::make_shared<std::string>(pJson[\"amount\"].asString());\n        }\n    }\n}\n\nvoid Wallets::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int32_t>(\n                (int32_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            userId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            amount_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[2]].asString());\n        }\n    }\n}\n\nvoid Wallets::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int32_t>((int32_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(pJson[\"user_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"amount\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"amount\"].isNull())\n        {\n            amount_ = std::make_shared<std::string>(pJson[\"amount\"].asString());\n        }\n    }\n}\n\nconst int32_t &Wallets::getValueOfId() const noexcept\n{\n    static const int32_t defaultValue = int32_t();\n    if (id_)\n        return *id_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int32_t> &Wallets::getId() const noexcept\n{\n    return id_;\n}\n\nvoid Wallets::setId(const int32_t &pId) noexcept\n{\n    id_ = std::make_shared<int32_t>(pId);\n    dirtyFlag_[0] = true;\n}\n\nconst typename Wallets::PrimaryKeyType &Wallets::getPrimaryKey() const\n{\n    assert(id_);\n    return *id_;\n}\n\nconst std::string &Wallets::getValueOfUserId() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (userId_)\n        return *userId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Wallets::getUserId() const noexcept\n{\n    return userId_;\n}\n\nvoid Wallets::setUserId(const std::string &pUserId) noexcept\n{\n    userId_ = std::make_shared<std::string>(pUserId);\n    dirtyFlag_[1] = true;\n}\n\nvoid Wallets::setUserId(std::string &&pUserId) noexcept\n{\n    userId_ = std::make_shared<std::string>(std::move(pUserId));\n    dirtyFlag_[1] = true;\n}\n\nvoid Wallets::setUserIdToNull() noexcept\n{\n    userId_.reset();\n    dirtyFlag_[1] = true;\n}\n\nconst std::string &Wallets::getValueOfAmount() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (amount_)\n        return *amount_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Wallets::getAmount() const noexcept\n{\n    return amount_;\n}\n\nvoid Wallets::setAmount(const std::string &pAmount) noexcept\n{\n    amount_ = std::make_shared<std::string>(pAmount);\n    dirtyFlag_[2] = true;\n}\n\nvoid Wallets::setAmount(std::string &&pAmount) noexcept\n{\n    amount_ = std::make_shared<std::string>(std::move(pAmount));\n    dirtyFlag_[2] = true;\n}\n\nvoid Wallets::setAmountToNull() noexcept\n{\n    amount_.reset();\n    dirtyFlag_[2] = true;\n}\n\nvoid Wallets::updateId(const uint64_t id)\n{\n}\n\nconst std::vector<std::string> &Wallets::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"user_id\", \"amount\"};\n    return inCols;\n}\n\nvoid Wallets::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getUserId())\n        {\n            binder << getValueOfUserId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getAmount())\n        {\n            binder << getValueOfAmount();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Wallets::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    if (dirtyFlag_[2])\n    {\n        ret.push_back(getColumnName(2));\n    }\n    return ret;\n}\n\nvoid Wallets::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getUserId())\n        {\n            binder << getValueOfUserId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getAmount())\n        {\n            binder << getValueOfAmount();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Wallets::toJson() const\n{\n    Json::Value ret;\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getUserId())\n    {\n        ret[\"user_id\"] = getValueOfUserId();\n    }\n    else\n    {\n        ret[\"user_id\"] = Json::Value();\n    }\n    if (getAmount())\n    {\n        ret[\"amount\"] = getValueOfAmount();\n    }\n    else\n    {\n        ret[\"amount\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Wallets::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 3)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getId())\n            {\n                ret[pMasqueradingVector[0]] = getValueOfId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getUserId())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfUserId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (getAmount())\n            {\n                ret[pMasqueradingVector[2]] = getValueOfAmount();\n            }\n            else\n            {\n                ret[pMasqueradingVector[2]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getId())\n    {\n        ret[\"id\"] = getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getUserId())\n    {\n        ret[\"user_id\"] = getValueOfUserId();\n    }\n    else\n    {\n        ret[\"user_id\"] = Json::Value();\n    }\n    if (getAmount())\n    {\n        ret[\"amount\"] = getValueOfAmount();\n    }\n    else\n    {\n        ret[\"amount\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Wallets::validateJsonForCreation(const Json::Value &pJson,\n                                      std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        if (!validJsonOfField(1, \"user_id\", pJson[\"user_id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"amount\"))\n    {\n        if (!validJsonOfField(2, \"amount\", pJson[\"amount\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Wallets::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[2]))\n            {\n                if (!validJsonOfField(2,\n                                      pMasqueradingVector[2],\n                                      pJson[pMasqueradingVector[2]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Wallets::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        if (!validJsonOfField(1, \"user_id\", pJson[\"user_id\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"amount\"))\n    {\n        if (!validJsonOfField(2, \"amount\", pJson[\"amount\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Wallets::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[2].empty() &&\n            pJson.isMember(pMasqueradingVector[2]))\n        {\n            if (!validJsonOfField(2,\n                                  pMasqueradingVector[2],\n                                  pJson[pMasqueradingVector[2]],\n                                  err,\n                                  false))\n                return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Wallets::validJsonOfField(size_t index,\n                               const std::string &fieldName,\n                               const Json::Value &pJson,\n                               std::string &err,\n                               bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (isForCreation)\n            {\n                err = \"The automatic primary key cannot be set\";\n                return false;\n            }\n            if (!pJson.isInt())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            if (pJson.isString() && std::strlen(pJson.asCString()) > 32)\n            {\n                err = \"String length exceeds limit for the \" + fieldName +\n                      \" field (the maximum value is 32)\";\n                return false;\n            }\n\n            break;\n        case 2:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n\nUsers Wallets::getUser(const DbClientPtr &clientPtr) const\n{\n    static const std::string sql = \"select * from users where user_id = $1\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *userId_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    if (r.size() == 0)\n    {\n        throw UnexpectedRows(\"0 rows found\");\n    }\n    else if (r.size() > 1)\n    {\n        throw UnexpectedRows(\"Found more than one row\");\n    }\n    return Users(r[0]);\n}\n\nvoid Wallets::getUser(const DbClientPtr &clientPtr,\n                      const std::function<void(Users)> &rcb,\n                      const ExceptionCallback &ecb) const\n{\n    static const std::string sql = \"select * from users where user_id = $1\";\n    *clientPtr << sql << *userId_ >> [rcb = std::move(rcb),\n                                      ecb](const Result &r) {\n        if (r.size() == 0)\n        {\n            ecb(UnexpectedRows(\"0 rows found\"));\n        }\n        else if (r.size() > 1)\n        {\n            ecb(UnexpectedRows(\"Found more than one row\"));\n        }\n        else\n        {\n            rcb(Users(r[0]));\n        }\n    } >> ecb;\n}\n"
  },
  {
    "path": "orm_lib/tests/postgresql/Wallets.h",
    "content": "/**\n *\n *  Wallets.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace postgres\n{\nclass Users;\n\nclass Wallets\n{\n  public:\n    struct Cols\n    {\n        static const std::string _id;\n        static const std::string _user_id;\n        static const std::string _amount;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = int32_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Wallets(const drogon::orm::Row &r,\n                     const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Wallets(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Wallets(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Wallets() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column id  */\n    /// Get the value of the column id, returns the default value if the column\n    /// is null\n    const int32_t &getValueOfId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int32_t> &getId() const noexcept;\n    /// Set the value of the column id\n    void setId(const int32_t &pId) noexcept;\n\n    /**  For column user_id  */\n    /// Get the value of the column user_id, returns the default value if the\n    /// column is null\n    const std::string &getValueOfUserId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getUserId() const noexcept;\n    /// Set the value of the column user_id\n    void setUserId(const std::string &pUserId) noexcept;\n    void setUserId(std::string &&pUserId) noexcept;\n    void setUserIdToNull() noexcept;\n\n    /**  For column amount  */\n    /// Get the value of the column amount, returns the default value if the\n    /// column is null\n    const std::string &getValueOfAmount() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getAmount() const noexcept;\n    /// Set the value of the column amount\n    void setAmount(const std::string &pAmount) noexcept;\n    void setAmount(std::string &&pAmount) noexcept;\n    void setAmountToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 3;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n    Users getUser(const drogon::orm::DbClientPtr &clientPtr) const;\n    void getUser(const drogon::orm::DbClientPtr &clientPtr,\n                 const std::function<void(Users)> &rcb,\n                 const drogon::orm::ExceptionCallback &ecb) const;\n\n  private:\n    friend drogon::orm::Mapper<Wallets>;\n    friend drogon::orm::BaseBuilder<Wallets, true, true>;\n    friend drogon::orm::BaseBuilder<Wallets, true, false>;\n    friend drogon::orm::BaseBuilder<Wallets, false, true>;\n    friend drogon::orm::BaseBuilder<Wallets, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<Wallets>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int32_t> id_;\n    std::shared_ptr<std::string> userId_;\n    std::shared_ptr<std::string> amount_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[3] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where id = $1\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where id = $1\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        sql += \"id,\";\n        ++parametersCount;\n        sql += \"user_id,\";\n        ++parametersCount;\n        if (!dirtyFlag_[1])\n        {\n            needSelection = true;\n        }\n        sql += \"amount,\";\n        ++parametersCount;\n        if (!dirtyFlag_[2])\n        {\n            needSelection = true;\n        }\n        needSelection = true;\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        int placeholder = 1;\n        char placeholderStr[64];\n        size_t n = 0;\n        sql += \"default,\";\n        if (dirtyFlag_[1])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        else\n        {\n            sql += \"default,\";\n        }\n        if (dirtyFlag_[2])\n        {\n            n = snprintf(placeholderStr,\n                         sizeof(placeholderStr),\n                         \"$%d,\",\n                         placeholder++);\n            sql.append(placeholderStr, n);\n        }\n        else\n        {\n            sql += \"default,\";\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        if (needSelection)\n        {\n            sql.append(\") returning *\");\n        }\n        else\n        {\n            sql.append(1, ')');\n        }\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace postgres\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/postgresql/model.json",
    "content": "{\n    //rdbms:server type, postgresql \n    \"rdbms\":\"postgresql\",\n    //host:server address,localhost by default;\n    \"host\":\"127.0.0.1\",\n    //port:server port, 5432 by default;\n    \"port\":5432,\n    //dbname:Database name;\n    \"dbname\":\"postgres\",\n    \"user\":\"postgres\",\n    \"passwd\":\"\",\n    //\"tables\":[\"group_users\"]\n    \"tables\": [\n        \"users\",\n        \"wallets\",\n        \"blog\",\n        \"category\",\n        \"blog_tag\",\n        \"tag\"\n    ],\n    \"relationships\": {\n        \"enabled\": true,\n        \"items\": [\n            {\n                \"type\": \"has one\",\n                \"original_table_name\": \"users\",\n                \"original_table_alias\": \"user\",\n                \"original_key\": \"user_id\",\n                \"target_table_name\": \"wallets\",\n                \"target_table_alias\": \"wallet\",\n                \"target_key\": \"user_id\",\n                \"enable_reverse\": true\n            },\n            {\n                \"type\": \"has many\",\n                \"original_table_name\": \"category\",\n                \"original_table_alias\": \"category\",\n                \"original_key\": \"id\",\n                \"target_table_name\": \"blog\",\n                \"target_table_alias\": \"blogs\",\n                \"target_key\": \"category_id\",\n                \"enable_reverse\": true\n            },\n            {\n                \"type\": \"many to many\",\n                \"original_table_name\": \"blog\",\n                \"original_table_alias\": \"blogs\",\n                \"original_key\": \"id\",\n                \"pivot_table\": {\n                    \"table_name\": \"blog_tag\",\n                    \"original_key\": \"blog_id\",\n                    \"target_key\": \"tag_id\"\n                },\n                \"target_table_name\": \"tag\",\n                \"target_table_alias\": \"tags\",\n                \"target_key\": \"id\",\n                \"enable_reverse\": true\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "orm_lib/tests/sqlite3/Blog.cc",
    "content": "/**\n *\n *  Blog.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Blog.h\"\n#include \"BlogTag.h\"\n#include \"Category.h\"\n#include \"Tag.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::sqlite3;\n\nconst std::string Blog::Cols::_id = \"id\";\nconst std::string Blog::Cols::_title = \"title\";\nconst std::string Blog::Cols::_category_id = \"category_id\";\nconst std::string Blog::primaryKeyName = \"id\";\nconst bool Blog::hasPrimaryKey = true;\nconst std::string Blog::tableName = \"blog\";\n\nconst std::vector<typename Blog::MetaData> Blog::metaData_ = {\n    {\"id\", \"int64_t\", \"integer auto_increment\", 8, 0, 1, 0},\n    {\"title\", \"std::string\", \"varchar(30)\", 0, 0, 0, 0},\n    {\"category_id\", \"int64_t\", \"integer\", 8, 0, 0, 0}};\n\nconst std::string &Blog::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nBlog::Blog(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int64_t>(r[\"id\"].as<int64_t>());\n        }\n        if (!r[\"title\"].isNull())\n        {\n            title_ =\n                std::make_shared<std::string>(r[\"title\"].as<std::string>());\n        }\n        if (!r[\"category_id\"].isNull())\n        {\n            categoryId_ =\n                std::make_shared<int64_t>(r[\"category_id\"].as<int64_t>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 3 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            id_ = std::make_shared<int64_t>(r[index].as<int64_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            title_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 2;\n        if (!r[index].isNull())\n        {\n            categoryId_ = std::make_shared<int64_t>(r[index].as<int64_t>());\n        }\n    }\n}\n\nBlog::Blog(const Json::Value &pJson,\n           const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            title_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            categoryId_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[2]].asInt64());\n        }\n    }\n}\n\nBlog::Blog(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int64_t>((int64_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"title\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"title\"].isNull())\n        {\n            title_ = std::make_shared<std::string>(pJson[\"title\"].asString());\n        }\n    }\n    if (pJson.isMember(\"category_id\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"category_id\"].isNull())\n        {\n            categoryId_ = std::make_shared<int64_t>(\n                (int64_t)pJson[\"category_id\"].asInt64());\n        }\n    }\n}\n\nvoid Blog::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            title_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            categoryId_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[2]].asInt64());\n        }\n    }\n}\n\nvoid Blog::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int64_t>((int64_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"title\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"title\"].isNull())\n        {\n            title_ = std::make_shared<std::string>(pJson[\"title\"].asString());\n        }\n    }\n    if (pJson.isMember(\"category_id\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"category_id\"].isNull())\n        {\n            categoryId_ = std::make_shared<int64_t>(\n                (int64_t)pJson[\"category_id\"].asInt64());\n        }\n    }\n}\n\nconst int64_t &Blog::getValueOfId() const noexcept\n{\n    static const int64_t defaultValue = int64_t();\n    if (id_)\n        return *id_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int64_t> &Blog::getId() const noexcept\n{\n    return id_;\n}\n\nvoid Blog::setId(const int64_t &pId) noexcept\n{\n    id_ = std::make_shared<int64_t>(pId);\n    dirtyFlag_[0] = true;\n}\n\nvoid Blog::setIdToNull() noexcept\n{\n    id_.reset();\n    dirtyFlag_[0] = true;\n}\n\nconst typename Blog::PrimaryKeyType &Blog::getPrimaryKey() const\n{\n    assert(id_);\n    return *id_;\n}\n\nconst std::string &Blog::getValueOfTitle() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (title_)\n        return *title_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Blog::getTitle() const noexcept\n{\n    return title_;\n}\n\nvoid Blog::setTitle(const std::string &pTitle) noexcept\n{\n    title_ = std::make_shared<std::string>(pTitle);\n    dirtyFlag_[1] = true;\n}\n\nvoid Blog::setTitle(std::string &&pTitle) noexcept\n{\n    title_ = std::make_shared<std::string>(std::move(pTitle));\n    dirtyFlag_[1] = true;\n}\n\nvoid Blog::setTitleToNull() noexcept\n{\n    title_.reset();\n    dirtyFlag_[1] = true;\n}\n\nconst int64_t &Blog::getValueOfCategoryId() const noexcept\n{\n    static const int64_t defaultValue = int64_t();\n    if (categoryId_)\n        return *categoryId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int64_t> &Blog::getCategoryId() const noexcept\n{\n    return categoryId_;\n}\n\nvoid Blog::setCategoryId(const int64_t &pCategoryId) noexcept\n{\n    categoryId_ = std::make_shared<int64_t>(pCategoryId);\n    dirtyFlag_[2] = true;\n}\n\nvoid Blog::setCategoryIdToNull() noexcept\n{\n    categoryId_.reset();\n    dirtyFlag_[2] = true;\n}\n\nvoid Blog::updateId(const uint64_t id)\n{\n}\n\nconst std::vector<std::string> &Blog::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"id\",\n                                                    \"title\",\n                                                    \"category_id\"};\n    return inCols;\n}\n\nvoid Blog::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[0])\n    {\n        if (getId())\n        {\n            binder << getValueOfId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[1])\n    {\n        if (getTitle())\n        {\n            binder << getValueOfTitle();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getCategoryId())\n        {\n            binder << getValueOfCategoryId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Blog::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[0])\n    {\n        ret.push_back(getColumnName(0));\n    }\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    if (dirtyFlag_[2])\n    {\n        ret.push_back(getColumnName(2));\n    }\n    return ret;\n}\n\nvoid Blog::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[0])\n    {\n        if (getId())\n        {\n            binder << getValueOfId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[1])\n    {\n        if (getTitle())\n        {\n            binder << getValueOfTitle();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getCategoryId())\n        {\n            binder << getValueOfCategoryId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Blog::toJson() const\n{\n    Json::Value ret;\n    if (getId())\n    {\n        ret[\"id\"] = (Json::Int64)getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getTitle())\n    {\n        ret[\"title\"] = getValueOfTitle();\n    }\n    else\n    {\n        ret[\"title\"] = Json::Value();\n    }\n    if (getCategoryId())\n    {\n        ret[\"category_id\"] = (Json::Int64)getValueOfCategoryId();\n    }\n    else\n    {\n        ret[\"category_id\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Blog::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 3)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getId())\n            {\n                ret[pMasqueradingVector[0]] = (Json::Int64)getValueOfId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getTitle())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfTitle();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (getCategoryId())\n            {\n                ret[pMasqueradingVector[2]] =\n                    (Json::Int64)getValueOfCategoryId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[2]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getId())\n    {\n        ret[\"id\"] = (Json::Int64)getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getTitle())\n    {\n        ret[\"title\"] = getValueOfTitle();\n    }\n    else\n    {\n        ret[\"title\"] = Json::Value();\n    }\n    if (getCategoryId())\n    {\n        ret[\"category_id\"] = (Json::Int64)getValueOfCategoryId();\n    }\n    else\n    {\n        ret[\"category_id\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Blog::validateJsonForCreation(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"title\"))\n    {\n        if (!validJsonOfField(1, \"title\", pJson[\"title\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"category_id\"))\n    {\n        if (!validJsonOfField(\n                2, \"category_id\", pJson[\"category_id\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Blog::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[2]))\n            {\n                if (!validJsonOfField(2,\n                                      pMasqueradingVector[2],\n                                      pJson[pMasqueradingVector[2]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Blog::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"title\"))\n    {\n        if (!validJsonOfField(1, \"title\", pJson[\"title\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"category_id\"))\n    {\n        if (!validJsonOfField(\n                2, \"category_id\", pJson[\"category_id\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Blog::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[2].empty() &&\n            pJson.isMember(pMasqueradingVector[2]))\n        {\n            if (!validJsonOfField(2,\n                                  pMasqueradingVector[2],\n                                  pJson[pMasqueradingVector[2]],\n                                  err,\n                                  false))\n                return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Blog::validJsonOfField(size_t index,\n                            const std::string &fieldName,\n                            const Json::Value &pJson,\n                            std::string &err,\n                            bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isInt64())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 2:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isInt64())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n\nCategory Blog::getCategory(const DbClientPtr &clientPtr) const\n{\n    static const std::string sql = \"select * from category where id = ?\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *categoryId_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    if (r.size() == 0)\n    {\n        throw UnexpectedRows(\"0 rows found\");\n    }\n    else if (r.size() > 1)\n    {\n        throw UnexpectedRows(\"Found more than one row\");\n    }\n    return Category(r[0]);\n}\n\nvoid Blog::getCategory(const DbClientPtr &clientPtr,\n                       const std::function<void(Category)> &rcb,\n                       const ExceptionCallback &ecb) const\n{\n    static const std::string sql = \"select * from category where id = ?\";\n    *clientPtr << sql << *categoryId_ >> [rcb = std::move(rcb),\n                                          ecb](const Result &r) {\n        if (r.size() == 0)\n        {\n            ecb(UnexpectedRows(\"0 rows found\"));\n        }\n        else if (r.size() > 1)\n        {\n            ecb(UnexpectedRows(\"Found more than one row\"));\n        }\n        else\n        {\n            rcb(Category(r[0]));\n        }\n    } >> ecb;\n}\n\nstd::vector<std::pair<Tag, BlogTag>> Blog::getTags(\n    const DbClientPtr &clientPtr) const\n{\n    static const std::string sql =\n        \"select * from tag,blog_tag where blog_tag.blog_id = ? and \"\n        \"blog_tag.tag_id = tag.id\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *id_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    std::vector<std::pair<Tag, BlogTag>> ret;\n    ret.reserve(r.size());\n    for (auto const &row : r)\n    {\n        ret.emplace_back(\n            std::pair<Tag, BlogTag>(Tag(row),\n                                    BlogTag(row, Tag::getColumnNumber())));\n    }\n    return ret;\n}\n\nvoid Blog::getTags(\n    const DbClientPtr &clientPtr,\n    const std::function<void(std::vector<std::pair<Tag, BlogTag>>)> &rcb,\n    const ExceptionCallback &ecb) const\n{\n    static const std::string sql =\n        \"select * from tag,blog_tag where blog_tag.blog_id = ? and \"\n        \"blog_tag.tag_id = tag.id\";\n    *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) {\n        std::vector<std::pair<Tag, BlogTag>> ret;\n        ret.reserve(r.size());\n        for (auto const &row : r)\n        {\n            ret.emplace_back(\n                std::pair<Tag, BlogTag>(Tag(row),\n                                        BlogTag(row, Tag::getColumnNumber())));\n        }\n        rcb(ret);\n    } >> ecb;\n}\n"
  },
  {
    "path": "orm_lib/tests/sqlite3/Blog.h",
    "content": "/**\n *\n *  Blog.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace sqlite3\n{\nclass BlogTag;\nclass Category;\nclass Tag;\n\nclass Blog\n{\n  public:\n    struct Cols\n    {\n        static const std::string _id;\n        static const std::string _title;\n        static const std::string _category_id;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = int64_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Blog(const drogon::orm::Row &r,\n                  const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Blog(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Blog(const Json::Value &pJson,\n         const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Blog() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column id  */\n    /// Get the value of the column id, returns the default value if the column\n    /// is null\n    const int64_t &getValueOfId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int64_t> &getId() const noexcept;\n    /// Set the value of the column id\n    void setId(const int64_t &pId) noexcept;\n    void setIdToNull() noexcept;\n\n    /**  For column title  */\n    /// Get the value of the column title, returns the default value if the\n    /// column is null\n    const std::string &getValueOfTitle() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getTitle() const noexcept;\n    /// Set the value of the column title\n    void setTitle(const std::string &pTitle) noexcept;\n    void setTitle(std::string &&pTitle) noexcept;\n    void setTitleToNull() noexcept;\n\n    /**  For column category_id  */\n    /// Get the value of the column category_id, returns the default value if\n    /// the column is null\n    const int64_t &getValueOfCategoryId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int64_t> &getCategoryId() const noexcept;\n    /// Set the value of the column category_id\n    void setCategoryId(const int64_t &pCategoryId) noexcept;\n    void setCategoryIdToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 3;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n    Category getCategory(const drogon::orm::DbClientPtr &clientPtr) const;\n    void getCategory(const drogon::orm::DbClientPtr &clientPtr,\n                     const std::function<void(Category)> &rcb,\n                     const drogon::orm::ExceptionCallback &ecb) const;\n    std::vector<std::pair<Tag, BlogTag>> getTags(\n        const drogon::orm::DbClientPtr &clientPtr) const;\n    void getTags(\n        const drogon::orm::DbClientPtr &clientPtr,\n        const std::function<void(std::vector<std::pair<Tag, BlogTag>>)> &rcb,\n        const drogon::orm::ExceptionCallback &ecb) const;\n\n  private:\n    friend drogon::orm::Mapper<Blog>;\n    friend drogon::orm::BaseBuilder<Blog, true, true>;\n    friend drogon::orm::BaseBuilder<Blog, true, false>;\n    friend drogon::orm::BaseBuilder<Blog, false, true>;\n    friend drogon::orm::BaseBuilder<Blog, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<Blog>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int64_t> id_;\n    std::shared_ptr<std::string> title_;\n    std::shared_ptr<int64_t> categoryId_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[3] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        if (dirtyFlag_[0])\n        {\n            sql += \"id,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[1])\n        {\n            sql += \"title,\";\n            ++parametersCount;\n        }\n        if (!dirtyFlag_[1])\n        {\n            needSelection = true;\n        }\n        if (dirtyFlag_[2])\n        {\n            sql += \"category_id,\";\n            ++parametersCount;\n        }\n        if (!dirtyFlag_[2])\n        {\n            needSelection = true;\n        }\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        if (dirtyFlag_[0])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[1])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[2])\n        {\n            sql.append(\"?,\");\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        sql.append(1, ')');\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace sqlite3\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/sqlite3/BlogTag.cc",
    "content": "/**\n *\n *  BlogTag.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"BlogTag.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::sqlite3;\n\nconst std::string BlogTag::Cols::_blog_id = \"blog_id\";\nconst std::string BlogTag::Cols::_tag_id = \"tag_id\";\nconst std::vector<std::string> BlogTag::primaryKeyName = {\"blog_id\", \"tag_id\"};\nconst bool BlogTag::hasPrimaryKey = true;\nconst std::string BlogTag::tableName = \"blog_tag\";\n\nconst std::vector<typename BlogTag::MetaData> BlogTag::metaData_ = {\n    {\"blog_id\", \"int64_t\", \"integer\", 8, 0, 1, 1},\n    {\"tag_id\", \"int64_t\", \"integer\", 8, 0, 1, 1}};\n\nconst std::string &BlogTag::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nBlogTag::BlogTag(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"blog_id\"].isNull())\n        {\n            blogId_ = std::make_shared<int64_t>(r[\"blog_id\"].as<int64_t>());\n        }\n        if (!r[\"tag_id\"].isNull())\n        {\n            tagId_ = std::make_shared<int64_t>(r[\"tag_id\"].as<int64_t>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 2 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            blogId_ = std::make_shared<int64_t>(r[index].as<int64_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            tagId_ = std::make_shared<int64_t>(r[index].as<int64_t>());\n        }\n    }\n}\n\nBlogTag::BlogTag(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            blogId_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            tagId_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[1]].asInt64());\n        }\n    }\n}\n\nBlogTag::BlogTag(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"blog_id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"blog_id\"].isNull())\n        {\n            blogId_ =\n                std::make_shared<int64_t>((int64_t)pJson[\"blog_id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"tag_id\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"tag_id\"].isNull())\n        {\n            tagId_ =\n                std::make_shared<int64_t>((int64_t)pJson[\"tag_id\"].asInt64());\n        }\n    }\n}\n\nvoid BlogTag::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            blogId_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            tagId_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[1]].asInt64());\n        }\n    }\n}\n\nvoid BlogTag::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"blog_id\"))\n    {\n        if (!pJson[\"blog_id\"].isNull())\n        {\n            blogId_ =\n                std::make_shared<int64_t>((int64_t)pJson[\"blog_id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"tag_id\"))\n    {\n        if (!pJson[\"tag_id\"].isNull())\n        {\n            tagId_ =\n                std::make_shared<int64_t>((int64_t)pJson[\"tag_id\"].asInt64());\n        }\n    }\n}\n\nconst int64_t &BlogTag::getValueOfBlogId() const noexcept\n{\n    static const int64_t defaultValue = int64_t();\n    if (blogId_)\n        return *blogId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int64_t> &BlogTag::getBlogId() const noexcept\n{\n    return blogId_;\n}\n\nvoid BlogTag::setBlogId(const int64_t &pBlogId) noexcept\n{\n    blogId_ = std::make_shared<int64_t>(pBlogId);\n    dirtyFlag_[0] = true;\n}\n\nconst int64_t &BlogTag::getValueOfTagId() const noexcept\n{\n    static const int64_t defaultValue = int64_t();\n    if (tagId_)\n        return *tagId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int64_t> &BlogTag::getTagId() const noexcept\n{\n    return tagId_;\n}\n\nvoid BlogTag::setTagId(const int64_t &pTagId) noexcept\n{\n    tagId_ = std::make_shared<int64_t>(pTagId);\n    dirtyFlag_[1] = true;\n}\n\nvoid BlogTag::updateId(const uint64_t id)\n{\n}\n\ntypename BlogTag::PrimaryKeyType BlogTag::getPrimaryKey() const\n{\n    return std::make_tuple(*blogId_, *tagId_);\n}\n\nconst std::vector<std::string> &BlogTag::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"blog_id\", \"tag_id\"};\n    return inCols;\n}\n\nvoid BlogTag::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[0])\n    {\n        if (getBlogId())\n        {\n            binder << getValueOfBlogId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[1])\n    {\n        if (getTagId())\n        {\n            binder << getValueOfTagId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> BlogTag::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[0])\n    {\n        ret.push_back(getColumnName(0));\n    }\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    return ret;\n}\n\nvoid BlogTag::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[0])\n    {\n        if (getBlogId())\n        {\n            binder << getValueOfBlogId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[1])\n    {\n        if (getTagId())\n        {\n            binder << getValueOfTagId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value BlogTag::toJson() const\n{\n    Json::Value ret;\n    if (getBlogId())\n    {\n        ret[\"blog_id\"] = (Json::Int64)getValueOfBlogId();\n    }\n    else\n    {\n        ret[\"blog_id\"] = Json::Value();\n    }\n    if (getTagId())\n    {\n        ret[\"tag_id\"] = (Json::Int64)getValueOfTagId();\n    }\n    else\n    {\n        ret[\"tag_id\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value BlogTag::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 2)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getBlogId())\n            {\n                ret[pMasqueradingVector[0]] = (Json::Int64)getValueOfBlogId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getTagId())\n            {\n                ret[pMasqueradingVector[1]] = (Json::Int64)getValueOfTagId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getBlogId())\n    {\n        ret[\"blog_id\"] = (Json::Int64)getValueOfBlogId();\n    }\n    else\n    {\n        ret[\"blog_id\"] = Json::Value();\n    }\n    if (getTagId())\n    {\n        ret[\"tag_id\"] = (Json::Int64)getValueOfTagId();\n    }\n    else\n    {\n        ret[\"tag_id\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool BlogTag::validateJsonForCreation(const Json::Value &pJson,\n                                      std::string &err)\n{\n    if (pJson.isMember(\"blog_id\"))\n    {\n        if (!validJsonOfField(0, \"blog_id\", pJson[\"blog_id\"], err, true))\n            return false;\n    }\n    else\n    {\n        err = \"The blog_id column cannot be null\";\n        return false;\n    }\n    if (pJson.isMember(\"tag_id\"))\n    {\n        if (!validJsonOfField(1, \"tag_id\", pJson[\"tag_id\"], err, true))\n            return false;\n    }\n    else\n    {\n        err = \"The tag_id column cannot be null\";\n        return false;\n    }\n    return true;\n}\n\nbool BlogTag::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n            else\n            {\n                err =\n                    \"The \" + pMasqueradingVector[0] + \" column cannot be null\";\n                return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n            else\n            {\n                err =\n                    \"The \" + pMasqueradingVector[1] + \" column cannot be null\";\n                return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool BlogTag::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"blog_id\"))\n    {\n        if (!validJsonOfField(0, \"blog_id\", pJson[\"blog_id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"tag_id\"))\n    {\n        if (!validJsonOfField(1, \"tag_id\", pJson[\"tag_id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    return true;\n}\n\nbool BlogTag::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool BlogTag::validJsonOfField(size_t index,\n                               const std::string &fieldName,\n                               const Json::Value &pJson,\n                               std::string &err,\n                               bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (!pJson.isInt64())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                err = \"The \" + fieldName + \" column cannot be null\";\n                return false;\n            }\n            if (!pJson.isInt64())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n"
  },
  {
    "path": "orm_lib/tests/sqlite3/BlogTag.h",
    "content": "/**\n *\n *  BlogTag.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace sqlite3\n{\n\nclass BlogTag\n{\n  public:\n    struct Cols\n    {\n        static const std::string _blog_id;\n        static const std::string _tag_id;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::vector<std::string> primaryKeyName;\n    using PrimaryKeyType = std::tuple<int64_t, int64_t>;  // blog_id,tag_id\n    PrimaryKeyType getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit BlogTag(const drogon::orm::Row &r,\n                     const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit BlogTag(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    BlogTag(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    BlogTag() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column blog_id  */\n    /// Get the value of the column blog_id, returns the default value if the\n    /// column is null\n    const int64_t &getValueOfBlogId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int64_t> &getBlogId() const noexcept;\n    /// Set the value of the column blog_id\n    void setBlogId(const int64_t &pBlogId) noexcept;\n\n    /**  For column tag_id  */\n    /// Get the value of the column tag_id, returns the default value if the\n    /// column is null\n    const int64_t &getValueOfTagId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int64_t> &getTagId() const noexcept;\n    /// Set the value of the column tag_id\n    void setTagId(const int64_t &pTagId) noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 2;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n  private:\n    friend drogon::orm::Mapper<BlogTag>;\n    friend drogon::orm::BaseBuilder<BlogTag, true, true>;\n    friend drogon::orm::BaseBuilder<BlogTag, true, false>;\n    friend drogon::orm::BaseBuilder<BlogTag, false, true>;\n    friend drogon::orm::BaseBuilder<BlogTag, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<BlogTag>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int64_t> blogId_;\n    std::shared_ptr<int64_t> tagId_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[2] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where blog_id = ? and tag_id = ?\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where blog_id = ? and tag_id = ?\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        if (dirtyFlag_[0])\n        {\n            sql += \"blog_id,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[1])\n        {\n            sql += \"tag_id,\";\n            ++parametersCount;\n        }\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        if (dirtyFlag_[0])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[1])\n        {\n            sql.append(\"?,\");\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        sql.append(1, ')');\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace sqlite3\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/sqlite3/Category.cc",
    "content": "/**\n *\n *  Category.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Category.h\"\n#include \"Blog.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::sqlite3;\n\nconst std::string Category::Cols::_id = \"id\";\nconst std::string Category::Cols::_name = \"name\";\nconst std::string Category::primaryKeyName = \"id\";\nconst bool Category::hasPrimaryKey = true;\nconst std::string Category::tableName = \"category\";\n\nconst std::vector<typename Category::MetaData> Category::metaData_ = {\n    {\"id\", \"int64_t\", \"integer auto_increment\", 8, 0, 1, 0},\n    {\"name\", \"std::string\", \"varchar(30)\", 0, 0, 0, 0}};\n\nconst std::string &Category::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nCategory::Category(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int64_t>(r[\"id\"].as<int64_t>());\n        }\n        if (!r[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(r[\"name\"].as<std::string>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 2 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            id_ = std::make_shared<int64_t>(r[index].as<int64_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            name_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n    }\n}\n\nCategory::Category(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            name_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n}\n\nCategory::Category(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int64_t>((int64_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(pJson[\"name\"].asString());\n        }\n    }\n}\n\nvoid Category::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            name_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n}\n\nvoid Category::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int64_t>((int64_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(pJson[\"name\"].asString());\n        }\n    }\n}\n\nconst int64_t &Category::getValueOfId() const noexcept\n{\n    static const int64_t defaultValue = int64_t();\n    if (id_)\n        return *id_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int64_t> &Category::getId() const noexcept\n{\n    return id_;\n}\n\nvoid Category::setId(const int64_t &pId) noexcept\n{\n    id_ = std::make_shared<int64_t>(pId);\n    dirtyFlag_[0] = true;\n}\n\nvoid Category::setIdToNull() noexcept\n{\n    id_.reset();\n    dirtyFlag_[0] = true;\n}\n\nconst typename Category::PrimaryKeyType &Category::getPrimaryKey() const\n{\n    assert(id_);\n    return *id_;\n}\n\nconst std::string &Category::getValueOfName() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (name_)\n        return *name_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Category::getName() const noexcept\n{\n    return name_;\n}\n\nvoid Category::setName(const std::string &pName) noexcept\n{\n    name_ = std::make_shared<std::string>(pName);\n    dirtyFlag_[1] = true;\n}\n\nvoid Category::setName(std::string &&pName) noexcept\n{\n    name_ = std::make_shared<std::string>(std::move(pName));\n    dirtyFlag_[1] = true;\n}\n\nvoid Category::setNameToNull() noexcept\n{\n    name_.reset();\n    dirtyFlag_[1] = true;\n}\n\nvoid Category::updateId(const uint64_t id)\n{\n}\n\nconst std::vector<std::string> &Category::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"id\", \"name\"};\n    return inCols;\n}\n\nvoid Category::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[0])\n    {\n        if (getId())\n        {\n            binder << getValueOfId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[1])\n    {\n        if (getName())\n        {\n            binder << getValueOfName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Category::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[0])\n    {\n        ret.push_back(getColumnName(0));\n    }\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    return ret;\n}\n\nvoid Category::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[0])\n    {\n        if (getId())\n        {\n            binder << getValueOfId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[1])\n    {\n        if (getName())\n        {\n            binder << getValueOfName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Category::toJson() const\n{\n    Json::Value ret;\n    if (getId())\n    {\n        ret[\"id\"] = (Json::Int64)getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getName())\n    {\n        ret[\"name\"] = getValueOfName();\n    }\n    else\n    {\n        ret[\"name\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Category::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 2)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getId())\n            {\n                ret[pMasqueradingVector[0]] = (Json::Int64)getValueOfId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getName())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfName();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getId())\n    {\n        ret[\"id\"] = (Json::Int64)getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getName())\n    {\n        ret[\"name\"] = getValueOfName();\n    }\n    else\n    {\n        ret[\"name\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Category::validateJsonForCreation(const Json::Value &pJson,\n                                       std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        if (!validJsonOfField(1, \"name\", pJson[\"name\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Category::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Category::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        if (!validJsonOfField(1, \"name\", pJson[\"name\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Category::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Category::validJsonOfField(size_t index,\n                                const std::string &fieldName,\n                                const Json::Value &pJson,\n                                std::string &err,\n                                bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isInt64())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n\nstd::vector<Blog> Category::getBlogs(const DbClientPtr &clientPtr) const\n{\n    static const std::string sql = \"select * from blog where category_id = ?\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *id_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    std::vector<Blog> ret;\n    ret.reserve(r.size());\n    for (auto const &row : r)\n    {\n        ret.emplace_back(Blog(row));\n    }\n    return ret;\n}\n\nvoid Category::getBlogs(const DbClientPtr &clientPtr,\n                        const std::function<void(std::vector<Blog>)> &rcb,\n                        const ExceptionCallback &ecb) const\n{\n    static const std::string sql = \"select * from blog where category_id = ?\";\n    *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) {\n        std::vector<Blog> ret;\n        ret.reserve(r.size());\n        for (auto const &row : r)\n        {\n            ret.emplace_back(Blog(row));\n        }\n        rcb(ret);\n    } >> ecb;\n}\n"
  },
  {
    "path": "orm_lib/tests/sqlite3/Category.h",
    "content": "/**\n *\n *  Category.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace sqlite3\n{\nclass Blog;\n\nclass Category\n{\n  public:\n    struct Cols\n    {\n        static const std::string _id;\n        static const std::string _name;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = int64_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Category(const drogon::orm::Row &r,\n                      const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Category(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Category(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Category() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column id  */\n    /// Get the value of the column id, returns the default value if the column\n    /// is null\n    const int64_t &getValueOfId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int64_t> &getId() const noexcept;\n    /// Set the value of the column id\n    void setId(const int64_t &pId) noexcept;\n    void setIdToNull() noexcept;\n\n    /**  For column name  */\n    /// Get the value of the column name, returns the default value if the\n    /// column is null\n    const std::string &getValueOfName() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getName() const noexcept;\n    /// Set the value of the column name\n    void setName(const std::string &pName) noexcept;\n    void setName(std::string &&pName) noexcept;\n    void setNameToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 2;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n    std::vector<Blog> getBlogs(const drogon::orm::DbClientPtr &clientPtr) const;\n    void getBlogs(const drogon::orm::DbClientPtr &clientPtr,\n                  const std::function<void(std::vector<Blog>)> &rcb,\n                  const drogon::orm::ExceptionCallback &ecb) const;\n\n  private:\n    friend drogon::orm::Mapper<Category>;\n    friend drogon::orm::BaseBuilder<Category, true, true>;\n    friend drogon::orm::BaseBuilder<Category, true, false>;\n    friend drogon::orm::BaseBuilder<Category, false, true>;\n    friend drogon::orm::BaseBuilder<Category, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<Category>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int64_t> id_;\n    std::shared_ptr<std::string> name_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[2] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        if (dirtyFlag_[0])\n        {\n            sql += \"id,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[1])\n        {\n            sql += \"name,\";\n            ++parametersCount;\n        }\n        if (!dirtyFlag_[1])\n        {\n            needSelection = true;\n        }\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        if (dirtyFlag_[0])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[1])\n        {\n            sql.append(\"?,\");\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        sql.append(1, ')');\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace sqlite3\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/sqlite3/Tag.cc",
    "content": "/**\n *\n *  Tag.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Tag.h\"\n#include \"Blog.h\"\n#include \"BlogTag.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::sqlite3;\n\nconst std::string Tag::Cols::_id = \"id\";\nconst std::string Tag::Cols::_name = \"name\";\nconst std::string Tag::primaryKeyName = \"id\";\nconst bool Tag::hasPrimaryKey = true;\nconst std::string Tag::tableName = \"tag\";\n\nconst std::vector<typename Tag::MetaData> Tag::metaData_ = {\n    {\"id\", \"int64_t\", \"integer auto_increment\", 8, 0, 1, 0},\n    {\"name\", \"std::string\", \"varchar(30)\", 0, 0, 0, 0}};\n\nconst std::string &Tag::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nTag::Tag(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int64_t>(r[\"id\"].as<int64_t>());\n        }\n        if (!r[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(r[\"name\"].as<std::string>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 2 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            id_ = std::make_shared<int64_t>(r[index].as<int64_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            name_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n    }\n}\n\nTag::Tag(const Json::Value &pJson,\n         const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            name_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n}\n\nTag::Tag(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int64_t>((int64_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(pJson[\"name\"].asString());\n        }\n    }\n}\n\nvoid Tag::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            name_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n}\n\nvoid Tag::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int64_t>((int64_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"name\"].isNull())\n        {\n            name_ = std::make_shared<std::string>(pJson[\"name\"].asString());\n        }\n    }\n}\n\nconst int64_t &Tag::getValueOfId() const noexcept\n{\n    static const int64_t defaultValue = int64_t();\n    if (id_)\n        return *id_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int64_t> &Tag::getId() const noexcept\n{\n    return id_;\n}\n\nvoid Tag::setId(const int64_t &pId) noexcept\n{\n    id_ = std::make_shared<int64_t>(pId);\n    dirtyFlag_[0] = true;\n}\n\nvoid Tag::setIdToNull() noexcept\n{\n    id_.reset();\n    dirtyFlag_[0] = true;\n}\n\nconst typename Tag::PrimaryKeyType &Tag::getPrimaryKey() const\n{\n    assert(id_);\n    return *id_;\n}\n\nconst std::string &Tag::getValueOfName() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (name_)\n        return *name_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Tag::getName() const noexcept\n{\n    return name_;\n}\n\nvoid Tag::setName(const std::string &pName) noexcept\n{\n    name_ = std::make_shared<std::string>(pName);\n    dirtyFlag_[1] = true;\n}\n\nvoid Tag::setName(std::string &&pName) noexcept\n{\n    name_ = std::make_shared<std::string>(std::move(pName));\n    dirtyFlag_[1] = true;\n}\n\nvoid Tag::setNameToNull() noexcept\n{\n    name_.reset();\n    dirtyFlag_[1] = true;\n}\n\nvoid Tag::updateId(const uint64_t id)\n{\n}\n\nconst std::vector<std::string> &Tag::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"id\", \"name\"};\n    return inCols;\n}\n\nvoid Tag::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[0])\n    {\n        if (getId())\n        {\n            binder << getValueOfId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[1])\n    {\n        if (getName())\n        {\n            binder << getValueOfName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Tag::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[0])\n    {\n        ret.push_back(getColumnName(0));\n    }\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    return ret;\n}\n\nvoid Tag::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[0])\n    {\n        if (getId())\n        {\n            binder << getValueOfId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[1])\n    {\n        if (getName())\n        {\n            binder << getValueOfName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Tag::toJson() const\n{\n    Json::Value ret;\n    if (getId())\n    {\n        ret[\"id\"] = (Json::Int64)getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getName())\n    {\n        ret[\"name\"] = getValueOfName();\n    }\n    else\n    {\n        ret[\"name\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Tag::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 2)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getId())\n            {\n                ret[pMasqueradingVector[0]] = (Json::Int64)getValueOfId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getName())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfName();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getId())\n    {\n        ret[\"id\"] = (Json::Int64)getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getName())\n    {\n        ret[\"name\"] = getValueOfName();\n    }\n    else\n    {\n        ret[\"name\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Tag::validateJsonForCreation(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        if (!validJsonOfField(1, \"name\", pJson[\"name\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Tag::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Tag::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"name\"))\n    {\n        if (!validJsonOfField(1, \"name\", pJson[\"name\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Tag::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 2)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Tag::validJsonOfField(size_t index,\n                           const std::string &fieldName,\n                           const Json::Value &pJson,\n                           std::string &err,\n                           bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isInt64())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n\nstd::vector<std::pair<Blog, BlogTag>> Tag::getBlogs(\n    const DbClientPtr &clientPtr) const\n{\n    static const std::string sql =\n        \"select * from blog,blog_tag where blog_tag.tag_id = ? and \"\n        \"blog_tag.blog_id = blog.id\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *id_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    std::vector<std::pair<Blog, BlogTag>> ret;\n    ret.reserve(r.size());\n    for (auto const &row : r)\n    {\n        ret.emplace_back(\n            std::pair<Blog, BlogTag>(Blog(row),\n                                     BlogTag(row, Blog::getColumnNumber())));\n    }\n    return ret;\n}\n\nvoid Tag::getBlogs(\n    const DbClientPtr &clientPtr,\n    const std::function<void(std::vector<std::pair<Blog, BlogTag>>)> &rcb,\n    const ExceptionCallback &ecb) const\n{\n    static const std::string sql =\n        \"select * from blog,blog_tag where blog_tag.tag_id = ? and \"\n        \"blog_tag.blog_id = blog.id\";\n    *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) {\n        std::vector<std::pair<Blog, BlogTag>> ret;\n        ret.reserve(r.size());\n        for (auto const &row : r)\n        {\n            ret.emplace_back(std::pair<Blog, BlogTag>(\n                Blog(row), BlogTag(row, Blog::getColumnNumber())));\n        }\n        rcb(ret);\n    } >> ecb;\n}\n"
  },
  {
    "path": "orm_lib/tests/sqlite3/Tag.h",
    "content": "/**\n *\n *  Tag.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace sqlite3\n{\nclass Blog;\nclass BlogTag;\n\nclass Tag\n{\n  public:\n    struct Cols\n    {\n        static const std::string _id;\n        static const std::string _name;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = int64_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Tag(const drogon::orm::Row &r,\n                 const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Tag(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Tag(const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Tag() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column id  */\n    /// Get the value of the column id, returns the default value if the column\n    /// is null\n    const int64_t &getValueOfId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int64_t> &getId() const noexcept;\n    /// Set the value of the column id\n    void setId(const int64_t &pId) noexcept;\n    void setIdToNull() noexcept;\n\n    /**  For column name  */\n    /// Get the value of the column name, returns the default value if the\n    /// column is null\n    const std::string &getValueOfName() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getName() const noexcept;\n    /// Set the value of the column name\n    void setName(const std::string &pName) noexcept;\n    void setName(std::string &&pName) noexcept;\n    void setNameToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 2;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n    std::vector<std::pair<Blog, BlogTag>> getBlogs(\n        const drogon::orm::DbClientPtr &clientPtr) const;\n    void getBlogs(\n        const drogon::orm::DbClientPtr &clientPtr,\n        const std::function<void(std::vector<std::pair<Blog, BlogTag>>)> &rcb,\n        const drogon::orm::ExceptionCallback &ecb) const;\n\n  private:\n    friend drogon::orm::Mapper<Tag>;\n    friend drogon::orm::BaseBuilder<Tag, true, true>;\n    friend drogon::orm::BaseBuilder<Tag, true, false>;\n    friend drogon::orm::BaseBuilder<Tag, false, true>;\n    friend drogon::orm::BaseBuilder<Tag, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<Tag>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int64_t> id_;\n    std::shared_ptr<std::string> name_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[2] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        if (dirtyFlag_[0])\n        {\n            sql += \"id,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[1])\n        {\n            sql += \"name,\";\n            ++parametersCount;\n        }\n        if (!dirtyFlag_[1])\n        {\n            needSelection = true;\n        }\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        if (dirtyFlag_[0])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[1])\n        {\n            sql.append(\"?,\");\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        sql.append(1, ')');\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace sqlite3\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/sqlite3/Users.cc",
    "content": "/**\n *\n *  Users.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Users.h\"\n#include \"Wallets.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::sqlite3;\n\nconst std::string Users::Cols::_id = \"id\";\nconst std::string Users::Cols::_user_id = \"user_id\";\nconst std::string Users::Cols::_user_name = \"user_name\";\nconst std::string Users::Cols::_password = \"password\";\nconst std::string Users::Cols::_org_name = \"org_name\";\nconst std::string Users::Cols::_signature = \"signature\";\nconst std::string Users::Cols::_avatar_id = \"avatar_id\";\nconst std::string Users::Cols::_salt = \"salt\";\nconst std::string Users::Cols::_admin = \"admin\";\nconst std::string Users::Cols::_create_time = \"create_time\";\nconst std::string Users::primaryKeyName = \"id\";\nconst bool Users::hasPrimaryKey = true;\nconst std::string Users::tableName = \"users\";\n\nconst std::vector<typename Users::MetaData> Users::metaData_ = {\n    {\"id\", \"int64_t\", \"integer\", 8, 1, 1, 0},\n    {\"user_id\", \"std::string\", \"varchar(32)\", 0, 0, 0, 0},\n    {\"user_name\", \"std::string\", \"varchar(64)\", 0, 0, 0, 0},\n    {\"password\", \"std::string\", \"varchar(64)\", 0, 0, 0, 0},\n    {\"org_name\", \"std::string\", \"varchar(20)\", 0, 0, 0, 0},\n    {\"signature\", \"std::string\", \"varchar(50)\", 0, 0, 0, 0},\n    {\"avatar_id\", \"std::string\", \"varchar(32)\", 0, 0, 0, 0},\n    {\"salt\", \"std::string\", \"character varchar(20)\", 0, 0, 0, 0},\n    {\"admin\", \"std::string\", \"boolean\", 0, 0, 0, 0},\n    {\"create_time\", \"::trantor::Date\", \"datetime\", 0, 0, 0, 0}};\n\nconst std::string &Users::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nUsers::Users(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int64_t>(r[\"id\"].as<int64_t>());\n        }\n        if (!r[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(r[\"user_id\"].as<std::string>());\n        }\n        if (!r[\"user_name\"].isNull())\n        {\n            userName_ =\n                std::make_shared<std::string>(r[\"user_name\"].as<std::string>());\n        }\n        if (!r[\"password\"].isNull())\n        {\n            password_ =\n                std::make_shared<std::string>(r[\"password\"].as<std::string>());\n        }\n        if (!r[\"org_name\"].isNull())\n        {\n            orgName_ =\n                std::make_shared<std::string>(r[\"org_name\"].as<std::string>());\n        }\n        if (!r[\"signature\"].isNull())\n        {\n            signature_ =\n                std::make_shared<std::string>(r[\"signature\"].as<std::string>());\n        }\n        if (!r[\"avatar_id\"].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(r[\"avatar_id\"].as<std::string>());\n        }\n        if (!r[\"salt\"].isNull())\n        {\n            salt_ = std::make_shared<std::string>(r[\"salt\"].as<std::string>());\n        }\n        if (!r[\"admin\"].isNull())\n        {\n            admin_ =\n                std::make_shared<std::string>(r[\"admin\"].as<std::string>());\n        }\n        if (!r[\"create_time\"].isNull())\n        {\n            auto timeStr = r[\"create_time\"].as<std::string>();\n            struct tm stm;\n            memset(&stm, 0, sizeof(stm));\n            auto p = strptime(timeStr.c_str(), \"%Y-%m-%d %H:%M:%S\", &stm);\n            time_t t = mktime(&stm);\n            size_t decimalNum = 0;\n            if (p)\n            {\n                if (*p == '.')\n                {\n                    std::string decimals(p + 1, &timeStr[timeStr.length()]);\n                    while (decimals.length() < 6)\n                    {\n                        decimals += \"0\";\n                    }\n                    decimalNum = (size_t)atol(decimals.c_str());\n                }\n                createTime_ =\n                    std::make_shared<::trantor::Date>(t * 1000000 + decimalNum);\n            }\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 10 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            id_ = std::make_shared<int64_t>(r[index].as<int64_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            userId_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 2;\n        if (!r[index].isNull())\n        {\n            userName_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 3;\n        if (!r[index].isNull())\n        {\n            password_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 4;\n        if (!r[index].isNull())\n        {\n            orgName_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 5;\n        if (!r[index].isNull())\n        {\n            signature_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 6;\n        if (!r[index].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 7;\n        if (!r[index].isNull())\n        {\n            salt_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 8;\n        if (!r[index].isNull())\n        {\n            admin_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 9;\n        if (!r[index].isNull())\n        {\n            auto timeStr = r[index].as<std::string>();\n            struct tm stm;\n            memset(&stm, 0, sizeof(stm));\n            auto p = strptime(timeStr.c_str(), \"%Y-%m-%d %H:%M:%S\", &stm);\n            time_t t = mktime(&stm);\n            size_t decimalNum = 0;\n            if (p)\n            {\n                if (*p == '.')\n                {\n                    std::string decimals(p + 1, &timeStr[timeStr.length()]);\n                    while (decimals.length() < 6)\n                    {\n                        decimals += \"0\";\n                    }\n                    decimalNum = (size_t)atol(decimals.c_str());\n                }\n                createTime_ =\n                    std::make_shared<::trantor::Date>(t * 1000000 + decimalNum);\n            }\n        }\n    }\n}\n\nUsers::Users(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 10)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            userId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            userName_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[2]].asString());\n        }\n    }\n    if (!pMasqueradingVector[3].empty() &&\n        pJson.isMember(pMasqueradingVector[3]))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[pMasqueradingVector[3]].isNull())\n        {\n            password_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[3]].asString());\n        }\n    }\n    if (!pMasqueradingVector[4].empty() &&\n        pJson.isMember(pMasqueradingVector[4]))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[pMasqueradingVector[4]].isNull())\n        {\n            orgName_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[4]].asString());\n        }\n    }\n    if (!pMasqueradingVector[5].empty() &&\n        pJson.isMember(pMasqueradingVector[5]))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[pMasqueradingVector[5]].isNull())\n        {\n            signature_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[5]].asString());\n        }\n    }\n    if (!pMasqueradingVector[6].empty() &&\n        pJson.isMember(pMasqueradingVector[6]))\n    {\n        dirtyFlag_[6] = true;\n        if (!pJson[pMasqueradingVector[6]].isNull())\n        {\n            avatarId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[6]].asString());\n        }\n    }\n    if (!pMasqueradingVector[7].empty() &&\n        pJson.isMember(pMasqueradingVector[7]))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[pMasqueradingVector[7]].isNull())\n        {\n            salt_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[7]].asString());\n        }\n    }\n    if (!pMasqueradingVector[8].empty() &&\n        pJson.isMember(pMasqueradingVector[8]))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[pMasqueradingVector[8]].isNull())\n        {\n            admin_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[8]].asString());\n        }\n    }\n    if (!pMasqueradingVector[9].empty() &&\n        pJson.isMember(pMasqueradingVector[9]))\n    {\n        dirtyFlag_[9] = true;\n        if (!pJson[pMasqueradingVector[9]].isNull())\n        {\n            auto timeStr = pJson[pMasqueradingVector[9]].asString();\n            struct tm stm;\n            memset(&stm, 0, sizeof(stm));\n            auto p = strptime(timeStr.c_str(), \"%Y-%m-%d %H:%M:%S\", &stm);\n            time_t t = mktime(&stm);\n            size_t decimalNum = 0;\n            if (p)\n            {\n                if (*p == '.')\n                {\n                    std::string decimals(p + 1, &timeStr[timeStr.length()]);\n                    while (decimals.length() < 6)\n                    {\n                        decimals += \"0\";\n                    }\n                    decimalNum = (size_t)atol(decimals.c_str());\n                }\n                createTime_ =\n                    std::make_shared<::trantor::Date>(t * 1000000 + decimalNum);\n            }\n        }\n    }\n}\n\nUsers::Users(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int64_t>((int64_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(pJson[\"user_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"user_name\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"user_name\"].isNull())\n        {\n            userName_ =\n                std::make_shared<std::string>(pJson[\"user_name\"].asString());\n        }\n    }\n    if (pJson.isMember(\"password\"))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[\"password\"].isNull())\n        {\n            password_ =\n                std::make_shared<std::string>(pJson[\"password\"].asString());\n        }\n    }\n    if (pJson.isMember(\"org_name\"))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[\"org_name\"].isNull())\n        {\n            orgName_ =\n                std::make_shared<std::string>(pJson[\"org_name\"].asString());\n        }\n    }\n    if (pJson.isMember(\"signature\"))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[\"signature\"].isNull())\n        {\n            signature_ =\n                std::make_shared<std::string>(pJson[\"signature\"].asString());\n        }\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        dirtyFlag_[6] = true;\n        if (!pJson[\"avatar_id\"].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(pJson[\"avatar_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"salt\"))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[\"salt\"].isNull())\n        {\n            salt_ = std::make_shared<std::string>(pJson[\"salt\"].asString());\n        }\n    }\n    if (pJson.isMember(\"admin\"))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[\"admin\"].isNull())\n        {\n            admin_ = std::make_shared<std::string>(pJson[\"admin\"].asString());\n        }\n    }\n    if (pJson.isMember(\"create_time\"))\n    {\n        dirtyFlag_[9] = true;\n        if (!pJson[\"create_time\"].isNull())\n        {\n            auto timeStr = pJson[\"create_time\"].asString();\n            struct tm stm;\n            memset(&stm, 0, sizeof(stm));\n            auto p = strptime(timeStr.c_str(), \"%Y-%m-%d %H:%M:%S\", &stm);\n            time_t t = mktime(&stm);\n            size_t decimalNum = 0;\n            if (p)\n            {\n                if (*p == '.')\n                {\n                    std::string decimals(p + 1, &timeStr[timeStr.length()]);\n                    while (decimals.length() < 6)\n                    {\n                        decimals += \"0\";\n                    }\n                    decimalNum = (size_t)atol(decimals.c_str());\n                }\n                createTime_ =\n                    std::make_shared<::trantor::Date>(t * 1000000 + decimalNum);\n            }\n        }\n    }\n}\n\nvoid Users::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 10)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            userId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            userName_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[2]].asString());\n        }\n    }\n    if (!pMasqueradingVector[3].empty() &&\n        pJson.isMember(pMasqueradingVector[3]))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[pMasqueradingVector[3]].isNull())\n        {\n            password_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[3]].asString());\n        }\n    }\n    if (!pMasqueradingVector[4].empty() &&\n        pJson.isMember(pMasqueradingVector[4]))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[pMasqueradingVector[4]].isNull())\n        {\n            orgName_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[4]].asString());\n        }\n    }\n    if (!pMasqueradingVector[5].empty() &&\n        pJson.isMember(pMasqueradingVector[5]))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[pMasqueradingVector[5]].isNull())\n        {\n            signature_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[5]].asString());\n        }\n    }\n    if (!pMasqueradingVector[6].empty() &&\n        pJson.isMember(pMasqueradingVector[6]))\n    {\n        dirtyFlag_[6] = true;\n        if (!pJson[pMasqueradingVector[6]].isNull())\n        {\n            avatarId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[6]].asString());\n        }\n    }\n    if (!pMasqueradingVector[7].empty() &&\n        pJson.isMember(pMasqueradingVector[7]))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[pMasqueradingVector[7]].isNull())\n        {\n            salt_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[7]].asString());\n        }\n    }\n    if (!pMasqueradingVector[8].empty() &&\n        pJson.isMember(pMasqueradingVector[8]))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[pMasqueradingVector[8]].isNull())\n        {\n            admin_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[8]].asString());\n        }\n    }\n    if (!pMasqueradingVector[9].empty() &&\n        pJson.isMember(pMasqueradingVector[9]))\n    {\n        dirtyFlag_[9] = true;\n        if (!pJson[pMasqueradingVector[9]].isNull())\n        {\n            auto timeStr = pJson[pMasqueradingVector[9]].asString();\n            struct tm stm;\n            memset(&stm, 0, sizeof(stm));\n            auto p = strptime(timeStr.c_str(), \"%Y-%m-%d %H:%M:%S\", &stm);\n            time_t t = mktime(&stm);\n            size_t decimalNum = 0;\n            if (p)\n            {\n                if (*p == '.')\n                {\n                    std::string decimals(p + 1, &timeStr[timeStr.length()]);\n                    while (decimals.length() < 6)\n                    {\n                        decimals += \"0\";\n                    }\n                    decimalNum = (size_t)atol(decimals.c_str());\n                }\n                createTime_ =\n                    std::make_shared<::trantor::Date>(t * 1000000 + decimalNum);\n            }\n        }\n    }\n}\n\nvoid Users::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int64_t>((int64_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(pJson[\"user_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"user_name\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"user_name\"].isNull())\n        {\n            userName_ =\n                std::make_shared<std::string>(pJson[\"user_name\"].asString());\n        }\n    }\n    if (pJson.isMember(\"password\"))\n    {\n        dirtyFlag_[3] = true;\n        if (!pJson[\"password\"].isNull())\n        {\n            password_ =\n                std::make_shared<std::string>(pJson[\"password\"].asString());\n        }\n    }\n    if (pJson.isMember(\"org_name\"))\n    {\n        dirtyFlag_[4] = true;\n        if (!pJson[\"org_name\"].isNull())\n        {\n            orgName_ =\n                std::make_shared<std::string>(pJson[\"org_name\"].asString());\n        }\n    }\n    if (pJson.isMember(\"signature\"))\n    {\n        dirtyFlag_[5] = true;\n        if (!pJson[\"signature\"].isNull())\n        {\n            signature_ =\n                std::make_shared<std::string>(pJson[\"signature\"].asString());\n        }\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        dirtyFlag_[6] = true;\n        if (!pJson[\"avatar_id\"].isNull())\n        {\n            avatarId_ =\n                std::make_shared<std::string>(pJson[\"avatar_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"salt\"))\n    {\n        dirtyFlag_[7] = true;\n        if (!pJson[\"salt\"].isNull())\n        {\n            salt_ = std::make_shared<std::string>(pJson[\"salt\"].asString());\n        }\n    }\n    if (pJson.isMember(\"admin\"))\n    {\n        dirtyFlag_[8] = true;\n        if (!pJson[\"admin\"].isNull())\n        {\n            admin_ = std::make_shared<std::string>(pJson[\"admin\"].asString());\n        }\n    }\n    if (pJson.isMember(\"create_time\"))\n    {\n        dirtyFlag_[9] = true;\n        if (!pJson[\"create_time\"].isNull())\n        {\n            auto timeStr = pJson[\"create_time\"].asString();\n            struct tm stm;\n            memset(&stm, 0, sizeof(stm));\n            auto p = strptime(timeStr.c_str(), \"%Y-%m-%d %H:%M:%S\", &stm);\n            time_t t = mktime(&stm);\n            size_t decimalNum = 0;\n            if (p)\n            {\n                if (*p == '.')\n                {\n                    std::string decimals(p + 1, &timeStr[timeStr.length()]);\n                    while (decimals.length() < 6)\n                    {\n                        decimals += \"0\";\n                    }\n                    decimalNum = (size_t)atol(decimals.c_str());\n                }\n                createTime_ =\n                    std::make_shared<::trantor::Date>(t * 1000000 + decimalNum);\n            }\n        }\n    }\n}\n\nconst int64_t &Users::getValueOfId() const noexcept\n{\n    static const int64_t defaultValue = int64_t();\n    if (id_)\n        return *id_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int64_t> &Users::getId() const noexcept\n{\n    return id_;\n}\n\nvoid Users::setId(const int64_t &pId) noexcept\n{\n    id_ = std::make_shared<int64_t>(pId);\n    dirtyFlag_[0] = true;\n}\n\nvoid Users::setIdToNull() noexcept\n{\n    id_.reset();\n    dirtyFlag_[0] = true;\n}\n\nconst typename Users::PrimaryKeyType &Users::getPrimaryKey() const\n{\n    assert(id_);\n    return *id_;\n}\n\nconst std::string &Users::getValueOfUserId() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (userId_)\n        return *userId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getUserId() const noexcept\n{\n    return userId_;\n}\n\nvoid Users::setUserId(const std::string &pUserId) noexcept\n{\n    userId_ = std::make_shared<std::string>(pUserId);\n    dirtyFlag_[1] = true;\n}\n\nvoid Users::setUserId(std::string &&pUserId) noexcept\n{\n    userId_ = std::make_shared<std::string>(std::move(pUserId));\n    dirtyFlag_[1] = true;\n}\n\nvoid Users::setUserIdToNull() noexcept\n{\n    userId_.reset();\n    dirtyFlag_[1] = true;\n}\n\nconst std::string &Users::getValueOfUserName() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (userName_)\n        return *userName_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getUserName() const noexcept\n{\n    return userName_;\n}\n\nvoid Users::setUserName(const std::string &pUserName) noexcept\n{\n    userName_ = std::make_shared<std::string>(pUserName);\n    dirtyFlag_[2] = true;\n}\n\nvoid Users::setUserName(std::string &&pUserName) noexcept\n{\n    userName_ = std::make_shared<std::string>(std::move(pUserName));\n    dirtyFlag_[2] = true;\n}\n\nvoid Users::setUserNameToNull() noexcept\n{\n    userName_.reset();\n    dirtyFlag_[2] = true;\n}\n\nconst std::string &Users::getValueOfPassword() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (password_)\n        return *password_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getPassword() const noexcept\n{\n    return password_;\n}\n\nvoid Users::setPassword(const std::string &pPassword) noexcept\n{\n    password_ = std::make_shared<std::string>(pPassword);\n    dirtyFlag_[3] = true;\n}\n\nvoid Users::setPassword(std::string &&pPassword) noexcept\n{\n    password_ = std::make_shared<std::string>(std::move(pPassword));\n    dirtyFlag_[3] = true;\n}\n\nvoid Users::setPasswordToNull() noexcept\n{\n    password_.reset();\n    dirtyFlag_[3] = true;\n}\n\nconst std::string &Users::getValueOfOrgName() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (orgName_)\n        return *orgName_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getOrgName() const noexcept\n{\n    return orgName_;\n}\n\nvoid Users::setOrgName(const std::string &pOrgName) noexcept\n{\n    orgName_ = std::make_shared<std::string>(pOrgName);\n    dirtyFlag_[4] = true;\n}\n\nvoid Users::setOrgName(std::string &&pOrgName) noexcept\n{\n    orgName_ = std::make_shared<std::string>(std::move(pOrgName));\n    dirtyFlag_[4] = true;\n}\n\nvoid Users::setOrgNameToNull() noexcept\n{\n    orgName_.reset();\n    dirtyFlag_[4] = true;\n}\n\nconst std::string &Users::getValueOfSignature() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (signature_)\n        return *signature_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getSignature() const noexcept\n{\n    return signature_;\n}\n\nvoid Users::setSignature(const std::string &pSignature) noexcept\n{\n    signature_ = std::make_shared<std::string>(pSignature);\n    dirtyFlag_[5] = true;\n}\n\nvoid Users::setSignature(std::string &&pSignature) noexcept\n{\n    signature_ = std::make_shared<std::string>(std::move(pSignature));\n    dirtyFlag_[5] = true;\n}\n\nvoid Users::setSignatureToNull() noexcept\n{\n    signature_.reset();\n    dirtyFlag_[5] = true;\n}\n\nconst std::string &Users::getValueOfAvatarId() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (avatarId_)\n        return *avatarId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getAvatarId() const noexcept\n{\n    return avatarId_;\n}\n\nvoid Users::setAvatarId(const std::string &pAvatarId) noexcept\n{\n    avatarId_ = std::make_shared<std::string>(pAvatarId);\n    dirtyFlag_[6] = true;\n}\n\nvoid Users::setAvatarId(std::string &&pAvatarId) noexcept\n{\n    avatarId_ = std::make_shared<std::string>(std::move(pAvatarId));\n    dirtyFlag_[6] = true;\n}\n\nvoid Users::setAvatarIdToNull() noexcept\n{\n    avatarId_.reset();\n    dirtyFlag_[6] = true;\n}\n\nconst std::string &Users::getValueOfSalt() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (salt_)\n        return *salt_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getSalt() const noexcept\n{\n    return salt_;\n}\n\nvoid Users::setSalt(const std::string &pSalt) noexcept\n{\n    salt_ = std::make_shared<std::string>(pSalt);\n    dirtyFlag_[7] = true;\n}\n\nvoid Users::setSalt(std::string &&pSalt) noexcept\n{\n    salt_ = std::make_shared<std::string>(std::move(pSalt));\n    dirtyFlag_[7] = true;\n}\n\nvoid Users::setSaltToNull() noexcept\n{\n    salt_.reset();\n    dirtyFlag_[7] = true;\n}\n\nconst std::string &Users::getValueOfAdmin() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (admin_)\n        return *admin_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Users::getAdmin() const noexcept\n{\n    return admin_;\n}\n\nvoid Users::setAdmin(const std::string &pAdmin) noexcept\n{\n    admin_ = std::make_shared<std::string>(pAdmin);\n    dirtyFlag_[8] = true;\n}\n\nvoid Users::setAdmin(std::string &&pAdmin) noexcept\n{\n    admin_ = std::make_shared<std::string>(std::move(pAdmin));\n    dirtyFlag_[8] = true;\n}\n\nvoid Users::setAdminToNull() noexcept\n{\n    admin_.reset();\n    dirtyFlag_[8] = true;\n}\n\nconst ::trantor::Date &Users::getValueOfCreateTime() const noexcept\n{\n    static const ::trantor::Date defaultValue = ::trantor::Date();\n    if (createTime_)\n        return *createTime_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<::trantor::Date> &Users::getCreateTime() const noexcept\n{\n    return createTime_;\n}\n\nvoid Users::setCreateTime(const ::trantor::Date &pCreateTime) noexcept\n{\n    createTime_ = std::make_shared<::trantor::Date>(pCreateTime);\n    dirtyFlag_[9] = true;\n}\n\nvoid Users::setCreateTimeToNull() noexcept\n{\n    createTime_.reset();\n    dirtyFlag_[9] = true;\n}\n\nvoid Users::updateId(const uint64_t id)\n{\n    id_ = std::make_shared<int64_t>(static_cast<int64_t>(id));\n}\n\nconst std::vector<std::string> &Users::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"user_id\",\n                                                    \"user_name\",\n                                                    \"password\",\n                                                    \"org_name\",\n                                                    \"signature\",\n                                                    \"avatar_id\",\n                                                    \"salt\",\n                                                    \"admin\",\n                                                    \"create_time\"};\n    return inCols;\n}\n\nvoid Users::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getUserId())\n        {\n            binder << getValueOfUserId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getUserName())\n        {\n            binder << getValueOfUserName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[3])\n    {\n        if (getPassword())\n        {\n            binder << getValueOfPassword();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[4])\n    {\n        if (getOrgName())\n        {\n            binder << getValueOfOrgName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[5])\n    {\n        if (getSignature())\n        {\n            binder << getValueOfSignature();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[6])\n    {\n        if (getAvatarId())\n        {\n            binder << getValueOfAvatarId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[7])\n    {\n        if (getSalt())\n        {\n            binder << getValueOfSalt();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[8])\n    {\n        if (getAdmin())\n        {\n            binder << getValueOfAdmin();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[9])\n    {\n        if (getCreateTime())\n        {\n            binder << getValueOfCreateTime();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Users::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    if (dirtyFlag_[2])\n    {\n        ret.push_back(getColumnName(2));\n    }\n    if (dirtyFlag_[3])\n    {\n        ret.push_back(getColumnName(3));\n    }\n    if (dirtyFlag_[4])\n    {\n        ret.push_back(getColumnName(4));\n    }\n    if (dirtyFlag_[5])\n    {\n        ret.push_back(getColumnName(5));\n    }\n    if (dirtyFlag_[6])\n    {\n        ret.push_back(getColumnName(6));\n    }\n    if (dirtyFlag_[7])\n    {\n        ret.push_back(getColumnName(7));\n    }\n    if (dirtyFlag_[8])\n    {\n        ret.push_back(getColumnName(8));\n    }\n    if (dirtyFlag_[9])\n    {\n        ret.push_back(getColumnName(9));\n    }\n    return ret;\n}\n\nvoid Users::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getUserId())\n        {\n            binder << getValueOfUserId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getUserName())\n        {\n            binder << getValueOfUserName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[3])\n    {\n        if (getPassword())\n        {\n            binder << getValueOfPassword();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[4])\n    {\n        if (getOrgName())\n        {\n            binder << getValueOfOrgName();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[5])\n    {\n        if (getSignature())\n        {\n            binder << getValueOfSignature();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[6])\n    {\n        if (getAvatarId())\n        {\n            binder << getValueOfAvatarId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[7])\n    {\n        if (getSalt())\n        {\n            binder << getValueOfSalt();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[8])\n    {\n        if (getAdmin())\n        {\n            binder << getValueOfAdmin();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[9])\n    {\n        if (getCreateTime())\n        {\n            binder << getValueOfCreateTime();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Users::toJson() const\n{\n    Json::Value ret;\n    if (getId())\n    {\n        ret[\"id\"] = (Json::Int64)getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getUserId())\n    {\n        ret[\"user_id\"] = getValueOfUserId();\n    }\n    else\n    {\n        ret[\"user_id\"] = Json::Value();\n    }\n    if (getUserName())\n    {\n        ret[\"user_name\"] = getValueOfUserName();\n    }\n    else\n    {\n        ret[\"user_name\"] = Json::Value();\n    }\n    if (getPassword())\n    {\n        ret[\"password\"] = getValueOfPassword();\n    }\n    else\n    {\n        ret[\"password\"] = Json::Value();\n    }\n    if (getOrgName())\n    {\n        ret[\"org_name\"] = getValueOfOrgName();\n    }\n    else\n    {\n        ret[\"org_name\"] = Json::Value();\n    }\n    if (getSignature())\n    {\n        ret[\"signature\"] = getValueOfSignature();\n    }\n    else\n    {\n        ret[\"signature\"] = Json::Value();\n    }\n    if (getAvatarId())\n    {\n        ret[\"avatar_id\"] = getValueOfAvatarId();\n    }\n    else\n    {\n        ret[\"avatar_id\"] = Json::Value();\n    }\n    if (getSalt())\n    {\n        ret[\"salt\"] = getValueOfSalt();\n    }\n    else\n    {\n        ret[\"salt\"] = Json::Value();\n    }\n    if (getAdmin())\n    {\n        ret[\"admin\"] = getValueOfAdmin();\n    }\n    else\n    {\n        ret[\"admin\"] = Json::Value();\n    }\n    if (getCreateTime())\n    {\n        ret[\"create_time\"] = getCreateTime()->toDbStringLocal();\n    }\n    else\n    {\n        ret[\"create_time\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Users::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 10)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getId())\n            {\n                ret[pMasqueradingVector[0]] = (Json::Int64)getValueOfId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getUserId())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfUserId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (getUserName())\n            {\n                ret[pMasqueradingVector[2]] = getValueOfUserName();\n            }\n            else\n            {\n                ret[pMasqueradingVector[2]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[3].empty())\n        {\n            if (getPassword())\n            {\n                ret[pMasqueradingVector[3]] = getValueOfPassword();\n            }\n            else\n            {\n                ret[pMasqueradingVector[3]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[4].empty())\n        {\n            if (getOrgName())\n            {\n                ret[pMasqueradingVector[4]] = getValueOfOrgName();\n            }\n            else\n            {\n                ret[pMasqueradingVector[4]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[5].empty())\n        {\n            if (getSignature())\n            {\n                ret[pMasqueradingVector[5]] = getValueOfSignature();\n            }\n            else\n            {\n                ret[pMasqueradingVector[5]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[6].empty())\n        {\n            if (getAvatarId())\n            {\n                ret[pMasqueradingVector[6]] = getValueOfAvatarId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[6]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[7].empty())\n        {\n            if (getSalt())\n            {\n                ret[pMasqueradingVector[7]] = getValueOfSalt();\n            }\n            else\n            {\n                ret[pMasqueradingVector[7]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[8].empty())\n        {\n            if (getAdmin())\n            {\n                ret[pMasqueradingVector[8]] = getValueOfAdmin();\n            }\n            else\n            {\n                ret[pMasqueradingVector[8]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[9].empty())\n        {\n            if (getCreateTime())\n            {\n                ret[pMasqueradingVector[9]] =\n                    getCreateTime()->toDbStringLocal();\n            }\n            else\n            {\n                ret[pMasqueradingVector[9]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getId())\n    {\n        ret[\"id\"] = (Json::Int64)getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getUserId())\n    {\n        ret[\"user_id\"] = getValueOfUserId();\n    }\n    else\n    {\n        ret[\"user_id\"] = Json::Value();\n    }\n    if (getUserName())\n    {\n        ret[\"user_name\"] = getValueOfUserName();\n    }\n    else\n    {\n        ret[\"user_name\"] = Json::Value();\n    }\n    if (getPassword())\n    {\n        ret[\"password\"] = getValueOfPassword();\n    }\n    else\n    {\n        ret[\"password\"] = Json::Value();\n    }\n    if (getOrgName())\n    {\n        ret[\"org_name\"] = getValueOfOrgName();\n    }\n    else\n    {\n        ret[\"org_name\"] = Json::Value();\n    }\n    if (getSignature())\n    {\n        ret[\"signature\"] = getValueOfSignature();\n    }\n    else\n    {\n        ret[\"signature\"] = Json::Value();\n    }\n    if (getAvatarId())\n    {\n        ret[\"avatar_id\"] = getValueOfAvatarId();\n    }\n    else\n    {\n        ret[\"avatar_id\"] = Json::Value();\n    }\n    if (getSalt())\n    {\n        ret[\"salt\"] = getValueOfSalt();\n    }\n    else\n    {\n        ret[\"salt\"] = Json::Value();\n    }\n    if (getAdmin())\n    {\n        ret[\"admin\"] = getValueOfAdmin();\n    }\n    else\n    {\n        ret[\"admin\"] = Json::Value();\n    }\n    if (getCreateTime())\n    {\n        ret[\"create_time\"] = getCreateTime()->toDbStringLocal();\n    }\n    else\n    {\n        ret[\"create_time\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Users::validateJsonForCreation(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        if (!validJsonOfField(1, \"user_id\", pJson[\"user_id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"user_name\"))\n    {\n        if (!validJsonOfField(2, \"user_name\", pJson[\"user_name\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"password\"))\n    {\n        if (!validJsonOfField(3, \"password\", pJson[\"password\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"org_name\"))\n    {\n        if (!validJsonOfField(4, \"org_name\", pJson[\"org_name\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"signature\"))\n    {\n        if (!validJsonOfField(5, \"signature\", pJson[\"signature\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        if (!validJsonOfField(6, \"avatar_id\", pJson[\"avatar_id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"salt\"))\n    {\n        if (!validJsonOfField(7, \"salt\", pJson[\"salt\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"admin\"))\n    {\n        if (!validJsonOfField(8, \"admin\", pJson[\"admin\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"create_time\"))\n    {\n        if (!validJsonOfField(\n                9, \"create_time\", pJson[\"create_time\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Users::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 10)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[2]))\n            {\n                if (!validJsonOfField(2,\n                                      pMasqueradingVector[2],\n                                      pJson[pMasqueradingVector[2]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[3].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[3]))\n            {\n                if (!validJsonOfField(3,\n                                      pMasqueradingVector[3],\n                                      pJson[pMasqueradingVector[3]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[4].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[4]))\n            {\n                if (!validJsonOfField(4,\n                                      pMasqueradingVector[4],\n                                      pJson[pMasqueradingVector[4]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[5].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[5]))\n            {\n                if (!validJsonOfField(5,\n                                      pMasqueradingVector[5],\n                                      pJson[pMasqueradingVector[5]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[6].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[6]))\n            {\n                if (!validJsonOfField(6,\n                                      pMasqueradingVector[6],\n                                      pJson[pMasqueradingVector[6]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[7].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[7]))\n            {\n                if (!validJsonOfField(7,\n                                      pMasqueradingVector[7],\n                                      pJson[pMasqueradingVector[7]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[8].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[8]))\n            {\n                if (!validJsonOfField(8,\n                                      pMasqueradingVector[8],\n                                      pJson[pMasqueradingVector[8]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[9].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[9]))\n            {\n                if (!validJsonOfField(9,\n                                      pMasqueradingVector[9],\n                                      pJson[pMasqueradingVector[9]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Users::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        if (!validJsonOfField(1, \"user_id\", pJson[\"user_id\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"user_name\"))\n    {\n        if (!validJsonOfField(2, \"user_name\", pJson[\"user_name\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"password\"))\n    {\n        if (!validJsonOfField(3, \"password\", pJson[\"password\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"org_name\"))\n    {\n        if (!validJsonOfField(4, \"org_name\", pJson[\"org_name\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"signature\"))\n    {\n        if (!validJsonOfField(5, \"signature\", pJson[\"signature\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"avatar_id\"))\n    {\n        if (!validJsonOfField(6, \"avatar_id\", pJson[\"avatar_id\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"salt\"))\n    {\n        if (!validJsonOfField(7, \"salt\", pJson[\"salt\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"admin\"))\n    {\n        if (!validJsonOfField(8, \"admin\", pJson[\"admin\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"create_time\"))\n    {\n        if (!validJsonOfField(\n                9, \"create_time\", pJson[\"create_time\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Users::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 10)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[2].empty() &&\n            pJson.isMember(pMasqueradingVector[2]))\n        {\n            if (!validJsonOfField(2,\n                                  pMasqueradingVector[2],\n                                  pJson[pMasqueradingVector[2]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[3].empty() &&\n            pJson.isMember(pMasqueradingVector[3]))\n        {\n            if (!validJsonOfField(3,\n                                  pMasqueradingVector[3],\n                                  pJson[pMasqueradingVector[3]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[4].empty() &&\n            pJson.isMember(pMasqueradingVector[4]))\n        {\n            if (!validJsonOfField(4,\n                                  pMasqueradingVector[4],\n                                  pJson[pMasqueradingVector[4]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[5].empty() &&\n            pJson.isMember(pMasqueradingVector[5]))\n        {\n            if (!validJsonOfField(5,\n                                  pMasqueradingVector[5],\n                                  pJson[pMasqueradingVector[5]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[6].empty() &&\n            pJson.isMember(pMasqueradingVector[6]))\n        {\n            if (!validJsonOfField(6,\n                                  pMasqueradingVector[6],\n                                  pJson[pMasqueradingVector[6]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[7].empty() &&\n            pJson.isMember(pMasqueradingVector[7]))\n        {\n            if (!validJsonOfField(7,\n                                  pMasqueradingVector[7],\n                                  pJson[pMasqueradingVector[7]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[8].empty() &&\n            pJson.isMember(pMasqueradingVector[8]))\n        {\n            if (!validJsonOfField(8,\n                                  pMasqueradingVector[8],\n                                  pJson[pMasqueradingVector[8]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[9].empty() &&\n            pJson.isMember(pMasqueradingVector[9]))\n        {\n            if (!validJsonOfField(9,\n                                  pMasqueradingVector[9],\n                                  pJson[pMasqueradingVector[9]],\n                                  err,\n                                  false))\n                return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Users::validJsonOfField(size_t index,\n                             const std::string &fieldName,\n                             const Json::Value &pJson,\n                             std::string &err,\n                             bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (isForCreation)\n            {\n                err = \"The automatic primary key cannot be set\";\n                return false;\n            }\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isInt64())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 2:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 3:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 4:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 5:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 6:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 7:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 8:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 9:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n\nWallets Users::getWallet(const DbClientPtr &clientPtr) const\n{\n    static const std::string sql = \"select * from wallets where user_id = ?\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *userId_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    if (r.size() == 0)\n    {\n        throw UnexpectedRows(\"0 rows found\");\n    }\n    else if (r.size() > 1)\n    {\n        throw UnexpectedRows(\"Found more than one row\");\n    }\n    return Wallets(r[0]);\n}\n\nvoid Users::getWallet(const DbClientPtr &clientPtr,\n                      const std::function<void(Wallets)> &rcb,\n                      const ExceptionCallback &ecb) const\n{\n    static const std::string sql = \"select * from wallets where user_id = ?\";\n    *clientPtr << sql << *userId_ >> [rcb = std::move(rcb),\n                                      ecb](const Result &r) {\n        if (r.size() == 0)\n        {\n            ecb(UnexpectedRows(\"0 rows found\"));\n        }\n        else if (r.size() > 1)\n        {\n            ecb(UnexpectedRows(\"Found more than one row\"));\n        }\n        else\n        {\n            rcb(Wallets(r[0]));\n        }\n    } >> ecb;\n}\n"
  },
  {
    "path": "orm_lib/tests/sqlite3/Users.h",
    "content": "/**\n *\n *  Users.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace sqlite3\n{\nclass Wallets;\n\nclass Users\n{\n  public:\n    struct Cols\n    {\n        static const std::string _id;\n        static const std::string _user_id;\n        static const std::string _user_name;\n        static const std::string _password;\n        static const std::string _org_name;\n        static const std::string _signature;\n        static const std::string _avatar_id;\n        static const std::string _salt;\n        static const std::string _admin;\n        static const std::string _create_time;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = int64_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Users(const drogon::orm::Row &r,\n                   const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Users(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Users(const Json::Value &pJson,\n          const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Users() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column id  */\n    /// Get the value of the column id, returns the default value if the column\n    /// is null\n    const int64_t &getValueOfId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int64_t> &getId() const noexcept;\n    /// Set the value of the column id\n    void setId(const int64_t &pId) noexcept;\n    void setIdToNull() noexcept;\n\n    /**  For column user_id  */\n    /// Get the value of the column user_id, returns the default value if the\n    /// column is null\n    const std::string &getValueOfUserId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getUserId() const noexcept;\n    /// Set the value of the column user_id\n    void setUserId(const std::string &pUserId) noexcept;\n    void setUserId(std::string &&pUserId) noexcept;\n    void setUserIdToNull() noexcept;\n\n    /**  For column user_name  */\n    /// Get the value of the column user_name, returns the default value if the\n    /// column is null\n    const std::string &getValueOfUserName() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getUserName() const noexcept;\n    /// Set the value of the column user_name\n    void setUserName(const std::string &pUserName) noexcept;\n    void setUserName(std::string &&pUserName) noexcept;\n    void setUserNameToNull() noexcept;\n\n    /**  For column password  */\n    /// Get the value of the column password, returns the default value if the\n    /// column is null\n    const std::string &getValueOfPassword() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getPassword() const noexcept;\n    /// Set the value of the column password\n    void setPassword(const std::string &pPassword) noexcept;\n    void setPassword(std::string &&pPassword) noexcept;\n    void setPasswordToNull() noexcept;\n\n    /**  For column org_name  */\n    /// Get the value of the column org_name, returns the default value if the\n    /// column is null\n    const std::string &getValueOfOrgName() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getOrgName() const noexcept;\n    /// Set the value of the column org_name\n    void setOrgName(const std::string &pOrgName) noexcept;\n    void setOrgName(std::string &&pOrgName) noexcept;\n    void setOrgNameToNull() noexcept;\n\n    /**  For column signature  */\n    /// Get the value of the column signature, returns the default value if the\n    /// column is null\n    const std::string &getValueOfSignature() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getSignature() const noexcept;\n    /// Set the value of the column signature\n    void setSignature(const std::string &pSignature) noexcept;\n    void setSignature(std::string &&pSignature) noexcept;\n    void setSignatureToNull() noexcept;\n\n    /**  For column avatar_id  */\n    /// Get the value of the column avatar_id, returns the default value if the\n    /// column is null\n    const std::string &getValueOfAvatarId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getAvatarId() const noexcept;\n    /// Set the value of the column avatar_id\n    void setAvatarId(const std::string &pAvatarId) noexcept;\n    void setAvatarId(std::string &&pAvatarId) noexcept;\n    void setAvatarIdToNull() noexcept;\n\n    /**  For column salt  */\n    /// Get the value of the column salt, returns the default value if the\n    /// column is null\n    const std::string &getValueOfSalt() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getSalt() const noexcept;\n    /// Set the value of the column salt\n    void setSalt(const std::string &pSalt) noexcept;\n    void setSalt(std::string &&pSalt) noexcept;\n    void setSaltToNull() noexcept;\n\n    /**  For column admin  */\n    /// Get the value of the column admin, returns the default value if the\n    /// column is null\n    const std::string &getValueOfAdmin() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getAdmin() const noexcept;\n    /// Set the value of the column admin\n    void setAdmin(const std::string &pAdmin) noexcept;\n    void setAdmin(std::string &&pAdmin) noexcept;\n    void setAdminToNull() noexcept;\n\n    /**  For column create_time  */\n    /// Get the value of the column create_time, returns the default value if\n    /// the column is null\n    const ::trantor::Date &getValueOfCreateTime() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<::trantor::Date> &getCreateTime() const noexcept;\n    /// Set the value of the column create_time\n    void setCreateTime(const ::trantor::Date &pCreateTime) noexcept;\n    void setCreateTimeToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 10;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n    Wallets getWallet(const drogon::orm::DbClientPtr &clientPtr) const;\n    void getWallet(const drogon::orm::DbClientPtr &clientPtr,\n                   const std::function<void(Wallets)> &rcb,\n                   const drogon::orm::ExceptionCallback &ecb) const;\n\n  private:\n    friend drogon::orm::Mapper<Users>;\n    friend drogon::orm::BaseBuilder<Users, true, true>;\n    friend drogon::orm::BaseBuilder<Users, true, false>;\n    friend drogon::orm::BaseBuilder<Users, false, true>;\n    friend drogon::orm::BaseBuilder<Users, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<Users>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int64_t> id_;\n    std::shared_ptr<std::string> userId_;\n    std::shared_ptr<std::string> userName_;\n    std::shared_ptr<std::string> password_;\n    std::shared_ptr<std::string> orgName_;\n    std::shared_ptr<std::string> signature_;\n    std::shared_ptr<std::string> avatarId_;\n    std::shared_ptr<std::string> salt_;\n    std::shared_ptr<std::string> admin_;\n    std::shared_ptr<::trantor::Date> createTime_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[10] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        if (dirtyFlag_[1])\n        {\n            sql += \"user_id,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[2])\n        {\n            sql += \"user_name,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[3])\n        {\n            sql += \"password,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[4])\n        {\n            sql += \"org_name,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[5])\n        {\n            sql += \"signature,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[6])\n        {\n            sql += \"avatar_id,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[7])\n        {\n            sql += \"salt,\";\n            ++parametersCount;\n        }\n        if (dirtyFlag_[8])\n        {\n            sql += \"admin,\";\n            ++parametersCount;\n        }\n        if (!dirtyFlag_[8])\n        {\n            needSelection = true;\n        }\n        if (dirtyFlag_[9])\n        {\n            sql += \"create_time,\";\n            ++parametersCount;\n        }\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        if (dirtyFlag_[1])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[2])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[3])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[4])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[5])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[6])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[7])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[8])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[9])\n        {\n            sql.append(\"?,\");\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        sql.append(1, ')');\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace sqlite3\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/sqlite3/Wallets.cc",
    "content": "/**\n *\n *  Wallets.cc\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#include \"Wallets.h\"\n#include \"Users.h\"\n#include <drogon/utils/Utilities.h>\n#include <string>\n\nusing namespace drogon;\nusing namespace drogon::orm;\nusing namespace drogon_model::sqlite3;\n\nconst std::string Wallets::Cols::_id = \"id\";\nconst std::string Wallets::Cols::_user_id = \"user_id\";\nconst std::string Wallets::Cols::_amount = \"amount\";\nconst std::string Wallets::primaryKeyName = \"id\";\nconst bool Wallets::hasPrimaryKey = true;\nconst std::string Wallets::tableName = \"wallets\";\n\nconst std::vector<typename Wallets::MetaData> Wallets::metaData_ = {\n    {\"id\", \"int64_t\", \"integer\", 8, 1, 1, 0},\n    {\"user_id\", \"std::string\", \"varchar(32)\", 0, 0, 0, 0},\n    {\"amount\", \"std::string\", \"decimal(16,2)\", 0, 0, 0, 0}};\n\nconst std::string &Wallets::getColumnName(size_t index) noexcept(false)\n{\n    assert(index < metaData_.size());\n    return metaData_[index].colName_;\n}\n\nWallets::Wallets(const Row &r, const ssize_t indexOffset) noexcept\n{\n    if (indexOffset < 0)\n    {\n        if (!r[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int64_t>(r[\"id\"].as<int64_t>());\n        }\n        if (!r[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(r[\"user_id\"].as<std::string>());\n        }\n        if (!r[\"amount\"].isNull())\n        {\n            amount_ =\n                std::make_shared<std::string>(r[\"amount\"].as<std::string>());\n        }\n    }\n    else\n    {\n        size_t offset = (size_t)indexOffset;\n        if (offset + 3 > r.size())\n        {\n            LOG_FATAL << \"Invalid SQL result for this model\";\n            return;\n        }\n        size_t index;\n        index = offset + 0;\n        if (!r[index].isNull())\n        {\n            id_ = std::make_shared<int64_t>(r[index].as<int64_t>());\n        }\n        index = offset + 1;\n        if (!r[index].isNull())\n        {\n            userId_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n        index = offset + 2;\n        if (!r[index].isNull())\n        {\n            amount_ = std::make_shared<std::string>(r[index].as<std::string>());\n        }\n    }\n}\n\nWallets::Wallets(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            userId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            amount_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[2]].asString());\n        }\n    }\n}\n\nWallets::Wallets(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        dirtyFlag_[0] = true;\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int64_t>((int64_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(pJson[\"user_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"amount\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"amount\"].isNull())\n        {\n            amount_ = std::make_shared<std::string>(pJson[\"amount\"].asString());\n        }\n    }\n}\n\nvoid Wallets::updateByMasqueradedJson(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector) noexcept(false)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        LOG_ERROR << \"Bad masquerading vector\";\n        return;\n    }\n    if (!pMasqueradingVector[0].empty() &&\n        pJson.isMember(pMasqueradingVector[0]))\n    {\n        if (!pJson[pMasqueradingVector[0]].isNull())\n        {\n            id_ = std::make_shared<int64_t>(\n                (int64_t)pJson[pMasqueradingVector[0]].asInt64());\n        }\n    }\n    if (!pMasqueradingVector[1].empty() &&\n        pJson.isMember(pMasqueradingVector[1]))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[pMasqueradingVector[1]].isNull())\n        {\n            userId_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[1]].asString());\n        }\n    }\n    if (!pMasqueradingVector[2].empty() &&\n        pJson.isMember(pMasqueradingVector[2]))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[pMasqueradingVector[2]].isNull())\n        {\n            amount_ = std::make_shared<std::string>(\n                pJson[pMasqueradingVector[2]].asString());\n        }\n    }\n}\n\nvoid Wallets::updateByJson(const Json::Value &pJson) noexcept(false)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!pJson[\"id\"].isNull())\n        {\n            id_ = std::make_shared<int64_t>((int64_t)pJson[\"id\"].asInt64());\n        }\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        dirtyFlag_[1] = true;\n        if (!pJson[\"user_id\"].isNull())\n        {\n            userId_ =\n                std::make_shared<std::string>(pJson[\"user_id\"].asString());\n        }\n    }\n    if (pJson.isMember(\"amount\"))\n    {\n        dirtyFlag_[2] = true;\n        if (!pJson[\"amount\"].isNull())\n        {\n            amount_ = std::make_shared<std::string>(pJson[\"amount\"].asString());\n        }\n    }\n}\n\nconst int64_t &Wallets::getValueOfId() const noexcept\n{\n    static const int64_t defaultValue = int64_t();\n    if (id_)\n        return *id_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<int64_t> &Wallets::getId() const noexcept\n{\n    return id_;\n}\n\nvoid Wallets::setId(const int64_t &pId) noexcept\n{\n    id_ = std::make_shared<int64_t>(pId);\n    dirtyFlag_[0] = true;\n}\n\nvoid Wallets::setIdToNull() noexcept\n{\n    id_.reset();\n    dirtyFlag_[0] = true;\n}\n\nconst typename Wallets::PrimaryKeyType &Wallets::getPrimaryKey() const\n{\n    assert(id_);\n    return *id_;\n}\n\nconst std::string &Wallets::getValueOfUserId() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (userId_)\n        return *userId_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Wallets::getUserId() const noexcept\n{\n    return userId_;\n}\n\nvoid Wallets::setUserId(const std::string &pUserId) noexcept\n{\n    userId_ = std::make_shared<std::string>(pUserId);\n    dirtyFlag_[1] = true;\n}\n\nvoid Wallets::setUserId(std::string &&pUserId) noexcept\n{\n    userId_ = std::make_shared<std::string>(std::move(pUserId));\n    dirtyFlag_[1] = true;\n}\n\nvoid Wallets::setUserIdToNull() noexcept\n{\n    userId_.reset();\n    dirtyFlag_[1] = true;\n}\n\nconst std::string &Wallets::getValueOfAmount() const noexcept\n{\n    static const std::string defaultValue = std::string();\n    if (amount_)\n        return *amount_;\n    return defaultValue;\n}\n\nconst std::shared_ptr<std::string> &Wallets::getAmount() const noexcept\n{\n    return amount_;\n}\n\nvoid Wallets::setAmount(const std::string &pAmount) noexcept\n{\n    amount_ = std::make_shared<std::string>(pAmount);\n    dirtyFlag_[2] = true;\n}\n\nvoid Wallets::setAmount(std::string &&pAmount) noexcept\n{\n    amount_ = std::make_shared<std::string>(std::move(pAmount));\n    dirtyFlag_[2] = true;\n}\n\nvoid Wallets::setAmountToNull() noexcept\n{\n    amount_.reset();\n    dirtyFlag_[2] = true;\n}\n\nvoid Wallets::updateId(const uint64_t id)\n{\n    id_ = std::make_shared<int64_t>(static_cast<int64_t>(id));\n}\n\nconst std::vector<std::string> &Wallets::insertColumns() noexcept\n{\n    static const std::vector<std::string> inCols = {\"user_id\", \"amount\"};\n    return inCols;\n}\n\nvoid Wallets::outputArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getUserId())\n        {\n            binder << getValueOfUserId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getAmount())\n        {\n            binder << getValueOfAmount();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nconst std::vector<std::string> Wallets::updateColumns() const\n{\n    std::vector<std::string> ret;\n    if (dirtyFlag_[1])\n    {\n        ret.push_back(getColumnName(1));\n    }\n    if (dirtyFlag_[2])\n    {\n        ret.push_back(getColumnName(2));\n    }\n    return ret;\n}\n\nvoid Wallets::updateArgs(drogon::orm::internal::SqlBinder &binder) const\n{\n    if (dirtyFlag_[1])\n    {\n        if (getUserId())\n        {\n            binder << getValueOfUserId();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n    if (dirtyFlag_[2])\n    {\n        if (getAmount())\n        {\n            binder << getValueOfAmount();\n        }\n        else\n        {\n            binder << nullptr;\n        }\n    }\n}\n\nJson::Value Wallets::toJson() const\n{\n    Json::Value ret;\n    if (getId())\n    {\n        ret[\"id\"] = (Json::Int64)getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getUserId())\n    {\n        ret[\"user_id\"] = getValueOfUserId();\n    }\n    else\n    {\n        ret[\"user_id\"] = Json::Value();\n    }\n    if (getAmount())\n    {\n        ret[\"amount\"] = getValueOfAmount();\n    }\n    else\n    {\n        ret[\"amount\"] = Json::Value();\n    }\n    return ret;\n}\n\nJson::Value Wallets::toMasqueradedJson(\n    const std::vector<std::string> &pMasqueradingVector) const\n{\n    Json::Value ret;\n    if (pMasqueradingVector.size() == 3)\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (getId())\n            {\n                ret[pMasqueradingVector[0]] = (Json::Int64)getValueOfId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[0]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (getUserId())\n            {\n                ret[pMasqueradingVector[1]] = getValueOfUserId();\n            }\n            else\n            {\n                ret[pMasqueradingVector[1]] = Json::Value();\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (getAmount())\n            {\n                ret[pMasqueradingVector[2]] = getValueOfAmount();\n            }\n            else\n            {\n                ret[pMasqueradingVector[2]] = Json::Value();\n            }\n        }\n        return ret;\n    }\n    LOG_ERROR << \"Masquerade failed\";\n    if (getId())\n    {\n        ret[\"id\"] = (Json::Int64)getValueOfId();\n    }\n    else\n    {\n        ret[\"id\"] = Json::Value();\n    }\n    if (getUserId())\n    {\n        ret[\"user_id\"] = getValueOfUserId();\n    }\n    else\n    {\n        ret[\"user_id\"] = Json::Value();\n    }\n    if (getAmount())\n    {\n        ret[\"amount\"] = getValueOfAmount();\n    }\n    else\n    {\n        ret[\"amount\"] = Json::Value();\n    }\n    return ret;\n}\n\nbool Wallets::validateJsonForCreation(const Json::Value &pJson,\n                                      std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        if (!validJsonOfField(1, \"user_id\", pJson[\"user_id\"], err, true))\n            return false;\n    }\n    if (pJson.isMember(\"amount\"))\n    {\n        if (!validJsonOfField(2, \"amount\", pJson[\"amount\"], err, true))\n            return false;\n    }\n    return true;\n}\n\nbool Wallets::validateMasqueradedJsonForCreation(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[0]))\n            {\n                if (!validJsonOfField(0,\n                                      pMasqueradingVector[0],\n                                      pJson[pMasqueradingVector[0]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[1].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[1]))\n            {\n                if (!validJsonOfField(1,\n                                      pMasqueradingVector[1],\n                                      pJson[pMasqueradingVector[1]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n        if (!pMasqueradingVector[2].empty())\n        {\n            if (pJson.isMember(pMasqueradingVector[2]))\n            {\n                if (!validJsonOfField(2,\n                                      pMasqueradingVector[2],\n                                      pJson[pMasqueradingVector[2]],\n                                      err,\n                                      true))\n                    return false;\n            }\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Wallets::validateJsonForUpdate(const Json::Value &pJson, std::string &err)\n{\n    if (pJson.isMember(\"id\"))\n    {\n        if (!validJsonOfField(0, \"id\", pJson[\"id\"], err, false))\n            return false;\n    }\n    else\n    {\n        err =\n            \"The value of primary key must be set in the json object for \"\n            \"update\";\n        return false;\n    }\n    if (pJson.isMember(\"user_id\"))\n    {\n        if (!validJsonOfField(1, \"user_id\", pJson[\"user_id\"], err, false))\n            return false;\n    }\n    if (pJson.isMember(\"amount\"))\n    {\n        if (!validJsonOfField(2, \"amount\", pJson[\"amount\"], err, false))\n            return false;\n    }\n    return true;\n}\n\nbool Wallets::validateMasqueradedJsonForUpdate(\n    const Json::Value &pJson,\n    const std::vector<std::string> &pMasqueradingVector,\n    std::string &err)\n{\n    if (pMasqueradingVector.size() != 3)\n    {\n        err = \"Bad masquerading vector\";\n        return false;\n    }\n    try\n    {\n        if (!pMasqueradingVector[0].empty() &&\n            pJson.isMember(pMasqueradingVector[0]))\n        {\n            if (!validJsonOfField(0,\n                                  pMasqueradingVector[0],\n                                  pJson[pMasqueradingVector[0]],\n                                  err,\n                                  false))\n                return false;\n        }\n        else\n        {\n            err =\n                \"The value of primary key must be set in the json object for \"\n                \"update\";\n            return false;\n        }\n        if (!pMasqueradingVector[1].empty() &&\n            pJson.isMember(pMasqueradingVector[1]))\n        {\n            if (!validJsonOfField(1,\n                                  pMasqueradingVector[1],\n                                  pJson[pMasqueradingVector[1]],\n                                  err,\n                                  false))\n                return false;\n        }\n        if (!pMasqueradingVector[2].empty() &&\n            pJson.isMember(pMasqueradingVector[2]))\n        {\n            if (!validJsonOfField(2,\n                                  pMasqueradingVector[2],\n                                  pJson[pMasqueradingVector[2]],\n                                  err,\n                                  false))\n                return false;\n        }\n    }\n    catch (const Json::LogicError &e)\n    {\n        err = e.what();\n        return false;\n    }\n    return true;\n}\n\nbool Wallets::validJsonOfField(size_t index,\n                               const std::string &fieldName,\n                               const Json::Value &pJson,\n                               std::string &err,\n                               bool isForCreation)\n{\n    switch (index)\n    {\n        case 0:\n            if (isForCreation)\n            {\n                err = \"The automatic primary key cannot be set\";\n                return false;\n            }\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isInt64())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 1:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        case 2:\n            if (pJson.isNull())\n            {\n                return true;\n            }\n            if (!pJson.isString())\n            {\n                err = \"Type error in the \" + fieldName + \" field\";\n                return false;\n            }\n            break;\n        default:\n            err = \"Internal error in the server\";\n            return false;\n    }\n    return true;\n}\n\nUsers Wallets::getUser(const DbClientPtr &clientPtr) const\n{\n    static const std::string sql = \"select * from users where user_id = ?\";\n    Result r(nullptr);\n    {\n        auto binder = *clientPtr << sql;\n        binder << *userId_ << Mode::Blocking >>\n            [&r](const Result &result) { r = result; };\n        binder.exec();\n    }\n    if (r.size() == 0)\n    {\n        throw UnexpectedRows(\"0 rows found\");\n    }\n    else if (r.size() > 1)\n    {\n        throw UnexpectedRows(\"Found more than one row\");\n    }\n    return Users(r[0]);\n}\n\nvoid Wallets::getUser(const DbClientPtr &clientPtr,\n                      const std::function<void(Users)> &rcb,\n                      const ExceptionCallback &ecb) const\n{\n    static const std::string sql = \"select * from users where user_id = ?\";\n    *clientPtr << sql << *userId_ >> [rcb = std::move(rcb),\n                                      ecb](const Result &r) {\n        if (r.size() == 0)\n        {\n            ecb(UnexpectedRows(\"0 rows found\"));\n        }\n        else if (r.size() > 1)\n        {\n            ecb(UnexpectedRows(\"Found more than one row\"));\n        }\n        else\n        {\n            rcb(Users(r[0]));\n        }\n    } >> ecb;\n}\n"
  },
  {
    "path": "orm_lib/tests/sqlite3/Wallets.h",
    "content": "/**\n *\n *  Wallets.h\n *  DO NOT EDIT. This file is generated by drogon_ctl\n *\n */\n\n#pragma once\n#include <drogon/orm/Result.h>\n#include <drogon/orm/Row.h>\n#include <drogon/orm/Field.h>\n#include <drogon/orm/SqlBinder.h>\n#include <drogon/orm/Mapper.h>\n#include <drogon/orm/BaseBuilder.h>\n#ifdef __cpp_impl_coroutine\n#include <drogon/orm/CoroMapper.h>\n#endif\n#include <trantor/utils/Date.h>\n#include <trantor/utils/Logger.h>\n#include <json/json.h>\n#include <string>\n#include <string_view>\n#include <memory>\n#include <vector>\n#include <tuple>\n#include <stdint.h>\n#include <iostream>\n\nnamespace drogon\n{\nnamespace orm\n{\nclass DbClient;\nusing DbClientPtr = std::shared_ptr<DbClient>;\n}  // namespace orm\n}  // namespace drogon\n\nnamespace drogon_model\n{\nnamespace sqlite3\n{\nclass Users;\n\nclass Wallets\n{\n  public:\n    struct Cols\n    {\n        static const std::string _id;\n        static const std::string _user_id;\n        static const std::string _amount;\n    };\n\n    static const int primaryKeyNumber;\n    static const std::string tableName;\n    static const bool hasPrimaryKey;\n    static const std::string primaryKeyName;\n    using PrimaryKeyType = int64_t;\n    const PrimaryKeyType &getPrimaryKey() const;\n\n    /**\n     * @brief constructor\n     * @param r One row of records in the SQL query result.\n     * @param indexOffset Set the offset to -1 to access all columns by column\n     * names, otherwise access all columns by offsets.\n     * @note If the SQL is not a style of 'select * from table_name ...' (select\n     * all columns by an asterisk), please set the offset to -1.\n     */\n    explicit Wallets(const drogon::orm::Row &r,\n                     const ssize_t indexOffset = 0) noexcept;\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     */\n    explicit Wallets(const Json::Value &pJson) noexcept(false);\n\n    /**\n     * @brief constructor\n     * @param pJson The json object to construct a new instance.\n     * @param pMasqueradingVector The aliases of table columns.\n     */\n    Wallets(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n\n    Wallets() = default;\n\n    void updateByJson(const Json::Value &pJson) noexcept(false);\n    void updateByMasqueradedJson(\n        const Json::Value &pJson,\n        const std::vector<std::string> &pMasqueradingVector) noexcept(false);\n    static bool validateJsonForCreation(const Json::Value &pJson,\n                                        std::string &err);\n    static bool validateMasqueradedJsonForCreation(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validateJsonForUpdate(const Json::Value &pJson,\n                                      std::string &err);\n    static bool validateMasqueradedJsonForUpdate(\n        const Json::Value &,\n        const std::vector<std::string> &pMasqueradingVector,\n        std::string &err);\n    static bool validJsonOfField(size_t index,\n                                 const std::string &fieldName,\n                                 const Json::Value &pJson,\n                                 std::string &err,\n                                 bool isForCreation);\n\n    /**  For column id  */\n    /// Get the value of the column id, returns the default value if the column\n    /// is null\n    const int64_t &getValueOfId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<int64_t> &getId() const noexcept;\n    /// Set the value of the column id\n    void setId(const int64_t &pId) noexcept;\n    void setIdToNull() noexcept;\n\n    /**  For column user_id  */\n    /// Get the value of the column user_id, returns the default value if the\n    /// column is null\n    const std::string &getValueOfUserId() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getUserId() const noexcept;\n    /// Set the value of the column user_id\n    void setUserId(const std::string &pUserId) noexcept;\n    void setUserId(std::string &&pUserId) noexcept;\n    void setUserIdToNull() noexcept;\n\n    /**  For column amount  */\n    /// Get the value of the column amount, returns the default value if the\n    /// column is null\n    const std::string &getValueOfAmount() const noexcept;\n    /// Return a shared_ptr object pointing to the column const value, or an\n    /// empty shared_ptr object if the column is null\n    const std::shared_ptr<std::string> &getAmount() const noexcept;\n    /// Set the value of the column amount\n    void setAmount(const std::string &pAmount) noexcept;\n    void setAmount(std::string &&pAmount) noexcept;\n    void setAmountToNull() noexcept;\n\n    static size_t getColumnNumber() noexcept\n    {\n        return 3;\n    }\n\n    static const std::string &getColumnName(size_t index) noexcept(false);\n\n    Json::Value toJson() const;\n    Json::Value toMasqueradedJson(\n        const std::vector<std::string> &pMasqueradingVector) const;\n    /// Relationship interfaces\n    Users getUser(const drogon::orm::DbClientPtr &clientPtr) const;\n    void getUser(const drogon::orm::DbClientPtr &clientPtr,\n                 const std::function<void(Users)> &rcb,\n                 const drogon::orm::ExceptionCallback &ecb) const;\n\n  private:\n    friend drogon::orm::Mapper<Wallets>;\n    friend drogon::orm::BaseBuilder<Wallets, true, true>;\n    friend drogon::orm::BaseBuilder<Wallets, true, false>;\n    friend drogon::orm::BaseBuilder<Wallets, false, true>;\n    friend drogon::orm::BaseBuilder<Wallets, false, false>;\n#ifdef __cpp_impl_coroutine\n    friend drogon::orm::CoroMapper<Wallets>;\n#endif\n    static const std::vector<std::string> &insertColumns() noexcept;\n    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;\n    const std::vector<std::string> updateColumns() const;\n    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;\n    /// For mysql or sqlite3\n    void updateId(const uint64_t id);\n    std::shared_ptr<int64_t> id_;\n    std::shared_ptr<std::string> userId_;\n    std::shared_ptr<std::string> amount_;\n\n    struct MetaData\n    {\n        const std::string colName_;\n        const std::string colType_;\n        const std::string colDatabaseType_;\n        const ssize_t colLength_;\n        const bool isAutoVal_;\n        const bool isPrimaryKey_;\n        const bool notNull_;\n    };\n\n    static const std::vector<MetaData> metaData_;\n    bool dirtyFlag_[3] = {false};\n\n  public:\n    static const std::string &sqlForFindingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"select * from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    static const std::string &sqlForDeletingByPrimaryKey()\n    {\n        static const std::string sql =\n            \"delete from \" + tableName + \" where id = ?\";\n        return sql;\n    }\n\n    std::string sqlForInserting(bool &needSelection) const\n    {\n        std::string sql = \"insert into \" + tableName + \" (\";\n        size_t parametersCount = 0;\n        needSelection = false;\n        if (dirtyFlag_[1])\n        {\n            sql += \"user_id,\";\n            ++parametersCount;\n        }\n        if (!dirtyFlag_[1])\n        {\n            needSelection = true;\n        }\n        if (dirtyFlag_[2])\n        {\n            sql += \"amount,\";\n            ++parametersCount;\n        }\n        if (!dirtyFlag_[2])\n        {\n            needSelection = true;\n        }\n        if (parametersCount > 0)\n        {\n            sql[sql.length() - 1] = ')';\n            sql += \" values (\";\n        }\n        else\n            sql += \") values (\";\n\n        if (dirtyFlag_[1])\n        {\n            sql.append(\"?,\");\n        }\n        if (dirtyFlag_[2])\n        {\n            sql.append(\"?,\");\n        }\n        if (parametersCount > 0)\n        {\n            sql.resize(sql.length() - 1);\n        }\n        sql.append(1, ')');\n        LOG_TRACE << sql;\n        return sql;\n    }\n};\n}  // namespace sqlite3\n}  // namespace drogon_model\n"
  },
  {
    "path": "orm_lib/tests/sqlite3/model.json",
    "content": "{\n    \"rdbms\": \"sqlite3\",\n    \"filename\": \"drogonTestSqlite\",\n    \"name\": \"\",\n    \"host\": \"127.0.0.1\",\n    \"port\": 5432,\n    \"dbname\": \"test\",\n    \"user\": \"\",\n    \"passwd\": \"\",\n    \"is_fast\": false,\n    \"connection_number\": 1,\n    \"tables\": [\n        \"users\",\n        \"wallets\",\n        \"blog\",\n        \"category\",\n        \"blog_tag\",\n        \"tag\"\n    ],\n    \"relationships\": {\n        \"enabled\": true,\n        \"items\": [\n            {\n                \"type\": \"has one\",\n                \"original_table_name\": \"users\",\n                \"original_table_alias\": \"user\",\n                \"original_key\": \"user_id\",\n                \"target_table_name\": \"wallets\",\n                \"target_table_alias\": \"wallet\",\n                \"target_key\": \"user_id\",\n                \"enable_reverse\": true\n            },\n            {\n                \"type\": \"has many\",\n                \"original_table_name\": \"category\",\n                \"original_table_alias\": \"category\",\n                \"original_key\": \"id\",\n                \"target_table_name\": \"blog\",\n                \"target_table_alias\": \"blogs\",\n                \"target_key\": \"category_id\",\n                \"enable_reverse\": true\n            },\n            {\n                \"type\": \"many to many\",\n                \"original_table_name\": \"blog\",\n                \"original_table_alias\": \"blogs\",\n                \"original_key\": \"id\",\n                \"pivot_table\": {\n                    \"table_name\": \"blog_tag\",\n                    \"original_key\": \"blog_id\",\n                    \"target_key\": \"tag_id\"\n                },\n                \"target_table_name\": \"tag\",\n                \"target_table_alias\": \"tags\",\n                \"target_key\": \"id\",\n                \"enable_reverse\": true\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "package.xml",
    "content": "<?xml-model href=\"http://download.ros.org/schema/package_format3.xsd\" schematypens=\"http://www.w3.org/2001/XMLSchema\"?>\n<package format=\"3\">\n    <name>drogon</name>\n\n    <version>1.8.4</version>\n\n    <description>Drogon: A C++14/17/20 based HTTP web application framework running on Linux/macOS/Unix/Windows.</description>\n\n    <maintainer email=\"antao2002@gmail.com\">An Tao</maintainer>\n\n    <license>MIT</license>\n\n    <url type=\"repository\">https://github.com/drogonframework/drogon</url>\n\n    <author email=\"antao2002@gmail.com\">An Tao</author>\n\n    <buildtool_depend>cmake</buildtool_depend>\n\n    <depend>libjsoncpp-dev</depend>\n    <depend>uuid</depend>\n    <depend>zlib</depend>\n    <depend>openssl</depend>\n    <depend>libssl-dev</depend>\n    <depend>libsqlite3-dev</depend>\n\n  <export>\n    <build_type>cmake</build_type>\n  </export>\n\n</package>\n"
  },
  {
    "path": "test.sh",
    "content": "#!/usr/bin/env bash\n\necho \"First arg:\"\necho $1\n\nos='linux'\nif [ \"X$1\" = \"X-w\" ]; then\n    os='windows'\nfi\necho \"OS:\" $os\n\nsrc_dir=$(pwd)\ntest_root=build/lib/tests\nif [ \"X$os\" = \"Xwindows\" ]; then\n    test_root=$test_root/Debug\nfi\n\nif [ \"X$os\" = \"Xlinux\" ]; then\n    drogon_ctl_exec=$(pwd)/build/drogon_ctl/drogon_ctl\nelse\n    drogon_ctl_exec=$(pwd)/build/drogon_ctl/Debug/drogon_ctl.exe\n    export PATH=$PATH:$src_dir/install/bin\nfi\necho \"drogon_ctl_exec: \" ${drogon_ctl_exec}\n\n#Make integration_test_server run as a daemon\nfunction do_integration_test()\n{\n    pushd $test_root\n    if [ \"X$os\" = \"Xlinux\" ]; then\n        sed -i -e \"s/\\\"run_as_daemon.*$/\\\"run_as_daemon\\\": true\\,/\" config.example.json\n    fi\n    sed -i -e \"s/\\\"relaunch_on_error.*$/\\\"relaunch_on_error\\\": true\\,/\" config.example.json\n    sed -i -e \"s/\\\"threads_num.*$/\\\"threads_num\\\": 0\\,/\" config.example.json\n    sed -i -e \"s/\\\"use_brotli.*$/\\\"use_brotli\\\": true\\,/\" config.example.json\n\n    if [ \"$1\" = \"stream_mode\" ]; then\n        sed -i -e \"s/\\\"enable_request_stream.*$/\\\"enable_request_stream\\\": true\\,/\" config.example.json\n    else\n        sed -i -e \"s/\\\"enable_request_stream.*$/\\\"enable_request_stream\\\": false\\,/\" config.example.json\n    fi\n\n    if [ ! -f \"integration_test_client\" ]; then\n        echo \"Build failed\"\n        exit -1\n    fi\n    if [ ! -f \"integration_test_server\" ]; then\n        echo \"Build failed\"\n        exit -1\n    fi\n\n    killall -9 integration_test_server\n    ./integration_test_server &\n\n    sleep 4\n\n    echo \"Running the integration test $1\"\n    ./integration_test_client -s\n\n    if [ $? -ne 0 ]; then\n        echo \"Integration test failed $1\"\n        exit -1\n    fi\n\n    killall -9 integration_test_server\n    popd\n}\n\n#Test drogon_ctl\nfunction do_drogon_ctl_test()\n{\n    echo \"Testing drogon_ctl\"\n    pushd $test_root\n    rm -rf drogon_test\n\n    ${drogon_ctl_exec} create project drogon_test\n\n    ls -la\n    cd drogon_test/controllers\n\n    ${drogon_ctl_exec} create controller Test::SimpleCtrl\n    ${drogon_ctl_exec} create controller -h Test::HttpCtrl\n    ${drogon_ctl_exec} create controller -w Test::WebsockCtrl\n    ${drogon_ctl_exec} create controller SimpleCtrl\n    ${drogon_ctl_exec} create controller -h HttpCtrl\n    ${drogon_ctl_exec} create controller -w WebsockCtrl\n\n    if [ ! -f \"Test_SimpleCtrl.h\" -o ! -f \"Test_SimpleCtrl.cc\" -o ! -f \"Test_HttpCtrl.h\" -o ! -f \"Test_HttpCtrl.cc\" -o ! -f \"Test_WebsockCtrl.h\" -o ! -f \"Test_WebsockCtrl.cc\" ]; then\n        echo \"Failed to create controllers\"\n        exit -1\n    fi\n\n    if [ ! -f \"SimpleCtrl.h\" -o ! -f \"SimpleCtrl.cc\" -o ! -f \"HttpCtrl.h\" -o ! -f \"HttpCtrl.cc\" -o ! -f \"WebsockCtrl.h\" -o ! -f \"WebsockCtrl.cc\" ]; then\n        echo \"Failed to create controllers\"\n        exit -1\n    fi\n\n    cd ../filters\n\n    ${drogon_ctl_exec} create filter Test::TestFilter\n\n    if [ ! -f \"Test_TestFilter.h\" -o ! -f \"Test_TestFilter.cc\" ]; then\n        echo \"Failed to create filters\"\n        exit -1\n    fi\n\n    cd ../plugins\n\n    ${drogon_ctl_exec} create plugin Test::TestPlugin\n\n    if [ ! -f \"Test_TestPlugin.h\" -o ! -f \"Test_TestPlugin.cc\" ]; then\n        echo \"Failed to create plugins\"\n        exit -1\n    fi\n\n    cd ../views\n\n    echo \"Hello, world!\" >>hello.csp\n\n    cd ../build\n\n    make_flags=''\n    cmake_gen=''\n    parallel=1\n\n    # simulate ninja's parallelism\n    case $(nproc) in\n    1)\n        parallel=$(($(nproc) + 1))\n        ;;\n    2)\n        parallel=$(($(nproc) + 1))\n        ;;\n    *)\n        parallel=$(($(nproc) + 2))\n        ;;\n    esac\n\n    if [ \"X$os\" = \"Xlinux\" ]; then\n        if [ -f /bin/ninja ]; then\n            cmake_gen='-G Ninja'\n        else\n            make_flags=\"$make_flags -j$parallel\"\n        fi\n    else\n        cmake_gen=\"$cmake_gen -DCMAKE_TOOLCHAIN_FILE=$src_dir/conan_toolchain.cmake \\\n                              -DCMAKE_PREFIX_PATH=$src_dir/install \\\n                              -DCMAKE_POLICY_DEFAULT_CMP0091=NEW \\\n                              -DCMAKE_CXX_STANDARD=17\"\n    fi\n    cmake .. $cmake_gen\n\n    if [ $? -ne 0 ]; then\n        echo \"Failed to run CMake for example project\"\n        exit -1\n    fi\n\n    cmake --build . -- $make_flags\n\n    if [ $? -ne 0 ]; then\n        echo \"Error in testing\"\n        exit -1\n    fi\n\n    if [ \"X$os\" = \"Xlinux\" ]; then\n      if [ ! -f \"drogon_test\" ]; then\n          echo \"Failed to build drogon_test\"\n          exit -1\n      fi\n    else\n      if [ ! -f \"Debug\\drogon_test.exe\" ]; then\n          echo \"Failed to build drogon_test\"\n          exit -1\n      fi\n    fi\n\n    cd ../../\n    rm -rf drogon_test\n    popd\n}\n\n#unit testing\nfunction do_unittest()\n{\n    echo \"Unit testing\"\n    pushd $src_dir/build\n\n    ctest . --output-on-failure\n    if [ $? -ne 0 ]; then\n        echo \"Error in unit testing\"\n        exit -1\n    fi\n    popd\n}\n\nfunction do_db_test()\n{\n    pushd $src_dir/build\n    if [ -f \"./orm_lib/tests/db_test\" ]; then\n        echo \"Test database\"\n        ./orm_lib/tests/db_test -s\n        if [ $? -ne 0 ]; then\n            echo \"Error in testing\"\n            exit -1\n        fi\n    fi\n    if [ -f \"./orm_lib/tests/pipeline_test\" ]; then\n        echo \"Test pipeline mode\"\n        ./orm_lib/tests/pipeline_test -s\n        if [ $? -ne 0 ]; then\n            echo \"Error in testing\"\n            exit -1\n        fi\n    fi\n    if [ -f \"./orm_lib/tests/db_listener_test\" ]; then\n        echo \"Test DbListener\"\n        ./orm_lib/tests/db_listener_test -s\n        if [ $? -ne 0 ]; then\n            echo \"Error in testing\"\n            exit -1\n        fi\n    fi\n    if [ -f \"./orm_lib/tests/conn_options_test\" ]; then\n        echo \"Test connection options\"\n        ./orm_lib/tests/conn_options_test -s\n        if [ $? -ne 0 ]; then\n            echo \"Error in testing\"\n            exit -1\n        fi\n    fi\n    if [ -f \"./orm_lib/tests/db_api_test\" ]; then\n        echo \"Test getDbClient() api\"\n        ./orm_lib/tests/db_api_test -s\n        if [ $? -ne 0 ]; then\n            echo \"Error in testing\"\n            exit -1\n        fi\n    fi\n    if [ -f \"./nosql_lib/redis/tests/redis_test\" ]; then\n        echo \"Test redis\"\n        ./nosql_lib/redis/tests/redis_test -s\n        if [ $? -ne 0 ]; then\n            echo \"Error in testing\"\n            exit -1\n        fi\n    fi\n}\n\nif ! drogon_ctl -v > /dev/null 2>&1\nthen\n    echo \"Warning: No drogon_ctl, skip integration test and drogon_ctl test\"\nelse\n    do_integration_test\n    do_integration_test stream_mode\n    do_drogon_ctl_test\nfi\n\nif [ \"X$1\" = \"X-t\" ]; then\n    do_unittest\n    do_db_test\nfi\n\necho \"Everything is ok!\"\nexit 0\n"
  },
  {
    "path": "third_party/mman-win32/README.md",
    "content": "mman-win32\n==========\n\nmman library for Windows. mirror of https://code.google.com/p/mman-win32/\n\nA light implementation of the mmap functions for MinGW.\n\nThe mmap-win32 library implements a wrapper for mmap functions around the memory mapping Windows API.\n\nLicense: MIT License\n"
  },
  {
    "path": "third_party/mman-win32/mman.c",
    "content": "\n#include <windows.h>\n#include <errno.h>\n#include <io.h>\n\n#include \"mman.h\"\n\n#ifndef FILE_MAP_EXECUTE\n#define FILE_MAP_EXECUTE    0x0020\n#endif /* FILE_MAP_EXECUTE */\n\nstatic int __map_mman_error(const DWORD err, const int deferr)\n{\n    if (err == 0)\n        return 0;\n    //TODO: implement\n    return err;\n}\n\nstatic DWORD __map_mmap_prot_page(const int prot)\n{\n    DWORD protect = 0;\n    \n    if (prot == PROT_NONE)\n        return protect;\n        \n    if ((prot & PROT_EXEC) != 0)\n    {\n        protect = ((prot & PROT_WRITE) != 0) ? \n                    PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ;\n    }\n    else\n    {\n        protect = ((prot & PROT_WRITE) != 0) ?\n                    PAGE_READWRITE : PAGE_READONLY;\n    }\n    \n    return protect;\n}\n\nstatic DWORD __map_mmap_prot_file(const int prot)\n{\n    DWORD desiredAccess = 0;\n    \n    if (prot == PROT_NONE)\n        return desiredAccess;\n        \n    if ((prot & PROT_READ) != 0)\n        desiredAccess |= FILE_MAP_READ;\n    if ((prot & PROT_WRITE) != 0)\n        desiredAccess |= FILE_MAP_WRITE;\n    if ((prot & PROT_EXEC) != 0)\n        desiredAccess |= FILE_MAP_EXECUTE;\n    \n    return desiredAccess;\n}\n\nvoid* mmap(void *addr, size_t len, int prot, int flags, int fildes, OffsetType off)\n{\n    HANDLE fm, h;\n    \n    void * map = MAP_FAILED;\n    \n#ifdef _MSC_VER\n#pragma warning(push)\n#pragma warning(disable: 4293)\n#endif\n\n    const DWORD dwFileOffsetLow = (sizeof(OffsetType) <= sizeof(DWORD)) ?\n                    (DWORD)off : (DWORD)(off & 0xFFFFFFFFL);\n    const DWORD dwFileOffsetHigh = (sizeof(OffsetType) <= sizeof(DWORD)) ?\n                    (DWORD)0 : (DWORD)((off >> 32) & 0xFFFFFFFFL);\n    const DWORD protect = __map_mmap_prot_page(prot);\n    const DWORD desiredAccess = __map_mmap_prot_file(prot);\n\n    const OffsetType maxSize = off + (OffsetType)len;\n\n    const DWORD dwMaxSizeLow = (sizeof(OffsetType) <= sizeof(DWORD)) ?\n                    (DWORD)maxSize : (DWORD)(maxSize & 0xFFFFFFFFL);\n    const DWORD dwMaxSizeHigh = (sizeof(OffsetType) <= sizeof(DWORD)) ?\n                    (DWORD)0 : (DWORD)((maxSize >> 32) & 0xFFFFFFFFL);\n\n#ifdef _MSC_VER\n#pragma warning(pop)\n#endif\n\n    errno = 0;\n    \n    if (len == 0 \n        /* Unsupported protection combinations */\n        || prot == PROT_EXEC)\n    {\n        errno = EINVAL;\n        return MAP_FAILED;\n    }\n    \n    h = ((flags & MAP_ANONYMOUS) == 0) ? \n                    (HANDLE)_get_osfhandle(fildes) : INVALID_HANDLE_VALUE;\n\n    if ((flags & MAP_ANONYMOUS) == 0 && h == INVALID_HANDLE_VALUE)\n    {\n        errno = EBADF;\n        return MAP_FAILED;\n    }\n\n    fm = CreateFileMapping(h, NULL, protect, dwMaxSizeHigh, dwMaxSizeLow, NULL);\n\n    if (fm == NULL)\n    {\n        errno = __map_mman_error(GetLastError(), EPERM);\n        return MAP_FAILED;\n    }\n  \n    if ((flags & MAP_FIXED) == 0)\n    {\n        map = MapViewOfFile(fm, desiredAccess, dwFileOffsetHigh, dwFileOffsetLow, len);\n    }\n    else\n    {\n        map = MapViewOfFileEx(fm, desiredAccess, dwFileOffsetHigh, dwFileOffsetLow, len, addr);\n    }\n\n    CloseHandle(fm);\n  \n    if (map == NULL)\n    {\n        errno = __map_mman_error(GetLastError(), EPERM);\n        return MAP_FAILED;\n    }\n\n    return map;\n}\n\nint munmap(void *addr, size_t len)\n{\n    if (UnmapViewOfFile(addr))\n        return 0;\n        \n    errno =  __map_mman_error(GetLastError(), EPERM);\n    \n    return -1;\n}\n\nint _mprotect(void *addr, size_t len, int prot)\n{\n    DWORD newProtect = __map_mmap_prot_page(prot);\n    DWORD oldProtect = 0;\n    \n    if (VirtualProtect(addr, len, newProtect, &oldProtect))\n        return 0;\n    \n    errno =  __map_mman_error(GetLastError(), EPERM);\n    \n    return -1;\n}\n\nint msync(void *addr, size_t len, int flags)\n{\n    if (FlushViewOfFile(addr, len))\n        return 0;\n    \n    errno =  __map_mman_error(GetLastError(), EPERM);\n    \n    return -1;\n}\n\nint mlock(const void *addr, size_t len)\n{\n    if (VirtualLock((LPVOID)addr, len))\n        return 0;\n        \n    errno =  __map_mman_error(GetLastError(), EPERM);\n    \n    return -1;\n}\n\nint munlock(const void *addr, size_t len)\n{\n    if (VirtualUnlock((LPVOID)addr, len))\n        return 0;\n        \n    errno =  __map_mman_error(GetLastError(), EPERM);\n    \n    return -1;\n}\n"
  },
  {
    "path": "third_party/mman-win32/mman.h",
    "content": "/*\n * sys/mman.h\n * mman-win32\n */\n\n#ifndef _SYS_MMAN_H_ // NOLINT(build/header_guard)\n#define _SYS_MMAN_H_\n\n#ifndef _WIN32_WINNT\t\t// Allow use of features specific to Windows XP or later.                   \n#define _WIN32_WINNT 0x0501\t// Change this to the appropriate value to target other versions of Windows.\n#endif\t\t\t\t\t\t\n\n/* All the headers include this file. */\n#ifndef _MSC_VER\n#include <_mingw.h>\n#endif\n\n#if defined(MMAN_LIBRARY_DLL)\n/* Windows shared libraries (DLL) must be declared export when building the lib and import when building the \napplication which links against the library. */\n#if defined(MMAN_LIBRARY)\n#define MMANSHARED_EXPORT __declspec(dllexport)\n#else\n#define MMANSHARED_EXPORT __declspec(dllimport)\n#endif /* MMAN_LIBRARY */\n#else\n/* Static libraries do not require a __declspec attribute.*/\n#define MMANSHARED_EXPORT\n#endif /* MMAN_LIBRARY_DLL */\n\n/* Determine offset type */\n#include <stdint.h>\n#if defined(_WIN64)\ntypedef int64_t OffsetType;\n#else\ntypedef uint32_t OffsetType;\n#endif\n\n#include <sys/types.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define PROT_NONE       0\n#define PROT_READ       1\n#define PROT_WRITE      2\n#define PROT_EXEC       4\n\n#define MAP_FILE        0\n#define MAP_SHARED      1\n#define MAP_PRIVATE     2\n#define MAP_TYPE        0xf\n#define MAP_FIXED       0x10\n#define MAP_ANONYMOUS   0x20\n#define MAP_ANON        MAP_ANONYMOUS\n\n#define MAP_FAILED      ((void *)-1)\n\n/* Flags for msync. */\n#define MS_ASYNC        1\n#define MS_SYNC         2\n#define MS_INVALIDATE   4\n\nMMANSHARED_EXPORT void*   mmap(void *addr, size_t len, int prot, int flags, int fildes, OffsetType off);\nMMANSHARED_EXPORT int     munmap(void *addr, size_t len);\nMMANSHARED_EXPORT int     _mprotect(void *addr, size_t len, int prot);\nMMANSHARED_EXPORT int     msync(void *addr, size_t len, int flags);\nMMANSHARED_EXPORT int     mlock(const void *addr, size_t len);\nMMANSHARED_EXPORT int     munlock(const void *addr, size_t len);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /*  _SYS_MMAN_H_ */\n"
  },
  {
    "path": "third_party/mman-win32/test.c",
    "content": "\n#include \"mman.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n\n#ifndef NULL\n#define NULL    (void*)0\n#endif\n\nconst char* map_file_name = \"map_file.dat\";\n\nint test_anon_map_readwrite()\n{    \n    void* map = mmap(NULL, 1024, PROT_READ | PROT_WRITE,\n \t\t      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n    if (map == MAP_FAILED)\n    {\n        printf(\"mmap (MAP_ANONYMOUS, PROT_READ | PROT_WRITE) returned unexpected error: %d\\n\", errno);\n        return -1;\n    }\n        \n    *((unsigned char*)map) = 1;\n    \n    int result = munmap(map, 1024);\n    \n    if (result != 0)\n        printf(\"munmap (MAP_ANONYMOUS, PROT_READ | PROT_WRITE) returned unexpected error: %d\\n\", errno);\n        \n    return result;\n}\n\nint test_anon_map_readonly()\n{    \n    void* map = mmap(NULL, 1024, PROT_READ,\n \t\t      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n    if (map == MAP_FAILED)\n    {\n        printf(\"mmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\\n\", errno);\n        return -1;\n    }\n        \n    *((unsigned char*)map) = 1;\n    \n    int result = munmap(map, 1024);\n    \n    if (result != 0)\n        printf(\"munmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\\n\", errno);\n        \n    return result;\n}\n\nint test_anon_map_writeonly()\n{    \n    void* map = mmap(NULL, 1024, PROT_WRITE,\n \t\t      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n    if (map == MAP_FAILED)\n    {\n        printf(\"mmap (MAP_ANONYMOUS, PROT_WRITE) returned unexpected error: %d\\n\", errno);\n        return -1;\n    }\n        \n    *((unsigned char*)map) = 1;\n    \n    int result = munmap(map, 1024);\n    \n    if (result != 0)\n        printf(\"munmap (MAP_ANONYMOUS, PROT_WRITE) returned unexpected error: %d\\n\", errno);\n        \n    return result;\n}\n\nint test_anon_map_readonly_nowrite()\n{    \n    void* map = mmap(NULL, 1024, PROT_READ,\n \t\t      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n    if (map == MAP_FAILED)\n    {\n        printf(\"mmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\\n\", errno);\n        return -1;\n    }\n    \n    if (*((unsigned char*)map) != 0)\n        printf(\"test_anon_map_readonly_nowrite (MAP_ANONYMOUS, PROT_READ) returned unexpected value: %d\\n\", \n                (int)*((unsigned char*)map));\n        \n    int result = munmap(map, 1024);\n    \n    if (result != 0)\n        printf(\"munmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\\n\", errno);\n        \n    return result;\n}\n\nint test_file_map_readwrite()\n{\n    mode_t mode = S_IRUSR | S_IWUSR;\n    int o = open(map_file_name, O_TRUNC | O_BINARY | O_RDWR | O_CREAT, mode);\n\n    void* map = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE, o, 0);\n    if (map == MAP_FAILED)\n    {\n        printf(\"mmap returned unexpected error: %d\\n\", errno);\n        return -1;\n    }\n\n    *((unsigned char*)map) = 1;\n    \n    int result = munmap(map, 1024);\n    \n    if (result != 0)\n        printf(\"munmap returned unexpected error: %d\\n\", errno);\n    \n    close(o);\n    \n    /*TODO: get file info and content and compare it with the sources conditions */\n    unlink(map_file_name);\n    \n    return result;\n}\n\nint test_file_map_mlock_munlock()\n{\n    const size_t map_size = 1024;\n    \n    int result = 0;\n    mode_t mode = S_IRUSR | S_IWUSR;\n    int o = open(map_file_name, O_TRUNC | O_BINARY | O_RDWR | O_CREAT, mode);\n    if (o == -1)\n    {\n        printf(\"unable to create file %s: %d\\n\", map_file_name, errno);\n        return -1;\n    }\n\n    void* map = mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, o, 0);\n    if (map == MAP_FAILED)\n    {\n        printf(\"mmap returned unexpected error: %d\\n\", errno);\n        result = -1;\n        goto done_close;\n    }\n     \n    if (mlock(map, map_size) != 0)\n    {\n        printf(\"mlock returned unexpected error: %d\\n\", errno);\n        result = -1;\n        goto done_munmap;        \n    }\n    \n    *((unsigned char*)map) = 1;\n    \n    if (munlock(map, map_size) != 0)\n    {\n        printf(\"munlock returned unexpected error: %d\\n\", errno);\n        result = -1;\n    }\n    \ndone_munmap:\n    result = munmap(map, map_size);\n    \n    if (result != 0)\n        printf(\"munmap returned unexpected error: %d\\n\", errno);\n        \ndone_close:\n    close(o);\n    \n    unlink(map_file_name);\ndone:\n    return result;\n}\n\nint test_file_map_msync()\n{\n    const size_t map_size = 1024;\n    \n    int result = 0;\n    mode_t mode = S_IRUSR | S_IWUSR;\n    int o = open(map_file_name, O_TRUNC | O_BINARY | O_RDWR | O_CREAT, mode);\n    if (o == -1)\n    {\n        printf(\"unable to create file %s: %d\\n\", map_file_name, errno);\n        return -1;\n    }\n\n    void* map = mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, o, 0);\n    if (map == MAP_FAILED)\n    {\n        printf(\"mmap returned unexpected error: %d\\n\", errno);\n        result = -1;\n        goto done_close;\n    }\n     \n    *((unsigned char*)map) = 1;\n\n    if (msync(map, map_size, MS_SYNC) != 0)\n    {\n        printf(\"msync returned unexpected error: %d\\n\", errno);\n        result = -1;\n    }\n    \n    result = munmap(map, map_size);\n    \n    if (result != 0)\n        printf(\"munmap returned unexpected error: %d\\n\", errno);\n        \ndone_close:\n    close(o);\n    \n    unlink(map_file_name);\ndone:\n    return result;\n}\n\n#define EXEC_TEST(name) \\\n    if (name() != 0) { result = -1; printf( #name \": fail\\n\"); } \\\n    else { printf(#name \": pass\\n\"); }\n\nint main()\n{\n    int result = 0;\n    \n    EXEC_TEST(test_anon_map_readwrite);\n    //NOTE: this test must cause an access violation exception\n    //EXEC_TEST(test_anon_map_readonly);\n    EXEC_TEST(test_anon_map_readonly_nowrite);\n    EXEC_TEST(test_anon_map_writeonly);\n    \n    EXEC_TEST(test_file_map_readwrite);\n    EXEC_TEST(test_file_map_mlock_munlock);\n    EXEC_TEST(test_file_map_msync);\n    //TODO: EXEC_TEST(test_file_map_mprotect);\n    \n    return result;\n}\n"
  }
]