Use mintaka http client

This commit is contained in:
Guilherme Werner
2023-12-08 17:01:58 -03:00
parent eb8cc797ca
commit 15e31c4f97
117 changed files with 950 additions and 35853 deletions

12
.cargo/config.toml Normal file
View File

@ -0,0 +1,12 @@
# Shared
[profile.dev]
lto = "off"
[profile.release]
lto = "thin"
# Windows
[target.'cfg(target_os = "windows")']
rustflags = ["-C", "target-feature=+crt-static"]

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "vendor/Mintaka"]
path = vendor/Mintaka
url = https://github.com/Tribufu/Mintaka

17
Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "tribufu-native"
version = "0.0.4"
description = "Tribufu Native"
repository = "https://github.com/Tribufu/TribufuNative"
authors = ["Tribufu <contact@tribufu.com>"]
license = "Apache-2.0"
edition = "2021"
publish = false
[lib]
name = "tribufu_native"
crate-type = ["staticlib"]
path = "src/lib.rs"
[dependencies]
mintaka-native = { path = "./vendor/Mintaka/src/native" }

View File

@ -2,13 +2,10 @@
if ($IsWindows)
{
& "./vendor/premake/windows/premake5.exe" "vs2022"
}
elseif ($IsMacOS)
{
& "./vendor/premake/mac/premake5" "xcode4"
}
elseif ($IsLinux)
{
& "./vendor/premake/linux/premake5" "gmake2"
}

View File

@ -2,9 +2,8 @@
if [ "$(expr substr $(uname -s) 1 5)" = "Linux" ]
then
./vendor/premake/linux/premake5 gmake2
elif [ "$(uname)" = "Darwin" ]
then
./vendor/premake/mac/premake5 xcode4
fi

653
config/cbindgen.toml Normal file
View File

@ -0,0 +1,653 @@
# The language to output bindings in
#
# possible values: "C", "C++", "Cython"
#
# default: "C++"
language = "C"
# Options for wrapping the contents of the header:
# An optional string of text to output at the beginning of the generated file
# default: doesn't emit anything
header = "// Copyright (c) Tribufu. All Rights Reserved."
# An optional string of text to output at the end of the generated file
# default: doesn't emit anything
#trailer = "/* Text to put at the end of the generated file */"
# An optional name to use as an include guard
# default: doesn't emit an include guard
#include_guard = "DEVKIT_H"
# Whether to add a `#pragma once` guard
# default: doesn't emit a `#pragma once`
pragma_once = true
# An optional string of text to output between major sections of the generated
# file as a warning against manual editing
#
# default: doesn't emit anything
#autogen_warning = "// Warning, this file is autogenerated. Don't modify it manually."
# Whether to include a comment with the version of cbindgen used to generate the file
# default: false
include_version = false
# An optional namespace to output around the generated bindings
# default: doesn't emit a namespace
#namespace = "Tribufu"
# An optional list of namespaces to output around the generated bindings
# default: []
namespaces = []
# An optional list of namespaces to declare as using with "using namespace"
# default: []
using_namespaces = []
# A list of sys headers to #include (with angle brackets)
# default: []
sys_includes = ["tribufu/prelude.h"]
# A list of headers to #include (with quotes)
# default: []
includes = []
# Whether cbindgen's default C/C++ standard imports should be suppressed. These
# imports are included by default because our generated headers tend to require
# them (e.g. for uint32_t). Currently, the generated imports are:
#
# * for C: <stdarg.h>, <stdbool.h>, <stdint.h>, <stdlib.h>, <uchar.h>
#
# * for C++: <cstdarg>, <cstdint>, <cstdlib>, <new>, <cassert> (depending on config)
#
# default: false
no_includes = true
# Whether to make a C header C++ compatible.
# These will wrap generated functions into a `extern "C"` block, e.g.
#
# #ifdef __cplusplus
# extern "C" {
# #endif // __cplusplus
#
# // Generated functions.
#
# #ifdef __cplusplus
# } // extern "C"
# #endif // __cplusplus
#
# If the language is not C this option won't have any effect.
#
# default: false
cpp_compat = false
# A list of lines to add verbatim after the includes block
#after_includes = "\n#include \"Base.h\""
# Code Style Options
# The style to use for curly braces
#
# possible values: "SameLine", "NextLine"
#
# default: "SameLine"
braces = "NextLine"
# The desired length of a line to use when formatting lines
# default: 100
line_length = 100
# The amount of spaces to indent by
# default: 2
tab_width = 4
# Include doc comments from Rust as documentation
documentation = true
# How the generated documentation should be commented.
#
# possible values:
# * "c": /* like this */
# * "c99": // like this
# * "c++": /// like this
# * "doxy": like C, but with leading *'s on each line
# * "auto": "c++" if that's the language, "doxy" otherwise
#
# default: "auto"
documentation_style = "doxy"
# Codegen Options
# When generating a C header, the kind of declaration style to use for structs
# or enums.
#
# possible values:
# * "type": typedef struct { ... } MyType;
# * "tag": struct MyType { ... };
# * "both": typedef struct MyType { ... } MyType;
#
# default: "both"
style = "tag"
# A list of substitutions for converting cfg's to ifdefs. cfgs which aren't
# defined here will just be discarded.
#
# e.g.
# `#[cfg(target = "freebsd")] ...`
# becomes
# `#if defined(DEFINE_FREEBSD) ... #endif`
[defines]
"debug_assertions" = "TRIBUFU_DEVEL"
"not(debug_assertions)" = "TRIBUFU_RETAIL"
"panic = abort" = "TRIBUFU_PANIC_ABORT"
"panic = unwind" = "TRIBUFU_PANIC_UNWIND"
"proc_macro" = "TRIBUFU_PROC_MACRO"
"target_arch = aarch64" = "TRIBUFU_AARCH64"
"target_arch = arm" = "TRIBUFU_ARM"
"target_arch = armV7" = "TRIBUFU_ARMV7"
"target_arch = armv4t" = "TRIBUFU_ARMV4T"
"target_arch = armv6" = "TRIBUFU_ARMV6"
"target_arch = armv7a" = "TRIBUFU_ARMV7A"
"target_arch = armv7s" = "TRIBUFU_ARMV7S"
"target_arch = avr" = "TRIBUFU_AVR"
"target_arch = bpfeb" = "TRIBUFU_BPFEB"
"target_arch = bpfel" = "TRIBUFU_BPFEL"
"target_arch = hexagon" = "TRIBUFU_HEXAGON"
"target_arch = i386" = "TRIBUFU_I386"
"target_arch = i586" = "TRIBUFU_I586"
"target_arch = i686" = "TRIBUFU_I686"
"target_arch = m68k" = "TRIBUFU_M68K"
"target_arch = mips" = "TRIBUFU_MIPS"
"target_arch = mipsel" = "TRIBUFU_MIPSEL"
"target_arch = msp430" = "TRIBUFU_MSP430"
"target_arch = powerpc" = "TRIBUFU_POWERPC"
"target_arch = powerpc64" = "TRIBUFU_POWERPC64"
"target_arch = powerpc64l" = "TRIBUFU_POWERPC64L"
"target_arch = riscv32gc" = "TRIBUFU_RISCV32GC"
"target_arch = s390x" = "TRIBUFU_S390X"
"target_arch = sparc" = "TRIBUFU_SPARC"
"target_arch = sparc64" = "TRIBUFU_SPARC64"
"target_arch = thumbv4t" = "TRIBUFU_THUMBV4T"
"target_arch = thumbv7a" = "TRIBUFU_THUMBV7A"
"target_arch = thumbv7neon" = "TRIBUFU_THUMBV7NEON"
"target_arch = wasm32" = "TRIBUFU_WASM32"
"target_arch = wasm64" = "TRIBUFU_WASM64"
"target_arch = x86" = "TRIBUFU_X86"
"target_arch = x86_64" = "TRIBUFU_X86_64"
"target_endian = big" = "TRIBUFU_BIG_ENDIAN"
"target_endian = little" = "TRIBUFU_LITTLE_ENDIAN"
"target_env = gnu" = "TRIBUFU_GNU"
"target_env = msvc" = "TRIBUFU_MSVC"
"target_env = musl" = "TRIBUFU_MUSL"
"target_env = sgx" = "TRIBUFU_SGX"
"target_family = unix" = "TRIBUFU_UNIX"
"target_family = wasm" = "TRIBUFU_WASM"
"target_family = windows" = "TRIBUFU_MICROSOFT"
"target_feature = avx" = "TRIBUFU_AVX"
"target_feature = avx2" = "TRIBUFU_AVX2"
"target_feature = crt-static" = "TRIBUFU_CRTSTATIC"
"target_feature = rdrand" = "TRIBUFU_RDRAND"
"target_feature = sse" = "TRIBUFU_SSE"
"target_feature = sse2" = "TRIBUFU_SSE2"
"target_feature = sse4.1" = "TRIBUFU_SSE41"
"target_has_atomic = 128" = "TRIBUFU_ATOMIC_128"
"target_has_atomic = 16" = "TRIBUFU_ATOMIC_16"
"target_has_atomic = 32" = "TRIBUFU_ATOMIC_32"
"target_has_atomic = 64" = "TRIBUFU_ATOMIC_64"
"target_has_atomic = 8" = "TRIBUFU_ATOMIC_8"
"target_has_atomic = ptr" = "TRIBUFU_ATOMIC_PTR"
"target_os = android" = "TRIBUFU_ANDROID"
"target_os = dragonfly" = "TRIBUFU_DRAGONFLY"
"target_os = freebsd" = "TRIBUFU_FREEBSD"
"target_os = ios" = "TRIBUFU_IOS"
"target_os = linux" = "TRIBUFU_LINUX"
"target_os = macos" = "TRIBUFU_MACOS"
"target_os = netbsd" = "TRIBUFU_NETBSD"
"target_os = openbsd" = "TRIBUFU_OPENBSD"
"target_os = windows" = "TRIBUFU_WINDOWS"
"target_pointer_width = 16" = "TRIBUFU_POINTER_16"
"target_pointer_width = 32" = "TRIBUFU_POINTER_32"
"target_pointer_width = 64" = "TRIBUFU_POINTER_64"
"target_vendor = apple" = "TRIBUFU_APPLE"
"target_vendor = fortanix" = "TRIBUFU_FORTANIX"
"target_vendor = pc" = "TRIBUFU_MICROSOFT"
"target_vendor = unknown" = "TRIBUFU_UNKNOWN"
[export]
# A list of additional items to always include in the generated bindings if they're
# found but otherwise don't appear to be used by the public API.
#
# default: []
include = []
# A list of items to not include in the generated bindings
# default: []
exclude = []
# A prefix to add before the name of every item
# default: no prefix is added
#prefix = "ALN_"
# Types of items that we'll generate. If empty, then all types of item are emitted.
#
# possible items: (TODO: explain these in detail)
# * "constants":
# * "globals":
# * "enums":
# * "structs":
# * "unions":
# * "typedefs":
# * "opaque":
# * "functions":
#
# default: []
item_types = ["enums", "structs", "typedefs", "functions"]
# Whether applying rules in export.rename prevents export.prefix from applying.
#
# e.g. given this toml:
#
# [export]
# prefix = "capi_"
# [export.rename]
# "MyType" = "my_cool_type"
#
# You get the following results:
#
# renaming_overrides_prefixing = true:
# "MyType" => "my_cool_type"
#
# renaming_overrides_prefixing = false:
# "MyType => capi_my_cool_type"
#
# default: false
renaming_overrides_prefixing = false
# Table of name conversions to apply to item names (lhs becomes rhs)
[export.rename]
"CPluginManager" = "PluginManager"
"my_function" = "BetterFunctionName"
# Table of things to prepend to the body of any struct, union, or enum that has the
# given name. This can be used to add things like methods which don't change ABI,
# mark fields private, etc
[export.pre_body]
"MyType" = """
MyType() = delete;
private:
"""
# Table of things to append to the body of any struct, union, or enum that has the
# given name. This can be used to add things like methods which don't change ABI.
[export.body]
"MyType" = """
void cppMethod() const;
"""
# Configuration for name mangling
[export.mangle]
# Whether the types should be renamed during mangling, for example
# c_char -> CChar, etc.
rename_types = "None"
# Whether the underscores from the mangled name should be omitted.
remove_underscores = false
[layout]
# A string that should come before the name of any type which has been marked
# as `#[repr(packed)]`. For instance, "__attribute__((packed))" would be a
# reasonable value if targeting gcc/clang. A more portable solution would
# involve emitting the name of a macro which you define in a platform-specific
# way. e.g. "PACKED"
#
# default: `#[repr(packed)]` types will be treated as opaque, since it would
# be unsafe for C callers to use a incorrectly laid-out union.
packed = "PACKED"
# A string that should come before the name of any type which has been marked
# as `#[repr(align(n))]`. This string must be a function-like macro which takes
# a single argument (the requested alignment, `n`). For instance, a macro
# `#define`d as `ALIGNED(n)` in `header` which translates to
# `__attribute__((aligned(n)))` would be a reasonable value if targeting
# gcc/clang.
#
# default: `#[repr(align(n))]` types will be treated as opaque, since it
# could be unsafe for C callers to use a incorrectly-aligned union.
aligned_n = "ALIGNED"
[fn]
# An optional prefix to put before every function declaration
# default: no prefix added
prefix = "NATIVE_API"
# An optional postfix to put after any function declaration
# default: no postix added
#postfix = "WR_END_FUNC"
# How to format function arguments
#
# possible values:
# * "horizontal": place all arguments on the same line
# * "vertical": place each argument on its own line
# * "auto": only use vertical if horizontal would exceed line_length
#
# default: "auto"
args = "horizontal"
# An optional string that should prefix function declarations which have been
# marked as `#[must_use]`. For instance, "__attribute__((warn_unused_result))"
# would be a reasonable value if targeting gcc/clang. A more portable solution
# would involve emitting the name of a macro which you define in a
# platform-specific way. e.g. "MUST_USE_FUNC"
# default: nothing is emitted for must_use functions
must_use = "MUST_USE_FUNC"
# An optional string that will be used in the attribute position for functions
# that don't return (that return `!` in Rust).
#
# For instance, `__attribute__((noreturn))` would be a reasonable value if
# targeting gcc/clang.
no_return = "NO_RETURN"
# An optional string that, if present, will be used to generate Swift function
# and method signatures for generated functions, for example "CF_SWIFT_NAME".
# If no such macro is available in your toolchain, you can define one using the
# `header` option in cbindgen.toml
# default: no swift_name function attributes are generated
#swift_name_macro = "CF_SWIFT_NAME"
# A rule to use to rename function argument names. The renaming assumes the input
# is the Rust standard snake_case, however it accepts all the different rename_args
# inputs. This means many options here are no-ops or redundant.
#
# possible values (that actually do something):
# * "CamelCase": my_arg => myArg
# * "PascalCase": my_arg => MyArg
# * "GeckoCase": my_arg => aMyArg
# * "ScreamingSnakeCase": my_arg => MY_ARG
# * "None": apply no renaming
#
# technically possible values (that shouldn't have a purpose here):
# * "SnakeCase": apply no renaming
# * "LowerCase": apply no renaming (actually applies to_lowercase, is this bug?)
# * "UpperCase": same as ScreamingSnakeCase in this context
# * "QualifiedScreamingSnakeCase" => same as ScreamingSnakeCase in this context
#
# default: "None"
rename_args = "None"
# This rule specifies the order in which functions will be sorted.
#
# "Name": sort by the name of the function
# "None": keep order in which the functions have been parsed
#
# default: "None"
sort_by = "Name"
# If this option is true `usize` and `isize` will be converted into `size_t` and `ptrdiff_t`
# instead of `uintptr_t` and `intptr_t` respectively.
#usize_is_size_t = true
[struct]
# A rule to use to rename struct field names. The renaming assumes the input is
# the Rust standard snake_case, however it acccepts all the different rename_args
# inputs. This means many options here are no-ops or redundant.
#
# possible values (that actually do something):
# * "CamelCase": my_arg => myArg
# * "PascalCase": my_arg => MyArg
# * "GeckoCase": my_arg => mMyArg
# * "ScreamingSnakeCase": my_arg => MY_ARG
# * "None": apply no renaming
#
# technically possible values (that shouldn't have a purpose here):
# * "SnakeCase": apply no renaming
# * "LowerCase": apply no renaming (actually applies to_lowercase, is this bug?)
# * "UpperCase": same as ScreamingSnakeCase in this context
# * "QualifiedScreamingSnakeCase" => same as ScreamingSnakeCase in this context
#
# default: "None"
rename_fields = "None"
# An optional string that should come before the name of any struct which has been
# marked as `#[must_use]`. For instance, "__attribute__((warn_unused))"
# would be a reasonable value if targeting gcc/clang. A more portable solution
# would involve emitting the name of a macro which you define in a
# platform-specific way. e.g. "MUST_USE_STRUCT"
#
# default: nothing is emitted for must_use structs
must_use = "MUST_USE_STRUCT"
# Whether a Rust type with associated consts should emit those consts inside the
# type's body. Otherwise they will be emitted trailing and with the type's name
# prefixed. This does nothing if the target is C, or if
# [const]allow_static_const = false
#
# default: false
# associated_constants_in_body: false
# Whether to derive a simple constructor that takes a value for every field.
# default: false
derive_constructor = true
# Whether to derive an operator== for all structs
# default: false
derive_eq = false
# Whether to derive an operator!= for all structs
# default: false
derive_neq = false
# Whether to derive an operator< for all structs
# default: false
derive_lt = false
# Whether to derive an operator<= for all structs
# default: false
derive_lte = false
# Whether to derive an operator> for all structs
# default: false
derive_gt = false
# Whether to derive an operator>= for all structs
# default: false
derive_gte = false
[enum]
# A rule to use to rename enum variants, and the names of any fields those
# variants have. This should probably be split up into two separate options, but
# for now, they're the same! See the documentation for `[struct]rename_fields`
# for how this applies to fields. Renaming of the variant assumes that the input
# is the Rust standard PascalCase. In the case of QualifiedScreamingSnakeCase,
# it also assumed that the enum's name is PascalCase.
#
# possible values (that actually do something):
# * "CamelCase": MyVariant => myVariant
# * "SnakeCase": MyVariant => my_variant
# * "ScreamingSnakeCase": MyVariant => MY_VARIANT
# * "QualifiedScreamingSnakeCase": MyVariant => ENUM_NAME_MY_VARIANT
# * "LowerCase": MyVariant => myvariant
# * "UpperCase": MyVariant => MYVARIANT
# * "None": apply no renaming
#
# technically possible values (that shouldn't have a purpose for the variants):
# * "PascalCase": apply no renaming
# * "GeckoCase": apply no renaming
#
# default: "None"
rename_variants = "None"
# Whether an extra "sentinel" enum variant should be added to all generated enums.
# Firefox uses this for their IPC serialization library.
#
# WARNING: if the sentinel is ever passed into Rust, behaviour will be Undefined.
# Rust does not know about this value, and will assume it cannot happen.
#
# default: false
add_sentinel = false
# Whether enum variant names should be prefixed with the name of the enum.
# default: false
prefix_with_name = false
# Whether to emit enums using "enum class" when targeting C++.
# default: true
enum_class = false
# Whether to generate static `::MyVariant(..)` constructors and `bool IsMyVariant()`
# methods for enums with fields.
#
# default: false
derive_helper_methods = false
# Whether to generate `const MyVariant& AsMyVariant() const` methods for enums with fields.
# default: false
derive_const_casts = false
# Whether to generate `MyVariant& AsMyVariant()` methods for enums with fields
# default: false
derive_mut_casts = false
# The name of the macro/function to use for asserting `IsMyVariant()` in the body of
# derived `AsMyVariant()` cast methods.
#
# default: "assert" (but also causes `<cassert>` to be included by default)
#cast_assert_name = "MOZ_RELEASE_ASSERT"
# An optional string that should come before the name of any enum which has been
# marked as `#[must_use]`. For instance, "__attribute__((warn_unused))"
# would be a reasonable value if targeting gcc/clang. A more portable solution
# would involve emitting the name of a macro which you define in a
# platform-specific way. e.g. "MUST_USE_ENUM"
#
# Note that this refers to the *output* type. That means this will not apply to an enum
# with fields, as it will be emitted as a struct. `[struct]must_use` will apply there.
#
# default: nothing is emitted for must_use enums
must_use = "MUST_USE_ENUM"
# Whether enums with fields should generate destructors. This exists so that generic
# enums can be properly instantiated with payloads that are C++ types with
# destructors. This isn't necessary for structs because C++ has rules to
# automatically derive the correct constructors and destructors for those types.
#
# Care should be taken with this option, as Rust and C++ cannot
# properly interoperate with eachother's notions of destructors. Also, this may
# change the ABI for the type. Either your destructor-full enums must live
# exclusively within C++, or they must only be passed by-reference between
# C++ and Rust.
#
# default: false
derive_tagged_enum_destructor = false
# Whether enums with fields should generate copy-constructor. See the discussion on
# derive_tagged_enum_destructor for why this is both useful and very dangerous.
#
# default: false
derive_tagged_enum_copy_constructor = false
# Whether enums with fields should generate copy-assignment operators.
#
# This depends on also deriving copy-constructors, and it is highly encouraged
# for this to be set to true.
#
# default: false
derive_tagged_enum_copy_assignment = false
# Whether enums with fields should generate an empty, private destructor.
# This allows the auto-generated constructor functions to compile, if there are
# non-trivially constructible members. This falls in the same family of
# dangerousness as `derive_tagged_enum_copy_constructor` and co.
#
# default: false
private_default_tagged_enum_constructor = false
[const]
# Whether a generated constant can be a static const in C++ mode. I have no
# idea why you would turn this off.
#
# default: true
allow_static_const = true
# Whether a generated constant can be constexpr in C++ mode.
#
# default: false
allow_constexpr = false
# This rule specifies the order in which constants will be sorted.
#
# "Name": sort by the name of the constant
# "None": keep order in which the constants have been parsed
#
# default: "None"
sort_by = "Name"
[macro_expansion]
# Whether bindings should be generated for instances of the bitflags! macro.
# default: false
bitflags = true
# Options for how your Rust library should be parsed
[parse]
# Whether to parse dependent crates and include their types in the output
# default: false
parse_deps = true
# A white list of crate names that are allowed to be parsed. If this is defined,
# only crates found in this list will ever be parsed.
#
# default: there is no whitelist (NOTE: this is the opposite of [])
include = []
# A black list of crate names that are not allowed to be parsed.
# default: []
exclude = []
# Whether to use a new temporary target directory when running `rustc --pretty=expanded`.
# This may be required for some build processes.
#
# default: false
clean = false
# Which crates other than the top-level binding crate we should generate
# bindings for.
#
# default: []
extra_bindings = []
[parse.expand]
# A list of crate names that should be run through `cargo expand` before
# parsing to expand any macros. Note that if a crate is named here, it
# will always be parsed, even if the blacklist/whitelist says it shouldn't be.
#
# default: []
crates = []
# If enabled, use the `--all-features` option when expanding. Ignored when
# `features` is set. For backwards-compatibility, this is forced on if
# `expand = ["euclid"]` shorthand is used.
#
# default: false
all_features = true
# When `all_features` is disabled and this is also disabled, use the
# `--no-default-features` option when expanding.
#
# default: true
default_features = true
# A list of feature names that should be used when running `cargo expand`. This
# combines with `default_features` like in your `Cargo.toml`. Note that the features
# listed here are features for the current crate being built, *not* the crates
# being expanded. The crate's `Cargo.toml` must take care of enabling the
# appropriate features in its dependencies
#
# default: []
features = []
[ptr]
# An optional string to decorate all pointers that are
# required to be non null. Nullability is inferred from the Rust type: `&T`,
# `&mut T` and `NonNull<T>` all require a valid pointer value.
#non_null_attribute = "_Nonnull"

View File

@ -1,7 +0,0 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#include <tribufu/defines.h>
#include <tribufu/platform/platform.h>
#include <tribufu/std.h>

View File

@ -2,9 +2,7 @@
#pragma once
#include <tribufu/base.h>
#include <tribufu/json.h>
#include <tribufu/http.h>
#include <tribufu/prelude.h>
const char *VERSION = "0.0.4";
@ -15,7 +13,7 @@ namespace tribufu
private:
uint64_t id;
std::string secret;
hv::HttpClient http;
HttpClient http;
public:
TribufuClient(uint64_t id, const std::string &secret);

View File

@ -1,61 +0,0 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#ifndef EXTERN_C
#ifdef __cplusplus
#define EXTERN_C extern "C"
#define TRIBUFU_CPP
#else
#define EXTERN_C
#endif
#endif
// Declare Enum
#ifdef __cplusplus
#define TRIBUFU_ENUM_START(name) enum class name {
#define TRIBUFU_ENUM_END(name) }
#else
#define TRIBUFU_ENUM_START(name) typedef enum name {
#define TRIBUFU_ENUM_END } name
#endif
#define TRIBUFU_DECLARE_ENUM(name, ...) TRIBUFU_ENUM_START(name) __VA_ARGS__ TRIBUFU_ENUM_END
// Declare Struct
#define TRIBUFU_PASTE(...) __VA_ARGS__
#define TRIBUFU_DECLARE_STRUCT(name, body) \
EXTERN_C typedef struct name \
{ \
TRIBUFU_PASTE body \
} name
// Experimental
#define TRIBUFU_CLASS(...)
#define TRIBUFU_STRUCT(...)
#define TRIBUFU_ENUM(...)
#define TRIBUFU_FUNCTION(...)
#define TRIBUFU_PROPERTY(...)
// Import/Export API
#define NATIVE_API EXTERN_C
#ifdef TRIBUFU_LIBRARY
#define TRIBUFU_API DLLEXPORT
#else
#define TRIBUFU_API DLLIMPORT
#endif
// Macros Utils
#define TRIBUFU_EXPAND_MACRO(x) x
#define TRIBUFU_STRINGIFY_MACRO(x) #x

View File

@ -1,7 +0,0 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#include <nlohmann/json.hpp>
using json = nlohmann::json;

18
include/tribufu/macros.h Normal file
View File

@ -0,0 +1,18 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#ifdef __cplusplus
#define TRIBUFU_CPP
#endif
#ifdef TRIBUFU_LIBRARY
#define TRIBUFU_API DLLEXPORT
#else
#define TRIBUFU_API DLLIMPORT
#endif
// Macros Utils
#define TRIBUFU_EXPAND_MACRO(x) x
#define TRIBUFU_STRINGIFY_MACRO(x) #x

View File

@ -2,4 +2,4 @@
#pragma once
#include <hv/requests.h>
#include <tribufu/prelude.h>

View File

@ -2,7 +2,7 @@
#pragma once
#include <tribufu/base.h>
#include <tribufu/prelude.h>
namespace tribufu
{

View File

@ -1,13 +0,0 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#include <tribufu/platform/unix.h>
#ifndef TRIBUFU_ANDROID
#define TRIBUFU_ANDROID
#endif
#ifndef TRIBUFU_MOBILE
#define TRIBUFU_MOBILE
#endif

View File

@ -1,10 +0,0 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#ifndef TRIBUFU_APPLE
#define TRIBUFU_APPLE
#endif
#define DLLEXPORT __attribute__((visibility("default")))
#define DLLIMPORT __attribute__((visibility("default")))

View File

@ -1,13 +0,0 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#include <tribufu/platform/unix.h>
#ifndef TRIBUFU_FREEBSD
#define TRIBUFU_FREEBSD
#endif
#ifndef TRIBUFU_DESKTOP
#define TRIBUFU_DESKTOP
#endif

View File

@ -1,13 +0,0 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#include <tribufu/platform/apple.h>
#ifndef TRIBUFU_IOS
#define TRIBUFU_IOS
#endif
#ifndef TRIBUFU_MOBILE
#define TRIBUFU_MOBILE
#endif

View File

@ -1,13 +0,0 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#include <tribufu/platform/unix.h>
#ifndef TRIBUFU_LINUX
#define TRIBUFU_LINUX
#endif
#ifndef TRIBUFU_DESKTOP
#define TRIBUFU_DESKTOP
#endif

View File

@ -1,13 +0,0 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#include <tribufu/platform/apple.h>
#ifndef TRIBUFU_MAC
#define TRIBUFU_MAC
#endif
#ifndef TRIBUFU_DESKTOP
#define TRIBUFU_DESKTOP
#endif

View File

@ -1,14 +0,0 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#ifndef TRIBUFU_MSVC
#define TRIBUFU_MSVC
#endif
#ifndef TRIBUFU_MICROSOFT
#define TRIBUFU_MICROSOFT
#endif
#define DLLEXPORT __declspec(dllexport)
#define DLLIMPORT __declspec(dllimport)

View File

@ -1,30 +0,0 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#ifdef _WIN32
#include <tribufu/platform/windows.h>
#endif
#ifdef __MACH__
#include <tribufu/platform/mac.h>
#endif
#ifdef __linux__
#include <tribufu/platform/linux.h>
#endif
#ifdef __FreeBSD__
#include <tribufu/platform/freebsd.h>
#endif
#ifdef __ANDROID__
#include <tribufu/platform/android.h>
#endif
#ifdef __APPLE__
// #include <TargetConditionals.h>
#ifdef TARGET_OS_IPHONE
#include <tribufu/platform/ios.h>
#endif
#endif

View File

@ -1,10 +0,0 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#ifndef TRIBUFU_UNIX
#define TRIBUFU_UNIX
#endif
#define DLLEXPORT __attribute__((visibility("default")))
#define DLLIMPORT __attribute__((visibility("default")))

View File

@ -1,13 +0,0 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#include <tribufu/platform/msvc.h>
#ifndef TRIBUFU_WINDOWS
#define TRIBUFU_WINDOWS
#endif
#ifndef TRIBUFU_DESKTOP
#define TRIBUFU_DESKTOP
#endif

13
include/tribufu/prelude.h Normal file
View File

@ -0,0 +1,13 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#include <mintaka/framework.h>
#include <tribufu/macros.h>
#include <tribufu/native.h>
#ifdef TRIBUFU_CPP
using namespace mintaka;
#endif

View File

@ -0,0 +1,27 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#include <tribufu/prelude.h>
namespace tribufu
{
class TRIBUFU_API Server
{
private:
uint64_t id;
std::string name;
std::string description;
std::string address;
uint16_t game_port;
uint16_t query_port;
uint64_t package_id;
public:
Server();
Server(json data);
~Server();
json to_json();
};
}

View File

@ -1,26 +0,0 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef TRIBUFU_CPP
#include <algorithm>
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <memory>
#include <new>
#include <ostream>
#include <sstream>
#include <string>
#include <utility>
#endif

View File

@ -0,0 +1,46 @@
// Copyright (c) Tribufu. All Rights Reserved.
#pragma once
#include <tribufu/prelude.h>
namespace tribufu
{
enum class UserType
{
User,
Bot,
Org,
};
class TRIBUFU_API Profile
{
private:
uint64_t id;
std::string uuid;
std::string name;
std::string display_name;
UserType type;
uint64_t public_flags;
bool verified;
uint32_t level;
double experience;
bool public_birthday;
double points;
std::string location;
std::string photo_url;
std::string banner_url;
std::string last_online;
std::string biography;
uint32_t view_count;
std::string created;
std::string updated;
public:
Profile();
Profile(json data);
~Profile();
json to_json();
};
}

View File

@ -1,8 +1,7 @@
--- @diagnostic disable: undefined-global
workspace "tribufu_cpp"
workspace "tribufu"
location "."
configurations { "debug", "release" }
if _ACTION == "vs2022" then

3
scripts/cbindgen.ps1 Normal file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env sh
cbindgen --config ./config/cbindgen.toml --crate tribufu-native --output ./include/tribufu/native.h

View File

@ -1,11 +1,11 @@
#!/usr/bin/env pwsh
# Windows (x86_64)
echo "Building for windows-x86_64"
cargo build --package tribufu-native --target x86_64-pc-windows-msvc
New-Item "bin/windows/x86_64" -ItemType Directory -Force
Remove-Item -Path "bin/windows/x86_64/*" -Force
Copy-Item -Path "target/x86_64-pc-windows-msvc/debug/deps/mintaka_native.dll" -Destination "bin/windows/x86_64" -Force
Copy-Item -Path "target/x86_64-pc-windows-msvc/debug/deps/mintaka_native.dll.lib" -Destination "bin/windows/x86_64" -Force
Copy-Item -Path "target/x86_64-pc-windows-msvc/debug/deps/mintaka_native.lib" -Destination "bin/windows/x86_64" -Force
Copy-Item -Path "target/x86_64-pc-windows-msvc/debug/tribufu_native.lib" -Destination "bin/windows/x86_64" -Force
msbuild /p:Configuration="debug" /p:Platform="windows-x86_64"
# Windows (i686)
#msbuild /p:Configuration="debug" /p:Platform="windows-i686"
# Windows (aarch64)
#msbuild /p:Configuration="debug" /p:Platform="windows-aarch64"

3
scripts/premake.bat Normal file
View File

@ -0,0 +1,3 @@
@echo off
call .\vendor\premake\windows\premake5.exe vs2022

10
scripts/premake.sh Normal file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env sh
if [ "$(expr substr $(uname -s) 1 5)" = "Linux" ]
then
./vendor/premake/linux/premake5 gmake2
elif [ "$(uname)" = "Darwin" ]
then
./vendor/premake/mac/premake5 xcode4
fi

View File

@ -8,7 +8,7 @@ namespace tribufu
{
this->id = id;
this->secret = secret;
this->http = hv::HttpClient();
this->http = HttpClient();
}
TribufuClient::~TribufuClient()
@ -19,24 +19,18 @@ namespace tribufu
{
try
{
const char *base_url = "http://localhost:5000";
std::string url = "https://api.tribufu.com/v1/servers";
FHttpResponse response = this->http.get(url);
std::cout << "status_code: " << response.status_code << std::endl;
HttpRequest req;
req.SetMethod("POST");
req.SetUrl(base_url);
HttpResponse resp;
int ret = this->http.send(&req, &resp);
if (ret != 0)
if (response.body != nullptr)
{
std::cout << "request_failed" << std::endl;
}
else
{
std::cout << "request_success" << std::endl;
json response_body = json::parse(response.body);
std::string json_str = response_body.dump(4);
std::cout << json_str << std::endl;
}
mintaka_http_free_response(response);
}
catch (std::exception &e)
{

1
src/lib.rs Normal file
View File

@ -0,0 +1 @@
// Copyright (c) Tribufu. All Rights Reserved.

View File

@ -78,7 +78,8 @@ project "tribufu_cpp"
links
{
"hv.lib",
"tribufu_native.lib",
"mintaka_native.dll.lib",
}
prelinkcommands
@ -87,7 +88,7 @@ project "tribufu_cpp"
postbuildcommands
{
"{COPYFILE} ../vendor/libhv/lib/%{cfg.platform:gsub('-', '/')}/hv.dll %{cfg.targetdir}/hv.dll",
"{COPYFILE} ../target/%{cfg.buildcfg}/deps/mintaka_native.dll %{cfg.targetdir}/mintaka_native.dll",
}
filter { "platforms:mac-*" }
@ -108,6 +109,8 @@ project "tribufu_cpp"
links
{
"tribufu_native",
"mintaka_native",
}
prelinkcommands
@ -135,6 +138,8 @@ project "tribufu_cpp"
links
{
"tribufu_native",
"mintaka_native",
}
prelinkcommands
@ -162,6 +167,8 @@ project "tribufu_cpp"
links
{
"tribufu_native",
"mintaka_native",
}
prelinkcommands
@ -189,6 +196,16 @@ project "tribufu_cpp"
}
links
{
"tribufu_native",
"mintaka_native",
}
prelinkcommands
{
}
postbuildcommands
{
}

38
src/servers/server.cpp Normal file
View File

@ -0,0 +1,38 @@
// Copyright (c) Tribufu. All Rights Reserved.
#include <tribufu/servers/server.h>
namespace tribufu
{
Server::Server()
{
}
Server::Server(json data)
{
this->id = data["id"];
this->name = data["name"];
this->description = data["description"];
this->address = data["address"];
this->game_port = data["game_port"];
this->query_port = data["query_port"];
this->package_id = data["package_id"];
}
Server::~Server()
{
}
json Server::to_json()
{
json data;
data["id"] = this->id;
data["name"] = this->name;
data["description"] = this->description;
data["address"] = this->address;
data["game_port"] = this->game_port;
data["query_port"] = this->query_port;
data["package_id"] = this->package_id;
return data;
}
}

62
src/users/profile.cpp Normal file
View File

@ -0,0 +1,62 @@
// Copyright (c) Tribufu. All Rights Reserved.
#include <tribufu/users/profile.h>
namespace tribufu
{
Profile::Profile()
{
}
Profile::Profile(json data)
{
this->id = data["id"];
this->uuid = data["uuid"];
this->name = data["name"];
this->display_name = data["display_name"];
this->type = data["type"];
this->public_flags = data["public_flags"];
this->verified = data["verified"];
this->level = data["level"];
this->experience = data["experience"];
this->public_birthday = data["public_birthday"];
this->points = data["points"];
this->location = data["location"];
this->photo_url = data["photo_url"];
this->banner_url = data["banner_url"];
this->last_online = data["last_online"];
this->biography = data["biography"];
this->view_count = data["view_count"];
this->created = data["created"];
this->updated = data["updated"];
}
Profile::~Profile()
{
}
json Profile::to_json()
{
json data;
data["id"] = this->id;
data["uuid"] = this->uuid;
data["name"] = this->name;
data["display_name"] = this->display_name;
data["type"] = this->type;
data["public_flags"] = this->public_flags;
data["verified"] = this->verified;
data["level"] = this->level;
data["experience"] = this->experience;
data["public_birthday"] = this->public_birthday;
data["points"] = this->points;
data["location"] = this->location;
data["photo_url"] = this->photo_url;
data["banner_url"] = this->banner_url;
data["last_online"] = this->last_online;
data["biography"] = this->biography;
data["view_count"] = this->view_count;
data["created"] = this->created;
data["updated"] = this->updated;
return data;
}
}

1
vendor/Mintaka vendored Submodule

Submodule vendor/Mintaka added at ea2230d1e3

29
vendor/libhv/LICENSE vendored
View File

@ -1,29 +0,0 @@
BSD 3-Clause License
Copyright (c) 2020, ithewei
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,174 +0,0 @@
#ifndef HV_ASYNC_HTTP_CLIENT_H_
#define HV_ASYNC_HTTP_CLIENT_H_
#include <map>
#include <list>
#include "EventLoopThread.h"
#include "Channel.h"
#include "HttpMessage.h"
#include "HttpParser.h"
namespace hv {
template<typename Conn>
class ConnPool {
public:
int size() {
return conns_.size();
}
bool get(Conn& conn) {
if (conns_.empty()) return false;
conn = conns_.front();
conns_.pop_front();
return true;
}
bool add(const Conn& conn) {
conns_.push_back(conn);
return true;
}
bool remove(const Conn& conn) {
auto iter = conns_.begin();
while (iter != conns_.end()) {
if (*iter == conn) {
iter = conns_.erase(iter);
return true;
} else {
++iter;
}
}
return false;
}
private:
std::list<Conn> conns_;
};
struct HttpClientTask {
HttpRequestPtr req;
HttpResponseCallback cb;
uint64_t start_time;
};
typedef std::shared_ptr<HttpClientTask> HttpClientTaskPtr;
struct HttpClientContext {
HttpClientTaskPtr task;
HttpResponsePtr resp;
HttpParserPtr parser;
TimerID timerID;
HttpClientContext() {
timerID = INVALID_TIMER_ID;
}
~HttpClientContext() {
cancelTimer();
}
void cancelTimer() {
if (timerID != INVALID_TIMER_ID) {
killTimer(timerID);
timerID = INVALID_TIMER_ID;
}
}
void cancelTask() {
cancelTimer();
task = NULL;
}
void callback() {
cancelTimer();
if (task && task->cb) {
task->cb(resp);
}
// NOTE: task done
task = NULL;
}
void successCallback() {
callback();
resp = NULL;
}
void errorCallback() {
resp = NULL;
callback();
}
};
class HV_EXPORT AsyncHttpClient : private EventLoopThread {
public:
AsyncHttpClient(EventLoopPtr loop = NULL) : EventLoopThread(loop) {
if (loop == NULL) {
EventLoopThread::start(true);
}
}
~AsyncHttpClient() {
EventLoopThread::stop(true);
}
// thread-safe
int send(const HttpRequestPtr& req, HttpResponseCallback resp_cb) {
HttpClientTaskPtr task(new HttpClientTask);
task->req = req;
task->cb = std::move(resp_cb);
task->start_time = hloop_now_hrtime(EventLoopThread::hloop());
if (req->retry_count > 0 && req->retry_delay > 0) {
req->retry_count = MIN(req->retry_count, req->timeout * 1000 / req->retry_delay - 1);
}
return send(task);
}
int send(const HttpClientTaskPtr& task) {
EventLoopThread::loop()->queueInLoop(std::bind(&AsyncHttpClient::sendInLoop, this, task));
return 0;
}
protected:
void sendInLoop(HttpClientTaskPtr task) {
int err = doTask(task);
if (err != 0 && task->cb) {
task->cb(NULL);
}
}
int doTask(const HttpClientTaskPtr& task);
static int sendRequest(const SocketChannelPtr& channel);
// channel
const SocketChannelPtr& getChannel(int fd) {
return channels[fd];
// return fd < channels.capacity() ? channels[fd] : NULL;
}
const SocketChannelPtr& addChannel(hio_t* io) {
SocketChannelPtr channel(new SocketChannel(io));
channel->newContext<HttpClientContext>();
int fd = channel->fd();
channels[fd] = channel;
return channels[fd];
}
void removeChannel(const SocketChannelPtr& channel) {
channel->deleteContext<HttpClientContext>();
int fd = channel->fd();
channels.erase(fd);
}
private:
// NOTE: just one loop thread, no need mutex.
// fd => SocketChannelPtr
std::map<int, SocketChannelPtr> channels;
// peeraddr => ConnPool
std::map<std::string, ConnPool<int>> conn_pools;
};
}
#endif // HV_ASYNC_HTTP_CLIENT_H_

View File

@ -1,15 +0,0 @@
#ifndef HV_BUFFER_HPP_
#define HV_BUFFER_HPP_
#include <memory>
#include "hbuf.h"
namespace hv {
typedef HBuf Buffer;
typedef std::shared_ptr<Buffer> BufferPtr;
}
#endif // HV_BUFFER_HPP_

View File

@ -1,371 +0,0 @@
#ifndef HV_CHANNEL_HPP_
#define HV_CHANNEL_HPP_
#include <string>
#include <functional>
#include <memory>
#include "hloop.h"
#include "hsocket.h"
#include "Buffer.h"
namespace hv {
class Channel {
public:
Channel(hio_t* io = NULL) {
io_ = io;
fd_ = -1;
id_ = 0;
ctx_ = NULL;
status = CLOSED;
if (io) {
fd_ = hio_fd(io);
id_ = hio_id(io);
ctx_ = hio_context(io);
hio_set_context(io, this);
if (hio_is_opened(io)) {
status = OPENED;
}
if (hio_getcb_read(io) == NULL) {
hio_setcb_read(io_, on_read);
}
if (hio_getcb_write(io) == NULL) {
hio_setcb_write(io_, on_write);
}
if (hio_getcb_close(io) == NULL) {
hio_setcb_close(io_, on_close);
}
}
}
virtual ~Channel() {
if (isOpened()) {
close();
// NOTE: Detach after destructor to avoid triggering onclose
if (io_ && id_ == hio_id(io_)) {
hio_set_context(io_, NULL);
}
}
}
hio_t* io() { return io_; }
int fd() { return fd_; }
uint32_t id() { return id_; }
int error() { return hio_error(io_); }
// context
void* context() {
return ctx_;
}
void setContext(void* ctx) {
ctx_ = ctx;
}
template<class T>
T* newContext() {
ctx_ = new T;
return (T*)ctx_;
}
template<class T>
T* getContext() {
return (T*)ctx_;
}
template<class T>
void deleteContext() {
if (ctx_) {
delete (T*)ctx_;
ctx_ = NULL;
}
}
// contextPtr
std::shared_ptr<void> contextPtr() {
return contextPtr_;
}
void setContextPtr(const std::shared_ptr<void>& ctx) {
contextPtr_ = ctx;
}
void setContextPtr(std::shared_ptr<void>&& ctx) {
contextPtr_ = std::move(ctx);
}
template<class T>
std::shared_ptr<T> newContextPtr() {
contextPtr_ = std::make_shared<T>();
return std::static_pointer_cast<T>(contextPtr_);
}
template<class T>
std::shared_ptr<T> getContextPtr() {
return std::static_pointer_cast<T>(contextPtr_);
}
void deleteContextPtr() {
contextPtr_.reset();
}
bool isOpened() {
if (io_ == NULL || status >= DISCONNECTED) return false;
return id_ == hio_id(io_) && hio_is_opened(io_);
}
bool isClosed() {
return !isOpened();
}
int startRead() {
if (!isOpened()) return -1;
return hio_read_start(io_);
}
int stopRead() {
if (!isOpened()) return -1;
return hio_read_stop(io_);
}
int readOnce() {
if (!isOpened()) return -1;
return hio_read_once(io_);
}
int readString() {
if (!isOpened()) return -1;
return hio_readstring(io_);
}
int readLine() {
if (!isOpened()) return -1;
return hio_readline(io_);
}
int readBytes(int len) {
if (!isOpened() || len <= 0) return -1;
return hio_readbytes(io_, len);
}
// write thread-safe
int write(const void* data, int size) {
if (!isOpened()) return -1;
return hio_write(io_, data, size);
}
int write(Buffer* buf) {
return write(buf->data(), buf->size());
}
int write(const std::string& str) {
return write(str.data(), str.size());
}
// iobuf setting
void setMaxReadBufsize(uint32_t size) {
if (io_ == NULL) return;
hio_set_max_read_bufsize(io_, size);
}
void setMaxWriteBufsize(uint32_t size) {
if (io_ == NULL) return;
hio_set_max_write_bufsize(io_, size);
}
size_t writeBufsize() {
if (io_ == NULL) return 0;
return hio_write_bufsize(io_);
}
bool isWriteComplete() {
return writeBufsize() == 0;
}
// close thread-safe
int close(bool async = false) {
if (isClosed()) return -1;
status = CLOSED;
return async ? hio_close_async(io_) : hio_close(io_);
}
public:
hio_t* io_;
int fd_;
uint32_t id_;
void* ctx_;
enum Status {
OPENED,
CONNECTING,
CONNECTED,
DISCONNECTED,
CLOSED,
} status;
std::function<void(Buffer*)> onread;
// NOTE: Use Channel::isWriteComplete in onwrite callback to determine whether all data has been written.
std::function<void(Buffer*)> onwrite;
std::function<void()> onclose;
std::shared_ptr<void> contextPtr_;
private:
static void on_read(hio_t* io, void* data, int readbytes) {
Channel* channel = (Channel*)hio_context(io);
if (channel && channel->onread) {
Buffer buf(data, readbytes);
channel->onread(&buf);
}
}
static void on_write(hio_t* io, const void* data, int writebytes) {
Channel* channel = (Channel*)hio_context(io);
if (channel && channel->onwrite) {
Buffer buf((void*)data, writebytes);
channel->onwrite(&buf);
}
}
static void on_close(hio_t* io) {
Channel* channel = (Channel*)hio_context(io);
if (channel) {
channel->status = CLOSED;
if (channel->onclose) {
channel->onclose();
}
}
}
};
class SocketChannel : public Channel {
public:
std::function<void()> onconnect; // only for TcpClient
std::function<void()> heartbeat;
SocketChannel(hio_t* io) : Channel(io) {
}
virtual ~SocketChannel() {}
// SSL/TLS
int enableSSL() {
if (io_ == NULL) return -1;
return hio_enable_ssl(io_);
}
bool isSSL() {
if (io_ == NULL) return false;
return hio_is_ssl(io_);
}
int setSSL(hssl_t ssl) {
if (io_ == NULL) return -1;
return hio_set_ssl(io_, ssl);
}
int setSslCtx(hssl_ctx_t ssl_ctx) {
if (io_ == NULL) return -1;
return hio_set_ssl_ctx(io_, ssl_ctx);
}
int newSslCtx(hssl_ctx_opt_t* opt) {
if (io_ == NULL) return -1;
return hio_new_ssl_ctx(io_, opt);
}
// for hssl_set_sni_hostname
int setHostname(const std::string& hostname) {
if (io_ == NULL) return -1;
return hio_set_hostname(io_, hostname.c_str());
}
// timeout
void setConnectTimeout(int timeout_ms) {
if (io_ == NULL) return;
hio_set_connect_timeout(io_, timeout_ms);
}
void setCloseTimeout(int timeout_ms) {
if (io_ == NULL) return;
hio_set_close_timeout(io_, timeout_ms);
}
void setReadTimeout(int timeout_ms) {
if (io_ == NULL) return;
hio_set_read_timeout(io_, timeout_ms);
}
void setWriteTimeout(int timeout_ms) {
if (io_ == NULL) return;
hio_set_write_timeout(io_, timeout_ms);
}
void setKeepaliveTimeout(int timeout_ms) {
if (io_ == NULL) return;
hio_set_keepalive_timeout(io_, timeout_ms);
}
// heartbeat
// NOTE: Beware of circular reference problems caused by passing SocketChannelPtr by value.
void setHeartbeat(int interval_ms, std::function<void()> fn) {
if (io_ == NULL) return;
heartbeat = std::move(fn);
hio_set_heartbeat(io_, interval_ms, send_heartbeat);
}
/*
* unpack
*
* NOTE: unpack_setting_t of multiple IOs of the same function also are same,
* so only the pointer of unpack_setting_t is stored in hio_t,
* the life time of unpack_setting_t shoud be guaranteed by caller.
*/
void setUnpack(unpack_setting_t* setting) {
if (io_ == NULL) return;
hio_set_unpack(io_, setting);
}
int startConnect(int port, const char* host = "127.0.0.1") {
sockaddr_u peeraddr;
memset(&peeraddr, 0, sizeof(peeraddr));
int ret = sockaddr_set_ipport(&peeraddr, host, port);
if (ret != 0) {
// hloge("unknown host %s", host);
return ret;
}
return startConnect(&peeraddr.sa);
}
int startConnect(struct sockaddr* peeraddr) {
if (io_ == NULL) return -1;
hio_set_peeraddr(io_, peeraddr, SOCKADDR_LEN(peeraddr));
return startConnect();
}
int startConnect() {
if (io_ == NULL) return -1;
status = CONNECTING;
hio_setcb_connect(io_, on_connect);
return hio_connect(io_);
}
bool isConnected() {
return status == CONNECTED && isOpened();
}
std::string localaddr() {
if (io_ == NULL) return "";
struct sockaddr* addr = hio_localaddr(io_);
char buf[SOCKADDR_STRLEN] = {0};
return SOCKADDR_STR(addr, buf);
}
std::string peeraddr() {
if (io_ == NULL) return "";
struct sockaddr* addr = hio_peeraddr(io_);
char buf[SOCKADDR_STRLEN] = {0};
return SOCKADDR_STR(addr, buf);
}
private:
static void on_connect(hio_t* io) {
SocketChannel* channel = (SocketChannel*)hio_context(io);
if (channel) {
channel->status = CONNECTED;
if (channel->onconnect) {
channel->onconnect();
}
}
}
static void send_heartbeat(hio_t* io) {
SocketChannel* channel = (SocketChannel*)hio_context(io);
if (channel && channel->heartbeat) {
channel->heartbeat();
}
}
};
typedef std::shared_ptr<Channel> ChannelPtr;
typedef std::shared_ptr<SocketChannel> SocketChannelPtr;
}
#endif // HV_CHANNEL_HPP_

View File

@ -1,47 +0,0 @@
#ifndef HV_EVENT_HPP_
#define HV_EVENT_HPP_
#include <functional>
#include <memory>
#include "hloop.h"
namespace hv {
struct Event;
struct Timer;
typedef uint64_t TimerID;
#define INVALID_TIMER_ID ((hv::TimerID)-1)
typedef std::function<void(Event*)> EventCallback;
typedef std::function<void(TimerID)> TimerCallback;
struct Event {
hevent_t event;
EventCallback cb;
Event(EventCallback cb = NULL) {
memset(&event, 0, sizeof(hevent_t));
this->cb = std::move(cb);
}
};
struct Timer {
htimer_t* timer;
TimerCallback cb;
uint32_t repeat;
Timer(htimer_t* timer = NULL, TimerCallback cb = NULL, uint32_t repeat = INFINITE) {
this->timer = timer;
this->cb = std::move(cb);
this->repeat = repeat;
}
};
typedef std::shared_ptr<Event> EventPtr;
typedef std::shared_ptr<Timer> TimerPtr;
}
#endif // HV_EVENT_HPP_

View File

@ -1,277 +0,0 @@
#ifndef HV_EVENT_LOOP_HPP_
#define HV_EVENT_LOOP_HPP_
#include <functional>
#include <queue>
#include <map>
#include <mutex>
#include "hloop.h"
#include "hthread.h"
#include "Status.h"
#include "Event.h"
#include "ThreadLocalStorage.h"
namespace hv {
class EventLoop : public Status {
public:
typedef std::function<void()> Functor;
// New an EventLoop using an existing hloop_t object,
// so we can embed an EventLoop object into the old application based on hloop.
// NOTE: Be careful to deal with destroy of hloop_t.
EventLoop(hloop_t* loop = NULL) {
setStatus(kInitializing);
if (loop) {
loop_ = loop;
is_loop_owner = false;
} else {
loop_ = hloop_new(HLOOP_FLAG_AUTO_FREE);
is_loop_owner = true;
}
connectionNum = 0;
nextTimerID = 0;
setStatus(kInitialized);
}
~EventLoop() {
stop();
}
hloop_t* loop() {
return loop_;
}
// @brief Run loop forever
void run() {
if (loop_ == NULL) return;
if (status() == kRunning) return;
ThreadLocalStorage::set(ThreadLocalStorage::EVENT_LOOP, this);
setStatus(kRunning);
hloop_run(loop_);
setStatus(kStopped);
}
// stop thread-safe
void stop() {
if (loop_ == NULL) return;
if (status() < kRunning) {
if (is_loop_owner) {
hloop_free(&loop_);
}
loop_ = NULL;
return;
}
setStatus(kStopping);
hloop_stop(loop_);
loop_ = NULL;
}
void pause() {
if (loop_ == NULL) return;
if (isRunning()) {
hloop_pause(loop_);
setStatus(kPause);
}
}
void resume() {
if (loop_ == NULL) return;
if (isPause()) {
hloop_resume(loop_);
setStatus(kRunning);
}
}
// Timer interfaces: setTimer, killTimer, resetTimer
TimerID setTimer(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE, TimerID timerID = INVALID_TIMER_ID) {
assertInLoopThread();
if (loop_ == NULL) return INVALID_TIMER_ID;
htimer_t* htimer = htimer_add(loop_, onTimer, timeout_ms, repeat);
assert(htimer != NULL);
if (timerID == INVALID_TIMER_ID) {
timerID = generateTimerID();
}
hevent_set_id(htimer, timerID);
hevent_set_userdata(htimer, this);
timers[timerID] = std::make_shared<Timer>(htimer, cb, repeat);
return timerID;
}
// setTimerInLoop thread-safe
TimerID setTimerInLoop(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE, TimerID timerID = INVALID_TIMER_ID) {
if (timerID == INVALID_TIMER_ID) {
timerID = generateTimerID();
}
runInLoop(std::bind(&EventLoop::setTimer, this, timeout_ms, cb, repeat, timerID));
return timerID;
}
// alias javascript setTimeout, setInterval
// setTimeout thread-safe
TimerID setTimeout(int timeout_ms, TimerCallback cb) {
return setTimerInLoop(timeout_ms, cb, 1);
}
// setInterval thread-safe
TimerID setInterval(int interval_ms, TimerCallback cb) {
return setTimerInLoop(interval_ms, cb, INFINITE);
}
// killTimer thread-safe
void killTimer(TimerID timerID) {
runInLoop([timerID, this](){
auto iter = timers.find(timerID);
if (iter != timers.end()) {
htimer_del(iter->second->timer);
timers.erase(iter);
}
});
}
// resetTimer thread-safe
void resetTimer(TimerID timerID, int timeout_ms = 0) {
runInLoop([timerID, timeout_ms, this](){
auto iter = timers.find(timerID);
if (iter != timers.end()) {
htimer_reset(iter->second->timer, timeout_ms);
if (iter->second->repeat == 0) {
iter->second->repeat = 1;
}
}
});
}
long tid() {
if (loop_ == NULL) return hv_gettid();
return hloop_tid(loop_);
}
bool isInLoopThread() {
if (loop_ == NULL) return false;
return hv_gettid() == hloop_tid(loop_);
}
void assertInLoopThread() {
assert(isInLoopThread());
}
void runInLoop(Functor fn) {
if (isRunning() && isInLoopThread()) {
if (fn) fn();
} else {
queueInLoop(std::move(fn));
}
}
void queueInLoop(Functor fn) {
postEvent([fn](Event* ev) {
if (fn) fn();
});
}
void postEvent(EventCallback cb) {
if (loop_ == NULL) return;
EventPtr ev(new Event(cb));
hevent_set_userdata(&ev->event, this);
ev->event.cb = onCustomEvent;
mutex_.lock();
customEvents.push(ev);
mutex_.unlock();
hloop_post_event(loop_, &ev->event);
}
private:
TimerID generateTimerID() {
return (((TimerID)tid() & 0xFFFFFFFF) << 32) | ++nextTimerID;
}
static void onTimer(htimer_t* htimer) {
EventLoop* loop = (EventLoop*)hevent_userdata(htimer);
TimerID timerID = hevent_id(htimer);
TimerPtr timer = NULL;
auto iter = loop->timers.find(timerID);
if (iter != loop->timers.end()) {
timer = iter->second;
if (timer->repeat != INFINITE) --timer->repeat;
}
if (timer) {
if (timer->cb) timer->cb(timerID);
if (timer->repeat == 0) {
// htimer_t alloc and free by hloop, but timers[timerID] managed by EventLoop.
loop->timers.erase(timerID);
}
}
}
static void onCustomEvent(hevent_t* hev) {
EventLoop* loop = (EventLoop*)hevent_userdata(hev);
loop->mutex_.lock();
EventPtr ev = loop->customEvents.front();
loop->customEvents.pop();
loop->mutex_.unlock();
if (ev && ev->cb) ev->cb(ev.get());
}
public:
std::atomic<uint32_t> connectionNum; // for LB_LeastConnections
private:
hloop_t* loop_;
bool is_loop_owner;
std::mutex mutex_;
std::queue<EventPtr> customEvents; // GUAREDE_BY(mutex_)
std::map<TimerID, TimerPtr> timers;
std::atomic<TimerID> nextTimerID;
};
typedef std::shared_ptr<EventLoop> EventLoopPtr;
// ThreadLocalStorage
static inline EventLoop* tlsEventLoop() {
return (EventLoop*)ThreadLocalStorage::get(ThreadLocalStorage::EVENT_LOOP);
}
#define currentThreadEventLoop tlsEventLoop()
static inline TimerID setTimer(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE) {
EventLoop* loop = tlsEventLoop();
assert(loop != NULL);
if (loop == NULL) return INVALID_TIMER_ID;
return loop->setTimer(timeout_ms, cb, repeat);
}
static inline void killTimer(TimerID timerID) {
EventLoop* loop = tlsEventLoop();
assert(loop != NULL);
if (loop == NULL) return;
loop->killTimer(timerID);
}
static inline void resetTimer(TimerID timerID, int timeout_ms) {
EventLoop* loop = tlsEventLoop();
assert(loop != NULL);
if (loop == NULL) return;
loop->resetTimer(timerID, timeout_ms);
}
static inline TimerID setTimeout(int timeout_ms, TimerCallback cb) {
return setTimer(timeout_ms, cb, 1);
}
static inline TimerID setInterval(int interval_ms, TimerCallback cb) {
return setTimer(interval_ms, cb, INFINITE);
}
}
#endif // HV_EVENT_LOOP_HPP_

View File

@ -1,117 +0,0 @@
#ifndef HV_EVENT_LOOP_THREAD_HPP_
#define HV_EVENT_LOOP_THREAD_HPP_
#include <thread>
#include "hlog.h"
#include "EventLoop.h"
namespace hv {
class EventLoopThread : public Status {
public:
// Return 0 means OK, other failed.
typedef std::function<int()> Functor;
EventLoopThread(EventLoopPtr loop = NULL) {
setStatus(kInitializing);
loop_ = loop ? loop : std::make_shared<EventLoop>();
setStatus(kInitialized);
}
~EventLoopThread() {
stop();
join();
}
const EventLoopPtr& loop() {
return loop_;
}
hloop_t* hloop() {
return loop_->loop();
}
bool isRunning() {
return loop_->isRunning();
}
// @param wait_thread_started: if ture this method will block until loop_thread started.
// @param pre: This functor will be executed when loop_thread started.
// @param post:This Functor will be executed when loop_thread stopped.
void start(bool wait_thread_started = true,
Functor pre = Functor(),
Functor post = Functor()) {
if (status() >= kStarting && status() < kStopped) return;
setStatus(kStarting);
thread_.reset(new std::thread(&EventLoopThread::loop_thread, this, pre, post));
if (wait_thread_started) {
while (loop_->status() < kRunning) {
hv_delay(1);
}
}
}
// @param wait_thread_started: if ture this method will block until loop_thread stopped.
// stop thread-safe
void stop(bool wait_thread_stopped = false) {
if (status() < kStarting || status() >= kStopping) return;
setStatus(kStopping);
long loop_tid = loop_->tid();
loop_->stop();
if (wait_thread_stopped) {
if (hv_gettid() == loop_tid) return;
while (!isStopped()) {
hv_delay(1);
}
}
}
// @brief join loop_thread
// @note destructor will join loop_thread if you forget to call this method.
void join() {
if (thread_ && thread_->joinable()) {
thread_->join();
thread_ = NULL;
}
}
private:
void loop_thread(const Functor& pre, const Functor& post) {
hlogi("EventLoopThread started, tid=%ld", hv_gettid());
setStatus(kStarted);
if (pre) {
loop_->queueInLoop([this, pre]{
if (pre() != 0) {
loop_->stop();
}
});
}
loop_->run();
assert(loop_->isStopped());
if (post) {
post();
}
setStatus(kStopped);
hlogi("EventLoopThread stopped, tid=%ld", hv_gettid());
}
private:
EventLoopPtr loop_;
std::shared_ptr<std::thread> thread_;
};
typedef std::shared_ptr<EventLoopThread> EventLoopThreadPtr;
}
#endif // HV_EVENT_LOOP_THREAD_HPP_

View File

@ -1,140 +0,0 @@
#ifndef HV_EVENT_LOOP_THREAD_POOL_HPP_
#define HV_EVENT_LOOP_THREAD_POOL_HPP_
#include "EventLoopThread.h"
#include "hbase.h"
namespace hv {
class EventLoopThreadPool : public Status {
public:
EventLoopThreadPool(int thread_num = std::thread::hardware_concurrency()) {
setStatus(kInitializing);
thread_num_ = thread_num;
next_loop_idx_ = 0;
setStatus(kInitialized);
}
~EventLoopThreadPool() {
stop();
join();
}
int threadNum() {
return thread_num_;
}
void setThreadNum(int num) {
thread_num_ = num;
}
EventLoopPtr nextLoop(load_balance_e lb = LB_RoundRobin) {
int numLoops = loop_threads_.size();
if (numLoops == 0) return NULL;
int idx = 0;
if (lb == LB_RoundRobin) {
if (++next_loop_idx_ >= numLoops) next_loop_idx_ = 0;
idx = next_loop_idx_;
} else if (lb == LB_Random) {
idx = hv_rand(0, numLoops - 1);
} else if (lb == LB_LeastConnections) {
for (int i = 1; i < numLoops; ++i) {
if (loop_threads_[i]->loop()->connectionNum < loop_threads_[idx]->loop()->connectionNum) {
idx = i;
}
}
} else {
// Not Implemented
}
return loop_threads_[idx]->loop();
}
EventLoopPtr loop(int idx = -1) {
if (idx >= 0 && idx < loop_threads_.size()) {
return loop_threads_[idx]->loop();
}
return nextLoop();
}
hloop_t* hloop(int idx = -1) {
EventLoopPtr ptr = loop(idx);
return ptr ? ptr->loop() : NULL;
}
// @param wait_threads_started: if ture this method will block until all loop_threads started.
// @param pre: This functor will be executed when loop_thread started.
// @param post:This Functor will be executed when loop_thread stopped.
void start(bool wait_threads_started = false,
std::function<void(const EventLoopPtr&)> pre = NULL,
std::function<void(const EventLoopPtr&)> post = NULL) {
if (thread_num_ == 0) return;
if (status() >= kStarting && status() < kStopped) return;
setStatus(kStarting);
std::shared_ptr<std::atomic<int>> started_cnt(new std::atomic<int>(0));
std::shared_ptr<std::atomic<int>> exited_cnt(new std::atomic<int>(0));
loop_threads_.clear();
for (int i = 0; i < thread_num_; ++i) {
EventLoopThreadPtr loop_thread(new EventLoopThread);
const EventLoopPtr& loop = loop_thread->loop();
loop_thread->start(false,
[this, started_cnt, pre, &loop]() {
if (++(*started_cnt) == thread_num_) {
setStatus(kRunning);
}
if (pre) pre(loop);
return 0;
},
[this, exited_cnt, post, &loop]() {
if (post) post(loop);
if (++(*exited_cnt) == thread_num_) {
setStatus(kStopped);
}
return 0;
}
);
loop_threads_.push_back(loop_thread);
}
if (wait_threads_started) {
while (status() < kRunning) {
hv_delay(1);
}
}
}
// @param wait_threads_started: if ture this method will block until all loop_threads stopped.
// stop thread-safe
void stop(bool wait_threads_stopped = false) {
if (status() < kStarting || status() >= kStopping) return;
setStatus(kStopping);
for (auto& loop_thread : loop_threads_) {
loop_thread->stop(false);
}
if (wait_threads_stopped) {
while (!isStopped()) {
hv_delay(1);
}
}
}
// @brief join all loop_threads
// @note destructor will join loop_threads if you forget to call this method.
void join() {
for (auto& loop_thread : loop_threads_) {
loop_thread->join();
}
}
private:
int thread_num_;
std::vector<EventLoopThreadPtr> loop_threads_;
std::atomic<unsigned int> next_loop_idx_;
};
}
#endif // HV_EVENT_LOOP_THREAD_POOL_HPP_

View File

@ -1,171 +0,0 @@
#ifndef HV_HTTP_CLIENT_H_
#define HV_HTTP_CLIENT_H_
#include "hexport.h"
#include "hssl.h"
#include "HttpMessage.h"
/*
#include <stdio.h>
#include "HttpClient.h"
int main(int argc, char* argv[]) {
HttpRequest req;
req.method = HTTP_GET;
req.url = "http://www.example.com";
HttpResponse res;
int ret = http_client_send(&req, &res);
printf("%s\n", req.Dump(true,true).c_str());
if (ret != 0) {
printf("* Failed:%s:%d\n", http_client_strerror(ret), ret);
}
else {
printf("%s\n", res.Dump(true,true).c_str());
}
return ret;
}
*/
typedef struct http_client_s http_client_t;
HV_EXPORT http_client_t* http_client_new(const char* host = NULL, int port = DEFAULT_HTTP_PORT, int https = 0);
HV_EXPORT int http_client_del(http_client_t* cli);
HV_EXPORT const char* http_client_strerror(int errcode);
// timeout: s
HV_EXPORT int http_client_set_timeout(http_client_t* cli, int timeout);
// SSL/TLS
HV_EXPORT int http_client_set_ssl_ctx(http_client_t* cli, hssl_ctx_t ssl_ctx);
// hssl_ctx_new(opt) -> http_client_set_ssl_ctx
HV_EXPORT int http_client_new_ssl_ctx(http_client_t* cli, hssl_ctx_opt_t* opt);
// common headers
HV_EXPORT int http_client_clear_headers(http_client_t* cli);
HV_EXPORT int http_client_set_header(http_client_t* cli, const char* key, const char* value);
HV_EXPORT int http_client_del_header(http_client_t* cli, const char* key);
HV_EXPORT const char* http_client_get_header(http_client_t* cli, const char* key);
// http_proxy
HV_EXPORT int http_client_set_http_proxy(http_client_t* cli, const char* host, int port);
// https_proxy
HV_EXPORT int http_client_set_https_proxy(http_client_t* cli, const char* host, int port);
// no_proxy
HV_EXPORT int http_client_add_no_proxy(http_client_t* cli, const char* host);
// sync
HV_EXPORT int http_client_send(http_client_t* cli, HttpRequest* req, HttpResponse* resp);
// async
// Intern will start an EventLoopThread when http_client_send_async first called,
// http_client_del will destroy the thread.
HV_EXPORT int http_client_send_async(http_client_t* cli, HttpRequestPtr req, HttpResponseCallback resp_cb = NULL);
// top-level api
// http_client_new -> http_client_send -> http_client_del
HV_EXPORT int http_client_send(HttpRequest* req, HttpResponse* resp);
// http_client_send_async(&default_async_client, ...)
HV_EXPORT int http_client_send_async(HttpRequestPtr req, HttpResponseCallback resp_cb = NULL);
// low-level api
// @retval >=0 connfd, <0 error
HV_EXPORT int http_client_connect(http_client_t* cli, const char* host, int port, int https, int timeout);
HV_EXPORT int http_client_send_header(http_client_t* cli, HttpRequest* req);
HV_EXPORT int http_client_send_data(http_client_t* cli, const char* data, int size);
HV_EXPORT int http_client_recv_data(http_client_t* cli, char* data, int size);
HV_EXPORT int http_client_recv_response(http_client_t* cli, HttpResponse* resp);
HV_EXPORT int http_client_close(http_client_t* cli);
namespace hv {
class HttpClient {
public:
HttpClient(const char* host = NULL, int port = DEFAULT_HTTP_PORT, int https = 0) {
_client = http_client_new(host, port, https);
}
~HttpClient() {
if (_client) {
http_client_del(_client);
_client = NULL;
}
}
// timeout: s
int setTimeout(int timeout) {
return http_client_set_timeout(_client, timeout);
}
// SSL/TLS
int setSslCtx(hssl_ctx_t ssl_ctx) {
return http_client_set_ssl_ctx(_client, ssl_ctx);
}
int newSslCtx(hssl_ctx_opt_t* opt) {
return http_client_new_ssl_ctx(_client, opt);
}
// headers
int clearHeaders() {
return http_client_clear_headers(_client);
}
int setHeader(const char* key, const char* value) {
return http_client_set_header(_client, key, value);
}
int delHeader(const char* key) {
return http_client_del_header(_client, key);
}
const char* getHeader(const char* key) {
return http_client_get_header(_client, key);
}
// http_proxy
int setHttpProxy(const char* host, int port) {
return http_client_set_http_proxy(_client, host, port);
}
// https_proxy
int setHttpsProxy(const char* host, int port) {
return http_client_set_https_proxy(_client, host, port);
}
// no_proxy
int addNoProxy(const char* host) {
return http_client_add_no_proxy(_client, host);
}
// sync
int send(HttpRequest* req, HttpResponse* resp) {
return http_client_send(_client, req, resp);
}
// async
int sendAsync(HttpRequestPtr req, HttpResponseCallback resp_cb = NULL) {
return http_client_send_async(_client, req, std::move(resp_cb));
}
// low-level api
int connect(const char* host, int port = DEFAULT_HTTP_PORT, int https = 0, int timeout = DEFAULT_HTTP_CONNECT_TIMEOUT) {
return http_client_connect(_client, host, port, https, timeout);
}
int sendHeader(HttpRequest* req) {
return http_client_send_header(_client, req);
}
int sendData(const char* data, int size) {
return http_client_send_data(_client, data, size);
}
int recvData(char* data, int size) {
return http_client_recv_data(_client, data, size);
}
int recvResponse(HttpResponse* resp) {
return http_client_recv_response(_client, resp);
}
int close() {
return http_client_close(_client);
}
private:
http_client_t* _client;
};
}
#endif // HV_HTTP_CLIENT_H_

View File

@ -1,213 +0,0 @@
#ifndef HV_HTTP_CONTEXT_H_
#define HV_HTTP_CONTEXT_H_
#include "hexport.h"
#include "HttpMessage.h"
#include "HttpResponseWriter.h"
namespace hv {
struct HttpService;
struct HV_EXPORT HttpContext {
HttpService* service;
HttpRequestPtr request;
HttpResponsePtr response;
HttpResponseWriterPtr writer;
void* userdata;
HttpContext() {
service = NULL;
userdata = NULL;
}
// HttpRequest aliases
// return request->xxx
std::string ip() {
return request->client_addr.ip;
}
int port() {
return request->client_addr.port;
}
http_method method() {
return request->method;
}
std::string url() {
return request->url;
}
std::string path() {
return request->Path();
}
std::string fullpath() {
return request->FullPath();
}
std::string host() {
return request->Host();
}
const http_headers& headers() {
return request->headers;
}
std::string header(const char* key, const std::string& defvalue = hv::empty_string) {
return request->GetHeader(key, defvalue);
}
const hv::QueryParams& params() {
return request->query_params;
}
std::string param(const char* key, const std::string& defvalue = hv::empty_string) {
return request->GetParam(key, defvalue);
}
const HttpCookie& cookie(const char* name) {
return request->GetCookie(name);
}
int length() {
return request->ContentLength();
}
http_content_type type() {
return request->ContentType();
}
bool is(http_content_type content_type) {
return request->ContentType() == content_type;
}
bool is(const char* content_type) {
return request->ContentType() == http_content_type_enum(content_type);
}
std::string& body() {
return request->body;
}
#ifndef WITHOUT_HTTP_CONTENT
// Content-Type: application/json
const hv::Json& json() {
return request->GetJson();
}
// Content-Type: multipart/form-data
const hv::MultiPart& form() {
return request->GetForm();
}
std::string form(const char* name, const std::string& defvalue = hv::empty_string) {
return request->GetFormData(name, defvalue);
}
// Content-Type: application/x-www-form-urlencoded
const hv::KeyValue& urlencoded() {
return request->GetUrlEncoded();
}
std::string urlencoded(const char* key, const std::string& defvalue = hv::empty_string) {
return request->GetUrlEncoded(key, defvalue);
}
// T=[bool, int, int64_t, float, double]
template<typename T>
T get(const char* key, T defvalue = 0) {
return request->Get(key, defvalue);
}
std::string get(const char* key, const std::string& defvalue = hv::empty_string) {
return request->GetString(key, defvalue);
}
#endif
// HttpResponse aliases
// response->xxx = xxx
void setStatus(http_status status) {
response->status_code = status;
}
void setContentType(http_content_type type) {
response->content_type = type;
}
void setContentType(const char* type) {
response->content_type = http_content_type_enum(type);
}
void setHeader(const char* key, const std::string& value) {
response->SetHeader(key, value);
if (stricmp(key, "Content-Type") == 0) {
setContentType(value.c_str());
}
}
void setCookie(const HttpCookie& cookie) {
response->AddCookie(cookie);
}
void setBody(const std::string& body) {
response->body = body;
}
// response->sendXxx
int send() {
if (writer) {
writer->End();
}
return response->status_code;
}
int send(const std::string& str, http_content_type type = APPLICATION_JSON) {
response->content_type = type;
response->body = str;
return send();
}
int sendString(const std::string& str) {
response->String(str);
return send();
}
int sendData(void* data, int len, bool nocopy = true) {
response->Data(data, len, nocopy);
return send();
}
int sendFile(const char* filepath) {
response->File(filepath);
return send();
}
#ifndef WITHOUT_HTTP_CONTENT
// T=[bool, int, int64_t, float, double, string]
template<typename T>
void set(const char* key, const T& value) {
response->Set(key, value);
}
// @see HttpMessage::Json
// @usage https://github.com/nlohmann/json
template<typename T>
int sendJson(const T& t) {
response->Json(t);
return send();
}
#endif
int redirect(const std::string& location, http_status status = HTTP_STATUS_FOUND) {
response->Redirect(location, status);
return send();
}
int close() {
return writer ? writer->close(true) : -1;
}
};
} // end namespace hv
typedef std::shared_ptr<hv::HttpContext> HttpContextPtr;
#endif // HV_HTTP_CONTEXT_H_

View File

@ -1,509 +0,0 @@
#ifndef HV_HTTP_MESSAGE_H_
#define HV_HTTP_MESSAGE_H_
/*
* @class HttpMessage
* HttpRequest extends HttpMessage
* HttpResponse extends HttpMessage
*
* @member
* request-line: GET / HTTP/1.1\r\n => method path
* response-line: HTTP/1.1 200 OK\r\n => status_code
* headers, cookies
* body
*
* content, content_length, content_type
* json, form, kv
*
* @function
* Content, ContentLength, ContentType
* ParseUrl, ParseBody
* DumpUrl, DumpHeaders, DumpBody, Dump
* GetHeader, GetParam, GetJson, GetFormData, GetUrlEncoded
* SetHeader, SetParam, SetBody, SetFormData, SetUrlEncoded
* Get<T>, Set<T>
* GetString, GetBool, GetInt, GetFloat
* String, Data, Json, File, FormFile
*
* @example
* examples/http_server_test.cpp
* examples/http_client_test.cpp
* examples/httpd
*
*/
#include <memory>
#include <string>
#include <map>
#include <functional>
#include "hexport.h"
#include "hbase.h"
#include "hstring.h"
#include "hfile.h"
#include "hpath.h"
#include "httpdef.h"
#include "http_content.h"
namespace hv {
struct NetAddr {
std::string ip;
int port;
std::string ipport() {
return hv::asprintf("%s:%d", ip.c_str(), port);
}
};
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
// Cookie: sessionid=1; domain=.example.com; path=/; max-age=86400; secure; httponly
struct HV_EXPORT HttpCookie {
std::string name;
std::string value;
std::string domain;
std::string path;
std::string expires;
int max_age;
bool secure;
bool httponly;
enum SameSite {
Default,
Strict,
Lax,
None
} samesite;
enum Priority {
NotSet,
Low,
Medium,
High,
} priority;
hv::KeyValue kv; // for multiple names
HttpCookie();
void init();
void reset();
bool parse(const std::string& str);
std::string dump() const;
};
typedef std::map<std::string, std::string, hv::StringCaseLess> http_headers;
typedef std::vector<HttpCookie> http_cookies;
typedef std::string http_body;
HV_EXPORT extern http_headers DefaultHeaders;
HV_EXPORT extern http_body NoBody;
HV_EXPORT extern HttpCookie NoCookie;
class HV_EXPORT HttpMessage {
public:
static char s_date[32];
int type;
unsigned short http_major;
unsigned short http_minor;
http_headers headers;
http_cookies cookies;
http_body body;
// http_cb
std::function<void(HttpMessage*, http_parser_state state, const char* data, size_t size)> http_cb;
// structured content
void* content; // DATA_NO_COPY
size_t content_length;
http_content_type content_type;
#ifndef WITHOUT_HTTP_CONTENT
hv::Json json; // APPLICATION_JSON
hv::MultiPart form; // MULTIPART_FORM_DATA
hv::KeyValue kv; // X_WWW_FORM_URLENCODED
// T=[bool, int, int64_t, float, double]
template<typename T>
T Get(const char* key, T defvalue = 0);
std::string GetString(const char* key, const std::string& = "");
bool GetBool(const char* key, bool defvalue = 0);
int64_t GetInt(const char* key, int64_t defvalue = 0);
double GetFloat(const char* key, double defvalue = 0);
template<typename T>
void Set(const char* key, const T& value) {
switch (ContentType()) {
case APPLICATION_JSON:
json[key] = value;
break;
case MULTIPART_FORM_DATA:
form[key] = hv::FormData(value);
break;
case X_WWW_FORM_URLENCODED:
kv[key] = hv::to_string(value);
break;
default:
break;
}
}
/*
* @usage https://github.com/nlohmann/json
*
* null: Json(nullptr);
* boolean: Json(true);
* number: Json(123);
* string: Json("hello");
* object: Json(std::map<string, ValueType>);
* Json(hv::Json::object({
{"k1", "v1"},
{"k2", "v2"}
}));
* array: Json(std::vector<ValueType>);
Json(hv::Json::array(
{1, 2, 3}
));
*/
// Content-Type: application/json
template<typename T>
int Json(const T& t) {
content_type = APPLICATION_JSON;
hv::Json j(t);
body = j.dump(2);
return 200;
}
const hv::Json& GetJson() {
if (json.empty() && ContentType() == APPLICATION_JSON) {
ParseBody();
}
return json;
}
// Content-Type: multipart/form-data
template<typename T>
void SetFormData(const char* name, const T& t) {
form[name] = hv::FormData(t);
}
void SetFormFile(const char* name, const char* filepath) {
form[name] = hv::FormData(NULL, filepath);
}
int FormFile(const char* name, const char* filepath) {
content_type = MULTIPART_FORM_DATA;
form[name] = hv::FormData(NULL, filepath);
return 200;
}
const hv::MultiPart& GetForm() {
if (form.empty() && ContentType() == MULTIPART_FORM_DATA) {
ParseBody();
}
return form;
}
std::string GetFormData(const char* name, const std::string& defvalue = hv::empty_string) {
if (form.empty() && ContentType() == MULTIPART_FORM_DATA) {
ParseBody();
}
auto iter = form.find(name);
return iter == form.end() ? defvalue : iter->second.content;
}
int SaveFormFile(const char* name, const char* path) {
if (ContentType() != MULTIPART_FORM_DATA) {
return HTTP_STATUS_BAD_REQUEST;
}
if (form.empty()) {
ParseBody();
if (form.empty()) return HTTP_STATUS_BAD_REQUEST;
}
auto iter = form.find(name);
if (iter == form.end()) {
return HTTP_STATUS_BAD_REQUEST;
}
const auto& formdata = iter->second;
if (formdata.content.empty()) {
return HTTP_STATUS_BAD_REQUEST;
}
std::string filepath(path);
if (HPath::isdir(path)) {
filepath = HPath::join(filepath, formdata.filename);
}
HFile file;
if (file.open(filepath.c_str(), "wb") != 0) {
return HTTP_STATUS_INTERNAL_SERVER_ERROR;
}
file.write(formdata.content.data(), formdata.content.size());
return 200;
}
// Content-Type: application/x-www-form-urlencoded
template<typename T>
void SetUrlEncoded(const char* key, const T& t) {
kv[key] = hv::to_string(t);
}
const hv::KeyValue& GetUrlEncoded() {
if (kv.empty() && ContentType() == X_WWW_FORM_URLENCODED) {
ParseBody();
}
return kv;
}
std::string GetUrlEncoded(const char* key, const std::string& defvalue = hv::empty_string) {
if (kv.empty() && ContentType() == X_WWW_FORM_URLENCODED) {
ParseBody();
}
auto iter = kv.find(key);
return iter == kv.end() ? defvalue : iter->second;
}
#endif
HttpMessage();
virtual ~HttpMessage();
void Init();
virtual void Reset();
// structured-content -> content_type <-> headers["Content-Type"]
void FillContentType();
// body.size -> content_length <-> headers["Content-Length"]
void FillContentLength();
bool IsChunked();
bool IsKeepAlive();
// headers
void SetHeader(const char* key, const std::string& value);
std::string GetHeader(const char* key, const std::string& defvalue = hv::empty_string);
// cookies
void AddCookie(const HttpCookie& cookie);
const HttpCookie& GetCookie(const std::string& name);
// body
void SetBody(const std::string& body);
const std::string& Body();
// headers -> string
void DumpHeaders(std::string& str);
// structured content -> body
void DumpBody();
void DumpBody(std::string& str);
// body -> structured content
// @retval 0:succeed
int ParseBody();
virtual std::string Dump(bool is_dump_headers, bool is_dump_body);
void* Content() {
if (content == NULL && body.size() != 0) {
content = (void*)body.data();
}
return content;
}
size_t ContentLength() {
if (content_length == 0) {
FillContentLength();
}
return content_length;
}
http_content_type ContentType() {
if (content_type == CONTENT_TYPE_NONE) {
FillContentType();
}
return content_type;
}
void SetContentType(http_content_type type) {
content_type = type;
}
void SetContentType(const char* type) {
content_type = http_content_type_enum(type);
}
void SetContentTypeByFilename(const char* filepath) {
const char* suffix = hv_suffixname(filepath);
if (suffix) {
content_type = http_content_type_enum_by_suffix(suffix);
}
if (content_type == CONTENT_TYPE_NONE || content_type == CONTENT_TYPE_UNDEFINED) {
content_type = APPLICATION_OCTET_STREAM;
}
}
int String(const std::string& str) {
content_type = TEXT_PLAIN;
body = str;
return 200;
}
int Data(void* data, int len, bool nocopy = true) {
content_type = APPLICATION_OCTET_STREAM;
if (nocopy) {
content = data;
content_length = len;
} else {
content_length = body.size();
body.resize(content_length + len);
memcpy((void*)(body.data() + content_length), data, len);
content_length += len;
}
return 200;
}
int File(const char* filepath) {
HFile file;
if (file.open(filepath, "rb") != 0) {
return HTTP_STATUS_NOT_FOUND;
}
SetContentTypeByFilename(filepath);
file.readall(body);
return 200;
}
int SaveFile(const char* filepath) {
HFile file;
if (file.open(filepath, "wb") != 0) {
return HTTP_STATUS_NOT_FOUND;
}
file.write(body.data(), body.size());
return 200;
}
};
#define DEFAULT_HTTP_USER_AGENT "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
#define DEFAULT_HTTP_TIMEOUT 60 // s
#define DEFAULT_HTTP_CONNECT_TIMEOUT 10 // s
#define DEFAULT_HTTP_FAIL_RETRY_COUNT 1
#define DEFAULT_HTTP_FAIL_RETRY_DELAY 1000 // ms
class HV_EXPORT HttpRequest : public HttpMessage {
public:
http_method method;
// scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
std::string url;
// structured url
std::string scheme;
std::string host;
int port;
std::string path;
hv::QueryParams query_params;
// client_addr
hv::NetAddr client_addr; // for http server save client addr of request
// for HttpClient
uint16_t timeout; // unit: s
uint16_t connect_timeout;// unit: s
uint32_t retry_count; // just for AsyncHttpClient fail retry
uint32_t retry_delay; // just for AsyncHttpClient fail retry
unsigned redirect: 1;
unsigned proxy : 1;
HttpRequest();
void Init();
virtual void Reset();
virtual std::string Dump(bool is_dump_headers = true, bool is_dump_body = false);
// method
void SetMethod(const char* method) {
this->method = http_method_enum(method);
}
const char* Method() {
return http_method_str(method);
}
// scheme
bool IsHttps() {
return strncmp(scheme.c_str(), "https", 5) == 0 ||
strncmp(url.c_str(), "https://", 8) == 0;
}
// url
void SetUrl(const char* url) {
this->url = url;
}
const std::string& Url() {
return url;
}
// structed url -> url
void DumpUrl();
// url -> structed url
void ParseUrl();
// /path?query#fragment
std::string FullPath() { return path; }
// /path
std::string Path();
// ?query_params
template<typename T>
void SetParam(const char* key, const T& t) {
query_params[key] = hv::to_string(t);
}
std::string GetParam(const char* key, const std::string& defvalue = hv::empty_string) {
auto iter = query_params.find(key);
return iter == query_params.end() ? defvalue : iter->second;
}
// Host:
std::string Host() {
auto iter = headers.find("Host");
return iter == headers.end() ? host : iter->second;
}
void FillHost(const char* host, int port = DEFAULT_HTTP_PORT);
void SetHost(const char* host, int port = DEFAULT_HTTP_PORT);
void SetProxy(const char* host, int port);
bool IsProxy() { return proxy; }
// Auth
void SetAuth(const std::string& auth);
void SetBasicAuth(const std::string& username, const std::string& password);
void SetBearerTokenAuth(const std::string& token);
void SetTimeout(int sec) { timeout = sec; }
void SetConnectTimeout(int sec) { connect_timeout = sec; }
void AllowRedirect(bool on = true) { redirect = on; }
// NOTE: SetRetry just for AsyncHttpClient
void SetRetry(int count = DEFAULT_HTTP_FAIL_RETRY_COUNT,
int delay = DEFAULT_HTTP_FAIL_RETRY_DELAY) {
retry_count = count;
retry_delay = delay;
}
// Range: bytes=0-4095
void SetRange(long from = 0, long to = -1);
bool GetRange(long& from, long& to);
};
class HV_EXPORT HttpResponse : public HttpMessage {
public:
http_status status_code;
const char* status_message() {
return http_status_str(status_code);
}
HttpResponse();
void Init();
virtual void Reset();
virtual std::string Dump(bool is_dump_headers = true, bool is_dump_body = false);
// Content-Range: bytes 0-4095/10240000
void SetRange(long from, long to, long total);
bool GetRange(long& from, long& to, long& total);
int Redirect(const std::string& location, http_status status = HTTP_STATUS_FOUND) {
status_code = status;
SetHeader("Location", location);
return status_code;
}
};
typedef std::shared_ptr<HttpRequest> HttpRequestPtr;
typedef std::shared_ptr<HttpResponse> HttpResponsePtr;
typedef std::function<void(const HttpResponsePtr&)> HttpResponseCallback;
#endif // HV_HTTP_MESSAGE_H_

View File

@ -1,53 +0,0 @@
#ifndef HV_HTTP_PARSER_H_
#define HV_HTTP_PARSER_H_
#include "hexport.h"
#include "HttpMessage.h"
class HV_EXPORT HttpParser {
public:
http_version version;
http_session_type type;
static HttpParser* New(http_session_type type = HTTP_CLIENT, http_version version = HTTP_V1);
virtual ~HttpParser() {}
virtual int GetSendData(char** data, size_t* len) = 0;
virtual int FeedRecvData(const char* data, size_t len) = 0;
// Http1Parser: http_parser_state
// Http2Parser: http2_session_state
virtual int GetState() = 0;
// Http1Parser: GetState() != HP_MESSAGE_COMPLETE
// Http2Parser: GetState() == H2_WANT_RECV
virtual bool WantRecv() = 0;
// Http1Parser: GetState() == HP_MESSAGE_COMPLETE
// Http2Parser: GetState() == H2_WANT_SEND
virtual bool WantSend() = 0;
// IsComplete: Is recved HttpRequest or HttpResponse complete?
// Http1Parser: GetState() == HP_MESSAGE_COMPLETE
// Http2Parser: (state == H2_RECV_HEADERS || state == H2_RECV_DATA) && stream_closed
virtual bool IsComplete() = 0;
virtual bool IsEof() { return false; }
// client
// SubmitRequest -> while(GetSendData) {send} -> InitResponse -> do {recv -> FeedRecvData} while(WantRecv)
virtual int SubmitRequest(HttpRequest* req) = 0;
virtual int InitResponse(HttpResponse* res) = 0;
// server
// InitRequest -> do {recv -> FeedRecvData} while(WantRecv) -> SubmitResponse -> while(GetSendData) {send}
virtual int InitRequest(HttpRequest* req) = 0;
virtual int SubmitResponse(HttpResponse* res) = 0;
virtual int GetError() = 0;
virtual const char* StrError(int error) = 0;
};
typedef std::shared_ptr<HttpParser> HttpParserPtr;
#endif // HV_HTTP_PARSER_H_

View File

@ -1,196 +0,0 @@
#ifndef HV_HTTP_RESPONSE_WRITER_H_
#define HV_HTTP_RESPONSE_WRITER_H_
#include "Channel.h"
#include "HttpMessage.h"
namespace hv {
class HttpResponseWriter : public SocketChannel {
public:
HttpResponsePtr response;
enum State {
SEND_BEGIN = 0,
SEND_HEADER,
SEND_BODY,
SEND_CHUNKED,
SEND_CHUNKED_END,
SEND_END,
} state: 8, end: 8;
HttpResponseWriter(hio_t* io, const HttpResponsePtr& resp)
: SocketChannel(io)
, response(resp)
, state(SEND_BEGIN)
, end(SEND_BEGIN)
{}
~HttpResponseWriter() {}
// Begin -> End
// Begin -> WriteResponse -> End
// Begin -> WriteStatus -> WriteHeader -> WriteBody -> End
// Begin -> EndHeaders("Content-Type", "text/event-stream") -> write -> write -> ... -> close
// Begin -> EndHeaders("Content-Length", content_length) -> WriteBody -> WriteBody -> ... -> End
// Begin -> EndHeaders("Transfer-Encoding", "chunked") -> WriteChunked -> WriteChunked -> ... -> End
int Begin() {
state = end = SEND_BEGIN;
return 0;
}
int WriteStatus(http_status status_codes) {
response->status_code = status_codes;
return 0;
}
int WriteHeader(const char* key, const char* value) {
response->SetHeader(key, value);
return 0;
}
template<typename T>
int WriteHeader(const char* key, T num) {
response->SetHeader(key, hv::to_string(num));
return 0;
}
int WriteCookie(const HttpCookie& cookie) {
response->cookies.push_back(cookie);
return 0;
}
int EndHeaders(const char* key = NULL, const char* value = NULL) {
if (state != SEND_BEGIN) return -1;
if (key && value) {
response->SetHeader(key, value);
}
std::string headers = response->Dump(true, false);
state = SEND_HEADER;
return write(headers);
}
template<typename T>
int EndHeaders(const char* key, T num) {
std::string value = hv::to_string(num);
return EndHeaders(key, value.c_str());
}
int WriteChunked(const char* buf, int len = -1) {
int ret = 0;
if (len == -1) len = strlen(buf);
if (state == SEND_BEGIN) {
EndHeaders("Transfer-Encoding", "chunked");
}
char chunked_header[64];
int chunked_header_len = snprintf(chunked_header, sizeof(chunked_header), "%x\r\n", len);
write(chunked_header, chunked_header_len);
if (buf && len) {
state = SEND_CHUNKED;
ret = write(buf, len);
} else {
state = SEND_CHUNKED_END;
}
write("\r\n", 2);
return ret;
}
int WriteChunked(const std::string& str) {
return WriteChunked(str.c_str(), str.size());
}
int EndChunked() {
return WriteChunked(NULL, 0);
}
int WriteBody(const char* buf, int len = -1) {
if (response->IsChunked()) {
return WriteChunked(buf, len);
}
if (len == -1) len = strlen(buf);
if (state == SEND_BEGIN) {
response->body.append(buf, len);
return len;
} else {
state = SEND_BODY;
return write(buf, len);
}
}
int WriteBody(const std::string& str) {
return WriteBody(str.c_str(), str.size());
}
int WriteResponse(HttpResponse* resp) {
if (resp == NULL) {
response->status_code = HTTP_STATUS_INTERNAL_SERVER_ERROR;
return 0;
}
bool is_dump_headers = state == SEND_BEGIN ? true : false;
std::string msg = resp->Dump(is_dump_headers, true);
state = SEND_BODY;
return write(msg);
}
int SSEvent(const std::string& data, const char* event = "message") {
if (state == SEND_BEGIN) {
EndHeaders("Content-Type", "text/event-stream");
}
std::string msg;
msg = "event: "; msg += event; msg += "\n";
msg += "data: "; msg += data; msg += "\n\n";
state = SEND_BODY;
return write(msg);
}
int End(const char* buf = NULL, int len = -1) {
if (end == SEND_END) return 0;
end = SEND_END;
if (!isConnected()) {
return -1;
}
int ret = 0;
bool keepAlive = response->IsKeepAlive();
if (state == SEND_CHUNKED) {
if (buf) {
ret = WriteChunked(buf, len);
}
if (state == SEND_CHUNKED) {
EndChunked();
}
} else {
if (buf) {
ret = WriteBody(buf, len);
}
bool is_dump_headers = true;
bool is_dump_body = true;
if (state == SEND_HEADER) {
is_dump_headers = false;
} else if (state == SEND_BODY) {
is_dump_headers = false;
is_dump_body = false;
}
if (is_dump_body) {
std::string msg = response->Dump(is_dump_headers, is_dump_body);
state = SEND_BODY;
ret = write(msg);
}
}
if (!keepAlive) {
close(true);
}
return ret;
}
int End(const std::string& str) {
return End(str.c_str(), str.size());
}
};
}
typedef std::shared_ptr<hv::HttpResponseWriter> HttpResponseWriterPtr;
#endif // HV_HTTP_RESPONSE_WRITER_H_

View File

@ -1,148 +0,0 @@
#ifndef HV_HTTP_SERVER_H_
#define HV_HTTP_SERVER_H_
#include "hexport.h"
#include "hssl.h"
#include "HttpService.h"
// #include "WebSocketServer.h"
namespace hv {
struct WebSocketService;
}
using hv::HttpService;
using hv::WebSocketService;
typedef struct http_server_s {
char host[64];
int port; // http_port
int https_port;
int http_version;
int worker_processes;
int worker_threads;
uint32_t worker_connections; // max_connections = workers * worker_connections
HttpService* service; // http service
WebSocketService* ws; // websocket service
void* userdata;
int listenfd[2]; // 0: http, 1: https
void* privdata;
// hooks
std::function<void()> onWorkerStart;
std::function<void()> onWorkerStop;
// SSL/TLS
hssl_ctx_t ssl_ctx;
unsigned alloced_ssl_ctx: 1;
#ifdef __cplusplus
http_server_s() {
strcpy(host, "0.0.0.0");
// port = DEFAULT_HTTP_PORT;
// https_port = DEFAULT_HTTPS_PORT;
// port = 8080;
// https_port = 8443;
port = https_port = 0;
http_version = 1;
worker_processes = 0;
worker_threads = 0;
worker_connections = 1024;
service = NULL;
ws = NULL;
listenfd[0] = listenfd[1] = -1;
userdata = NULL;
privdata = NULL;
// SSL/TLS
ssl_ctx = NULL;
alloced_ssl_ctx = 0;
}
#endif
} http_server_t;
// @param wait: Whether to occupy current thread
HV_EXPORT int http_server_run(http_server_t* server, int wait = 1);
// NOTE: stop all loops and join all threads
HV_EXPORT int http_server_stop(http_server_t* server);
/*
#include "HttpServer.h"
using namespace hv;
int main() {
HttpService service;
service.GET("/ping", [](HttpRequest* req, HttpResponse* resp) {
resp->body = "pong";
return 200;
});
HttpServer server;
server.registerHttpService(&service);
server.setPort(8080);
server.setThreadNum(4);
server.run();
return 0;
}
*/
namespace hv {
class HttpServer : public http_server_t {
public:
HttpServer(HttpService* service = NULL)
: http_server_t()
{
this->service = service;
}
~HttpServer() { stop(); }
void registerHttpService(HttpService* service) {
this->service = service;
}
void setHost(const char* host = "0.0.0.0") {
if (host) strcpy(this->host, host);
}
void setPort(int port = 0, int ssl_port = 0) {
if (port >= 0) this->port = port;
if (ssl_port >= 0) this->https_port = ssl_port;
}
void setListenFD(int fd = -1, int ssl_fd = -1) {
if (fd >= 0) this->listenfd[0] = fd;
if (ssl_fd >= 0) this->listenfd[1] = ssl_fd;
}
void setProcessNum(int num) {
this->worker_processes = num;
}
void setThreadNum(int num) {
this->worker_threads = num;
}
// SSL/TLS
int setSslCtx(hssl_ctx_t ssl_ctx) {
this->ssl_ctx = ssl_ctx;
return 0;
}
int newSslCtx(hssl_ctx_opt_t* opt) {
// NOTE: hssl_ctx_free in http_server_stop
hssl_ctx_t ssl_ctx = hssl_ctx_new(opt);
if (ssl_ctx == NULL) return -1;
this->alloced_ssl_ctx = 1;
return setSslCtx(ssl_ctx);
}
int run(bool wait = true) {
return http_server_run(this, wait);
}
int start() {
return run(false);
}
int stop() {
return http_server_stop(this);
}
};
}
#endif // HV_HTTP_SERVER_H_

View File

@ -1,270 +0,0 @@
#ifndef HV_HTTP_SERVICE_H_
#define HV_HTTP_SERVICE_H_
#include <string>
#include <map>
#include <unordered_map>
#include <vector>
#include <list>
#include <memory>
#include <functional>
#include "hexport.h"
#include "HttpMessage.h"
#include "HttpResponseWriter.h"
#include "HttpContext.h"
#define DEFAULT_BASE_URL "/api/v1"
#define DEFAULT_DOCUMENT_ROOT "/var/www/html"
#define DEFAULT_HOME_PAGE "index.html"
#define DEFAULT_ERROR_PAGE "error.html"
#define DEFAULT_INDEXOF_DIR "/downloads/"
#define DEFAULT_KEEPALIVE_TIMEOUT 75000 // ms
// for FileCache
#define MAX_FILE_CACHE_SIZE (1 << 22) // 4M
#define DEFAULT_FILE_CACHE_STAT_INTERVAL 10 // s
#define DEFAULT_FILE_CACHE_EXPIRED_TIME 60 // s
/*
* @param[in] req: parsed structured http request
* @param[out] resp: structured http response
* @return 0: handle next
* http_status_code: handle done
*/
#define HTTP_STATUS_NEXT 0
#define HTTP_STATUS_UNFINISHED 0
// NOTE: http_sync_handler run on IO thread
typedef std::function<int(HttpRequest* req, HttpResponse* resp)> http_sync_handler;
// NOTE: http_async_handler run on hv::async threadpool
typedef std::function<void(const HttpRequestPtr& req, const HttpResponseWriterPtr& writer)> http_async_handler;
// NOTE: http_ctx_handler run on IO thread, you can easily post HttpContextPtr to your consumer thread for processing.
typedef std::function<int(const HttpContextPtr& ctx)> http_ctx_handler;
// NOTE: http_state_handler run on IO thread
typedef std::function<int(const HttpContextPtr& ctx, http_parser_state state, const char* data, size_t size)> http_state_handler;
struct http_handler {
http_sync_handler sync_handler;
http_async_handler async_handler;
http_ctx_handler ctx_handler;
http_state_handler state_handler;
http_handler() {}
http_handler(http_sync_handler fn) : sync_handler(std::move(fn)) {}
http_handler(http_async_handler fn) : async_handler(std::move(fn)) {}
http_handler(http_ctx_handler fn) : ctx_handler(std::move(fn)) {}
http_handler(http_state_handler fn) : state_handler(std::move(fn)) {}
http_handler(const http_handler& rhs)
: sync_handler(std::move(rhs.sync_handler))
, async_handler(std::move(rhs.async_handler))
, ctx_handler(std::move(rhs.ctx_handler))
, state_handler(std::move(rhs.state_handler))
{}
const http_handler& operator=(http_sync_handler fn) {
sync_handler = std::move(fn);
return *this;
}
const http_handler& operator=(http_async_handler fn) {
async_handler = std::move(fn);
return *this;
}
const http_handler& operator=(http_ctx_handler fn) {
ctx_handler = std::move(fn);
return *this;
}
const http_handler& operator=(http_state_handler fn) {
state_handler = std::move(fn);
return *this;
}
bool isNull() {
return sync_handler == NULL &&
async_handler == NULL &&
ctx_handler == NULL;
}
operator bool() {
return !isNull();
}
};
typedef std::vector<http_handler> http_handlers;
struct http_method_handler {
http_method method;
http_handler handler;
http_method_handler() {}
http_method_handler(http_method m, const http_handler& h) : method(m), handler(h) {}
};
// method => http_method_handler
typedef std::list<http_method_handler> http_method_handlers;
// path => http_method_handlers
typedef std::unordered_map<std::string, std::shared_ptr<http_method_handlers>> http_path_handlers;
namespace hv {
struct HV_EXPORT HttpService {
// preprocessor -> middleware -> processor -> postprocessor
http_handler preprocessor;
http_handlers middleware;
// processor: pathHandlers -> staticHandler -> errorHandler
http_handler processor;
http_handler postprocessor;
// api service (that is http.ApiServer)
std::string base_url;
http_path_handlers pathHandlers;
// file service (that is http.FileServer)
http_handler staticHandler;
http_handler largeFileHandler;
std::string document_root;
std::string home_page;
std::string error_page;
// nginx: location => root
std::map<std::string, std::string, std::greater<std::string>> staticDirs;
// indexof service (that is http.DirectoryServer)
std::string index_of;
http_handler errorHandler;
// proxy service (that is http.ProxyServer)
// nginx: location => proxy_pass
std::map<std::string, std::string, std::greater<std::string>> proxies;
int proxy_connect_timeout;
int proxy_read_timeout;
int proxy_write_timeout;
// options
int keepalive_timeout;
int max_file_cache_size; // cache small file
int file_cache_stat_interval; // stat file is modified
int file_cache_expired_time; // remove expired file cache
/*
* @test limit_rate
* @build make examples
* @server bin/httpd -c etc/httpd.conf -s restart -d
* @client bin/wget http://127.0.0.1:8080/downloads/test.zip
*/
int limit_rate; // limit send rate, unit: KB/s
unsigned enable_forward_proxy :1;
HttpService() {
// base_url = DEFAULT_BASE_URL;
document_root = DEFAULT_DOCUMENT_ROOT;
home_page = DEFAULT_HOME_PAGE;
// error_page = DEFAULT_ERROR_PAGE;
// index_of = DEFAULT_INDEXOF_DIR;
proxy_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
proxy_read_timeout = 0;
proxy_write_timeout = 0;
keepalive_timeout = DEFAULT_KEEPALIVE_TIMEOUT;
max_file_cache_size = MAX_FILE_CACHE_SIZE;
file_cache_stat_interval = DEFAULT_FILE_CACHE_STAT_INTERVAL;
file_cache_expired_time = DEFAULT_FILE_CACHE_EXPIRED_TIME;
limit_rate = -1; // unlimited
enable_forward_proxy = 0;
}
void AddRoute(const char* path, http_method method, const http_handler& handler);
// @retval 0 OK, else HTTP_STATUS_NOT_FOUND, HTTP_STATUS_METHOD_NOT_ALLOWED
int GetRoute(const char* url, http_method method, http_handler** handler);
// RESTful API /:field/ => req->query_params["field"]
int GetRoute(HttpRequest* req, http_handler** handler);
// Static("/", "/var/www/html")
void Static(const char* path, const char* dir);
// @retval / => /var/www/html/index.html
std::string GetStaticFilepath(const char* path);
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
void AllowCORS();
// forward proxy
void EnableForwardProxy() { enable_forward_proxy = 1; }
// reverse proxy
// Proxy("/api/v1/", "http://www.httpbin.org/");
void Proxy(const char* path, const char* url);
// @retval /api/v1/test => http://www.httpbin.org/test
std::string GetProxyUrl(const char* path);
hv::StringList Paths() {
hv::StringList paths;
for (auto& pair : pathHandlers) {
paths.emplace_back(pair.first);
}
return paths;
}
// Handler = [ http_sync_handler, http_ctx_handler ]
template<typename Handler>
void Use(Handler handlerFunc) {
middleware.emplace_back(handlerFunc);
}
// Inspired by github.com/gin-gonic/gin
// Handler = [ http_sync_handler, http_async_handler, http_ctx_handler, http_state_handler ]
template<typename Handler>
void Handle(const char* httpMethod, const char* relativePath, Handler handlerFunc) {
AddRoute(relativePath, http_method_enum(httpMethod), http_handler(handlerFunc));
}
// HEAD
template<typename Handler>
void HEAD(const char* relativePath, Handler handlerFunc) {
Handle("HEAD", relativePath, handlerFunc);
}
// GET
template<typename Handler>
void GET(const char* relativePath, Handler handlerFunc) {
Handle("GET", relativePath, handlerFunc);
}
// POST
template<typename Handler>
void POST(const char* relativePath, Handler handlerFunc) {
Handle("POST", relativePath, handlerFunc);
}
// PUT
template<typename Handler>
void PUT(const char* relativePath, Handler handlerFunc) {
Handle("PUT", relativePath, handlerFunc);
}
// DELETE
// NOTE: Windows <winnt.h> #define DELETE as a macro, we have to replace DELETE with Delete.
template<typename Handler>
void Delete(const char* relativePath, Handler handlerFunc) {
Handle("DELETE", relativePath, handlerFunc);
}
// PATCH
template<typename Handler>
void PATCH(const char* relativePath, Handler handlerFunc) {
Handle("PATCH", relativePath, handlerFunc);
}
// Any
template<typename Handler>
void Any(const char* relativePath, Handler handlerFunc) {
Handle("HEAD", relativePath, handlerFunc);
Handle("GET", relativePath, handlerFunc);
Handle("POST", relativePath, handlerFunc);
Handle("PUT", relativePath, handlerFunc);
Handle("DELETE", relativePath, handlerFunc);
Handle("PATCH", relativePath, handlerFunc);
}
};
}
#endif // HV_HTTP_SERVICE_H_

View File

@ -1,56 +0,0 @@
#ifndef HV_STATUS_HPP_
#define HV_STATUS_HPP_
#include <atomic>
namespace hv {
class Status {
public:
enum KStatus {
kNull = 0,
kInitializing = 1,
kInitialized = 2,
kStarting = 3,
kStarted = 4,
kRunning = 5,
kPause = 6,
kStopping = 7,
kStopped = 8,
kDestroyed = 9,
};
Status() {
status_ = kNull;
}
~Status() {
status_ = kDestroyed;
}
KStatus status() {
return status_;
}
void setStatus(KStatus status) {
status_ = status;
}
bool isRunning() {
return status_ == kRunning;
}
bool isPause() {
return status_ == kPause;
}
bool isStopped() {
return status_ == kStopped;
}
private:
std::atomic<KStatus> status_;
};
}
#endif // HV_STATUS_HPP_

View File

@ -1,301 +0,0 @@
#ifndef HV_TCP_CLIENT_HPP_
#define HV_TCP_CLIENT_HPP_
#include "hsocket.h"
#include "hssl.h"
#include "hlog.h"
#include "EventLoopThread.h"
#include "Channel.h"
namespace hv {
template<class TSocketChannel = SocketChannel>
class TcpClientEventLoopTmpl {
public:
typedef std::shared_ptr<TSocketChannel> TSocketChannelPtr;
TcpClientEventLoopTmpl(EventLoopPtr loop = NULL) {
loop_ = loop ? loop : std::make_shared<EventLoop>();
remote_port = 0;
connect_timeout = HIO_DEFAULT_CONNECT_TIMEOUT;
tls = false;
tls_setting = NULL;
reconn_setting = NULL;
unpack_setting = NULL;
}
virtual ~TcpClientEventLoopTmpl() {
HV_FREE(tls_setting);
HV_FREE(reconn_setting);
HV_FREE(unpack_setting);
}
const EventLoopPtr& loop() {
return loop_;
}
// NOTE: By default, not bind local port. If necessary, you can call bind() after createsocket().
// @retval >=0 connfd, <0 error
int createsocket(int remote_port, const char* remote_host = "127.0.0.1") {
memset(&remote_addr, 0, sizeof(remote_addr));
int ret = sockaddr_set_ipport(&remote_addr, remote_host, remote_port);
if (ret != 0) {
return NABS(ret);
}
this->remote_host = remote_host;
this->remote_port = remote_port;
return createsocket(&remote_addr.sa);
}
int createsocket(struct sockaddr* remote_addr) {
int connfd = ::socket(remote_addr->sa_family, SOCK_STREAM, 0);
// SOCKADDR_PRINT(remote_addr);
if (connfd < 0) {
perror("socket");
return -2;
}
hio_t* io = hio_get(loop_->loop(), connfd);
assert(io != NULL);
hio_set_peeraddr(io, remote_addr, SOCKADDR_LEN(remote_addr));
channel.reset(new TSocketChannel(io));
return connfd;
}
int bind(int local_port, const char* local_host = "0.0.0.0") {
sockaddr_u local_addr;
memset(&local_addr, 0, sizeof(local_addr));
int ret = sockaddr_set_ipport(&local_addr, local_host, local_port);
if (ret != 0) {
return NABS(ret);
}
return bind(&local_addr.sa);
}
int bind(struct sockaddr* local_addr) {
if (channel == NULL || channel->isClosed()) {
return -1;
}
int ret = ::bind(channel->fd(), local_addr, SOCKADDR_LEN(local_addr));
if (ret != 0) {
perror("bind");
}
return ret;
}
// closesocket thread-safe
void closesocket() {
if (channel) {
loop_->runInLoop([this](){
if (channel) {
setReconnect(NULL);
channel->close();
}
});
}
}
int startConnect() {
if (channel == NULL || channel->isClosed()) {
int connfd = createsocket(&remote_addr.sa);
if (connfd < 0) {
hloge("createsocket %s:%d return %d!\n", remote_host.c_str(), remote_port, connfd);
return connfd;
}
}
if (channel == NULL || channel->status >= SocketChannel::CONNECTING) {
return -1;
}
if (connect_timeout) {
channel->setConnectTimeout(connect_timeout);
}
if (tls) {
channel->enableSSL();
if (tls_setting) {
int ret = channel->newSslCtx(tls_setting);
if (ret != 0) {
hloge("new SSL_CTX failed: %d", ret);
closesocket();
return ret;
}
}
if (!is_ipaddr(remote_host.c_str())) {
channel->setHostname(remote_host);
}
}
channel->onconnect = [this]() {
if (unpack_setting) {
channel->setUnpack(unpack_setting);
}
channel->startRead();
if (onConnection) {
onConnection(channel);
}
if (reconn_setting) {
reconn_setting_reset(reconn_setting);
}
};
channel->onread = [this](Buffer* buf) {
if (onMessage) {
onMessage(channel, buf);
}
};
channel->onwrite = [this](Buffer* buf) {
if (onWriteComplete) {
onWriteComplete(channel, buf);
}
};
channel->onclose = [this]() {
if (onConnection) {
onConnection(channel);
}
// reconnect
if (reconn_setting) {
startReconnect();
}
};
return channel->startConnect();
}
int startReconnect() {
if (!reconn_setting) return -1;
if (!reconn_setting_can_retry(reconn_setting)) return -2;
uint32_t delay = reconn_setting_calc_delay(reconn_setting);
hlogi("reconnect... cnt=%d, delay=%d", reconn_setting->cur_retry_cnt, reconn_setting->cur_delay);
loop_->setTimeout(delay, [this](TimerID timerID){
startConnect();
});
return 0;
}
// start thread-safe
void start() {
loop_->runInLoop(std::bind(&TcpClientEventLoopTmpl::startConnect, this));
}
bool isConnected() {
if (channel == NULL) return false;
return channel->isConnected();
}
// send thread-safe
int send(const void* data, int size) {
if (!isConnected()) return -1;
return channel->write(data, size);
}
int send(Buffer* buf) {
return send(buf->data(), buf->size());
}
int send(const std::string& str) {
return send(str.data(), str.size());
}
int withTLS(hssl_ctx_opt_t* opt = NULL) {
tls = true;
if (opt) {
if (tls_setting == NULL) {
HV_ALLOC_SIZEOF(tls_setting);
}
opt->endpoint = HSSL_CLIENT;
*tls_setting = *opt;
}
return 0;
}
void setConnectTimeout(int ms) {
connect_timeout = ms;
}
void setReconnect(reconn_setting_t* setting) {
if (setting == NULL) {
HV_FREE(reconn_setting);
return;
}
if (reconn_setting == NULL) {
HV_ALLOC_SIZEOF(reconn_setting);
}
*reconn_setting = *setting;
}
bool isReconnect() {
return reconn_setting && reconn_setting->cur_retry_cnt > 0;
}
void setUnpack(unpack_setting_t* setting) {
if (setting == NULL) {
HV_FREE(unpack_setting);
return;
}
if (unpack_setting == NULL) {
HV_ALLOC_SIZEOF(unpack_setting);
}
*unpack_setting = *setting;
}
public:
TSocketChannelPtr channel;
std::string remote_host;
int remote_port;
sockaddr_u remote_addr;
int connect_timeout;
bool tls;
hssl_ctx_opt_t* tls_setting;
reconn_setting_t* reconn_setting;
unpack_setting_t* unpack_setting;
// Callback
std::function<void(const TSocketChannelPtr&)> onConnection;
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// NOTE: Use Channel::isWriteComplete in onWriteComplete callback to determine whether all data has been written.
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
private:
EventLoopPtr loop_;
};
template<class TSocketChannel = SocketChannel>
class TcpClientTmpl : private EventLoopThread, public TcpClientEventLoopTmpl<TSocketChannel> {
public:
TcpClientTmpl(EventLoopPtr loop = NULL)
: EventLoopThread(loop)
, TcpClientEventLoopTmpl<TSocketChannel>(EventLoopThread::loop())
, is_loop_owner(loop == NULL)
{}
virtual ~TcpClientTmpl() {
stop(true);
}
const EventLoopPtr& loop() {
return EventLoopThread::loop();
}
// start thread-safe
void start(bool wait_threads_started = true) {
if (isRunning()) {
TcpClientEventLoopTmpl<TSocketChannel>::start();
} else {
EventLoopThread::start(wait_threads_started, [this]() {
TcpClientTmpl::startConnect();
return 0;
});
}
}
// stop thread-safe
void stop(bool wait_threads_stopped = true) {
TcpClientEventLoopTmpl<TSocketChannel>::closesocket();
if (is_loop_owner) {
EventLoopThread::stop(wait_threads_stopped);
}
}
private:
bool is_loop_owner;
};
typedef TcpClientTmpl<SocketChannel> TcpClient;
}
#endif // HV_TCP_CLIENT_HPP_

View File

@ -1,316 +0,0 @@
#ifndef HV_TCP_SERVER_HPP_
#define HV_TCP_SERVER_HPP_
#include "hsocket.h"
#include "hssl.h"
#include "hlog.h"
#include "EventLoopThreadPool.h"
#include "Channel.h"
namespace hv {
template<class TSocketChannel = SocketChannel>
class TcpServerEventLoopTmpl {
public:
typedef std::shared_ptr<TSocketChannel> TSocketChannelPtr;
TcpServerEventLoopTmpl(EventLoopPtr loop = NULL) {
acceptor_loop = loop ? loop : std::make_shared<EventLoop>();
port = 0;
listenfd = -1;
tls = false;
tls_setting = NULL;
unpack_setting = NULL;
max_connections = 0xFFFFFFFF;
load_balance = LB_RoundRobin;
}
virtual ~TcpServerEventLoopTmpl() {
HV_FREE(tls_setting);
HV_FREE(unpack_setting);
}
EventLoopPtr loop(int idx = -1) {
return worker_threads.loop(idx);
}
//@retval >=0 listenfd, <0 error
int createsocket(int port, const char* host = "0.0.0.0") {
listenfd = Listen(port, host);
if (listenfd < 0) return listenfd;
this->host = host;
this->port = port;
return listenfd;
}
// closesocket thread-safe
void closesocket() {
if (listenfd >= 0) {
hloop_t* loop = acceptor_loop->loop();
if (loop) {
hio_t* listenio = hio_get(loop, listenfd);
assert(listenio != NULL);
hio_close_async(listenio);
}
listenfd = -1;
}
}
void setMaxConnectionNum(uint32_t num) {
max_connections = num;
}
void setLoadBalance(load_balance_e lb) {
load_balance = lb;
}
// NOTE: totalThreadNum = 1 acceptor_thread + N worker_threads (N can be 0)
void setThreadNum(int num) {
worker_threads.setThreadNum(num);
}
int startAccept() {
if (listenfd < 0) {
listenfd = createsocket(port, host.c_str());
if (listenfd < 0) {
hloge("createsocket %s:%d return %d!\n", host.c_str(), port, listenfd);
return listenfd;
}
}
hloop_t* loop = acceptor_loop->loop();
if (loop == NULL) return -2;
hio_t* listenio = haccept(loop, listenfd, onAccept);
assert(listenio != NULL);
hevent_set_userdata(listenio, this);
if (tls) {
hio_enable_ssl(listenio);
if (tls_setting) {
int ret = hio_new_ssl_ctx(listenio, tls_setting);
if (ret != 0) {
hloge("new SSL_CTX failed: %d", ret);
closesocket();
return ret;
}
}
}
return 0;
}
int stopAccept() {
if (listenfd < 0) return -1;
hloop_t* loop = acceptor_loop->loop();
if (loop == NULL) return -2;
hio_t* listenio = hio_get(loop, listenfd);
assert(listenio != NULL);
return hio_del(listenio, HV_READ);
}
// start thread-safe
void start(bool wait_threads_started = true) {
if (worker_threads.threadNum() > 0) {
worker_threads.start(wait_threads_started);
}
acceptor_loop->runInLoop(std::bind(&TcpServerEventLoopTmpl::startAccept, this));
}
// stop thread-safe
void stop(bool wait_threads_stopped = true) {
closesocket();
if (worker_threads.threadNum() > 0) {
worker_threads.stop(wait_threads_stopped);
}
}
int withTLS(hssl_ctx_opt_t* opt = NULL) {
tls = true;
if (opt) {
if (tls_setting == NULL) {
HV_ALLOC_SIZEOF(tls_setting);
}
opt->endpoint = HSSL_SERVER;
*tls_setting = *opt;
}
return 0;
}
void setUnpack(unpack_setting_t* setting) {
if (setting == NULL) {
HV_FREE(unpack_setting);
return;
}
if (unpack_setting == NULL) {
HV_ALLOC_SIZEOF(unpack_setting);
}
*unpack_setting = *setting;
}
// channel
const TSocketChannelPtr& addChannel(hio_t* io) {
uint32_t id = hio_id(io);
auto channel = TSocketChannelPtr(new TSocketChannel(io));
std::lock_guard<std::mutex> locker(mutex_);
channels[id] = channel;
return channels[id];
}
TSocketChannelPtr getChannelById(uint32_t id) {
std::lock_guard<std::mutex> locker(mutex_);
auto iter = channels.find(id);
return iter != channels.end() ? iter->second : NULL;
}
void removeChannel(const TSocketChannelPtr& channel) {
uint32_t id = channel->id();
std::lock_guard<std::mutex> locker(mutex_);
channels.erase(id);
}
size_t connectionNum() {
std::lock_guard<std::mutex> locker(mutex_);
return channels.size();
}
int foreachChannel(std::function<void(const TSocketChannelPtr& channel)> fn) {
std::lock_guard<std::mutex> locker(mutex_);
for (auto& pair : channels) {
fn(pair.second);
}
return channels.size();
}
// broadcast thread-safe
int broadcast(const void* data, int size) {
return foreachChannel([data, size](const TSocketChannelPtr& channel) {
channel->write(data, size);
});
}
int broadcast(const std::string& str) {
return broadcast(str.data(), str.size());
}
private:
static void newConnEvent(hio_t* connio) {
TcpServerEventLoopTmpl* server = (TcpServerEventLoopTmpl*)hevent_userdata(connio);
if (server->connectionNum() >= server->max_connections) {
hlogw("over max_connections");
hio_close(connio);
return;
}
// NOTE: attach to worker loop
EventLoop* worker_loop = currentThreadEventLoop;
assert(worker_loop != NULL);
hio_attach(worker_loop->loop(), connio);
const TSocketChannelPtr& channel = server->addChannel(connio);
channel->status = SocketChannel::CONNECTED;
channel->onread = [server, &channel](Buffer* buf) {
if (server->onMessage) {
server->onMessage(channel, buf);
}
};
channel->onwrite = [server, &channel](Buffer* buf) {
if (server->onWriteComplete) {
server->onWriteComplete(channel, buf);
}
};
channel->onclose = [server, &channel]() {
EventLoop* worker_loop = currentThreadEventLoop;
assert(worker_loop != NULL);
--worker_loop->connectionNum;
channel->status = SocketChannel::CLOSED;
if (server->onConnection) {
server->onConnection(channel);
}
server->removeChannel(channel);
// NOTE: After removeChannel, channel may be destroyed,
// so in this lambda function, no code should be added below.
};
if (server->unpack_setting) {
channel->setUnpack(server->unpack_setting);
}
channel->startRead();
if (server->onConnection) {
server->onConnection(channel);
}
}
static void onAccept(hio_t* connio) {
TcpServerEventLoopTmpl* server = (TcpServerEventLoopTmpl*)hevent_userdata(connio);
// NOTE: detach from acceptor loop
hio_detach(connio);
EventLoopPtr worker_loop = server->worker_threads.nextLoop(server->load_balance);
if (worker_loop == NULL) {
worker_loop = server->acceptor_loop;
}
++worker_loop->connectionNum;
worker_loop->runInLoop(std::bind(&TcpServerEventLoopTmpl::newConnEvent, connio));
}
public:
std::string host;
int port;
int listenfd;
bool tls;
hssl_ctx_opt_t* tls_setting;
unpack_setting_t* unpack_setting;
// Callback
std::function<void(const TSocketChannelPtr&)> onConnection;
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// NOTE: Use Channel::isWriteComplete in onWriteComplete callback to determine whether all data has been written.
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
uint32_t max_connections;
load_balance_e load_balance;
private:
// id => TSocketChannelPtr
std::map<uint32_t, TSocketChannelPtr> channels; // GUAREDE_BY(mutex_)
std::mutex mutex_;
EventLoopPtr acceptor_loop;
EventLoopThreadPool worker_threads;
};
template<class TSocketChannel = SocketChannel>
class TcpServerTmpl : private EventLoopThread, public TcpServerEventLoopTmpl<TSocketChannel> {
public:
TcpServerTmpl(EventLoopPtr loop = NULL)
: EventLoopThread(loop)
, TcpServerEventLoopTmpl<TSocketChannel>(EventLoopThread::loop())
, is_loop_owner(loop == NULL)
{}
virtual ~TcpServerTmpl() {
stop(true);
}
const EventLoopPtr& loop(int idx = -1) {
return TcpServerEventLoopTmpl<TSocketChannel>::loop(idx);
}
// start thread-safe
void start(bool wait_threads_started = true) {
TcpServerEventLoopTmpl<TSocketChannel>::start(wait_threads_started);
EventLoopThread::start(wait_threads_started);
}
// stop thread-safe
void stop(bool wait_threads_stopped = true) {
if (is_loop_owner) {
EventLoopThread::stop(wait_threads_stopped);
}
TcpServerEventLoopTmpl<TSocketChannel>::stop(wait_threads_stopped);
}
private:
bool is_loop_owner;
};
typedef TcpServerTmpl<SocketChannel> TcpServer;
}
#endif // HV_TCP_SERVER_HPP_

View File

@ -1,67 +0,0 @@
#ifndef HV_THREAD_LOCAL_STORAGE_H_
#define HV_THREAD_LOCAL_STORAGE_H_
#include "hexport.h"
#include "hplatform.h"
#ifdef OS_WIN
#define hthread_key_t DWORD
#define INVALID_HTHREAD_KEY 0xFFFFFFFF
#define hthread_key_create(pkey) *pkey = TlsAlloc()
#define hthread_key_delete TlsFree
#define hthread_get_value TlsGetValue
#define hthread_set_value TlsSetValue
#else
#define hthread_key_t pthread_key_t
#define INVALID_HTHREAD_KEY 0xFFFFFFFF
#define hthread_key_create(pkey) pthread_key_create(pkey, NULL)
#define hthread_key_delete pthread_key_delete
#define hthread_get_value pthread_getspecific
#define hthread_set_value pthread_setspecific
#endif
#ifdef __cplusplus
namespace hv {
class HV_EXPORT ThreadLocalStorage {
public:
enum {
THREAD_NAME = 0,
EVENT_LOOP = 1,
MAX_NUM = 16,
};
ThreadLocalStorage() {
hthread_key_create(&key);
}
~ThreadLocalStorage() {
hthread_key_delete(key);
}
void set(void* val) {
hthread_set_value(key, val);
}
void* get() {
return hthread_get_value(key);
}
static void set(int idx, void* val);
static void* get(int idx);
static void setThreadName(const char* name);
static const char* threadName();
private:
hthread_key_t key;
static ThreadLocalStorage tls[MAX_NUM];
};
}
#endif
#endif // HV_THREAD_LOCAL_STORAGE_H_

View File

@ -1,191 +0,0 @@
#ifndef HV_UDP_CLIENT_HPP_
#define HV_UDP_CLIENT_HPP_
#include "hsocket.h"
#include "EventLoopThread.h"
#include "Channel.h"
namespace hv {
template<class TSocketChannel = SocketChannel>
class UdpClientEventLoopTmpl {
public:
typedef std::shared_ptr<TSocketChannel> TSocketChannelPtr;
UdpClientEventLoopTmpl(EventLoopPtr loop = NULL) {
loop_ = loop ? loop : std::make_shared<EventLoop>();
remote_port = 0;
#if WITH_KCP
enable_kcp = false;
#endif
}
virtual ~UdpClientEventLoopTmpl() {
}
const EventLoopPtr& loop() {
return loop_;
}
// NOTE: By default, not bind local port. If necessary, you can call bind() after createsocket().
// @retval >=0 sockfd, <0 error
int createsocket(int remote_port, const char* remote_host = "127.0.0.1") {
hio_t* io = hloop_create_udp_client(loop_->loop(), remote_host, remote_port);
if (io == NULL) return -1;
this->remote_host = remote_host;
this->remote_port = remote_port;
channel.reset(new TSocketChannel(io));
return channel->fd();
}
int bind(int local_port, const char* local_host = "0.0.0.0") {
if (channel == NULL || channel->isClosed()) {
return -1;
}
sockaddr_u local_addr;
memset(&local_addr, 0, sizeof(local_addr));
int ret = sockaddr_set_ipport(&local_addr, local_host, local_port);
if (ret != 0) {
return NABS(ret);
}
ret = ::bind(channel->fd(), &local_addr.sa, SOCKADDR_LEN(&local_addr));
if (ret != 0) {
perror("bind");
}
return ret;
}
// closesocket thread-safe
void closesocket() {
if (channel) {
channel->close(true);
}
}
int startRecv() {
if (channel == NULL || channel->isClosed()) {
int sockfd = createsocket(remote_port, remote_host.c_str());
if (sockfd < 0) {
hloge("createsocket %s:%d return %d!\n", remote_host.c_str(), remote_port, sockfd);
return sockfd;
}
}
if (channel == NULL || channel->isClosed()) {
return -1;
}
channel->onread = [this](Buffer* buf) {
if (onMessage) {
onMessage(channel, buf);
}
};
channel->onwrite = [this](Buffer* buf) {
if (onWriteComplete) {
onWriteComplete(channel, buf);
}
};
#if WITH_KCP
if (enable_kcp) {
hio_set_kcp(channel->io(), &kcp_setting);
}
#endif
return channel->startRead();
}
int stopRecv() {
if (channel == NULL) return -1;
return channel->stopRead();
}
// start thread-safe
void start() {
loop_->runInLoop(std::bind(&UdpClientEventLoopTmpl::startRecv, this));
}
// sendto thread-safe
int sendto(const void* data, int size, struct sockaddr* peeraddr = NULL) {
if (channel == NULL) return -1;
std::lock_guard<std::mutex> locker(sendto_mutex);
if (peeraddr) hio_set_peeraddr(channel->io(), peeraddr, SOCKADDR_LEN(peeraddr));
return channel->write(data, size);
}
int sendto(Buffer* buf, struct sockaddr* peeraddr = NULL) {
return sendto(buf->data(), buf->size(), peeraddr);
}
int sendto(const std::string& str, struct sockaddr* peeraddr = NULL) {
return sendto(str.data(), str.size(), peeraddr);
}
#if WITH_KCP
void setKcp(kcp_setting_t* setting) {
if (setting) {
enable_kcp = true;
kcp_setting = *setting;
} else {
enable_kcp = false;
}
}
#endif
public:
TSocketChannelPtr channel;
std::string remote_host;
int remote_port;
#if WITH_KCP
bool enable_kcp;
kcp_setting_t kcp_setting;
#endif
// Callback
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// NOTE: Use Channel::isWriteComplete in onWriteComplete callback to determine whether all data has been written.
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
private:
std::mutex sendto_mutex;
EventLoopPtr loop_;
};
template<class TSocketChannel = SocketChannel>
class UdpClientTmpl : private EventLoopThread, public UdpClientEventLoopTmpl<TSocketChannel> {
public:
UdpClientTmpl(EventLoopPtr loop = NULL)
: EventLoopThread(loop)
, UdpClientEventLoopTmpl<TSocketChannel>(EventLoopThread::loop())
, is_loop_owner(loop == NULL)
{}
virtual ~UdpClientTmpl() {
stop(true);
}
const EventLoopPtr& loop() {
return EventLoopThread::loop();
}
// start thread-safe
void start(bool wait_threads_started = true) {
if (isRunning()) {
UdpClientEventLoopTmpl<TSocketChannel>::start();
} else {
EventLoopThread::start(wait_threads_started, std::bind(&UdpClientTmpl::startRecv, this));
}
}
// stop thread-safe
void stop(bool wait_threads_stopped = true) {
UdpClientEventLoopTmpl<TSocketChannel>::closesocket();
if (is_loop_owner) {
EventLoopThread::stop(wait_threads_stopped);
}
}
private:
bool is_loop_owner;
};
typedef UdpClientTmpl<SocketChannel> UdpClient;
}
#endif // HV_UDP_CLIENT_HPP_

View File

@ -1,170 +0,0 @@
#ifndef HV_UDP_SERVER_HPP_
#define HV_UDP_SERVER_HPP_
#include "hsocket.h"
#include "EventLoopThreadPool.h"
#include "Channel.h"
namespace hv {
template<class TSocketChannel = SocketChannel>
class UdpServerEventLoopTmpl {
public:
typedef std::shared_ptr<TSocketChannel> TSocketChannelPtr;
UdpServerEventLoopTmpl(EventLoopPtr loop = NULL) {
loop_ = loop ? loop : std::make_shared<EventLoop>();
port = 0;
#if WITH_KCP
enable_kcp = false;
#endif
}
virtual ~UdpServerEventLoopTmpl() {
}
const EventLoopPtr& loop() {
return loop_;
}
//@retval >=0 bindfd, <0 error
int createsocket(int port, const char* host = "0.0.0.0") {
hio_t* io = hloop_create_udp_server(loop_->loop(), host, port);
if (io == NULL) return -1;
this->host = host;
this->port = port;
channel.reset(new TSocketChannel(io));
return channel->fd();
}
// closesocket thread-safe
void closesocket() {
if (channel) {
channel->close(true);
}
}
int startRecv() {
if (channel == NULL || channel->isClosed()) {
int bindfd = createsocket(port, host.c_str());
if (bindfd < 0) {
hloge("createsocket %s:%d return %d!\n", host.c_str(), port, bindfd);
return bindfd;
}
}
if (channel == NULL || channel->isClosed()) {
return -1;
}
channel->onread = [this](Buffer* buf) {
if (onMessage) {
onMessage(channel, buf);
}
};
channel->onwrite = [this](Buffer* buf) {
if (onWriteComplete) {
onWriteComplete(channel, buf);
}
};
#if WITH_KCP
if (enable_kcp) {
hio_set_kcp(channel->io(), &kcp_setting);
}
#endif
return channel->startRead();
}
int stopRecv() {
if (channel == NULL) return -1;
return channel->stopRead();
}
// start thread-safe
void start() {
loop_->runInLoop(std::bind(&UdpServerEventLoopTmpl::startRecv, this));
}
// sendto thread-safe
int sendto(const void* data, int size, struct sockaddr* peeraddr = NULL) {
if (channel == NULL) return -1;
std::lock_guard<std::mutex> locker(sendto_mutex);
if (peeraddr) hio_set_peeraddr(channel->io(), peeraddr, SOCKADDR_LEN(peeraddr));
return channel->write(data, size);
}
int sendto(Buffer* buf, struct sockaddr* peeraddr = NULL) {
return sendto(buf->data(), buf->size(), peeraddr);
}
int sendto(const std::string& str, struct sockaddr* peeraddr = NULL) {
return sendto(str.data(), str.size(), peeraddr);
}
#if WITH_KCP
void setKcp(kcp_setting_t* setting) {
if (setting) {
enable_kcp = true;
kcp_setting = *setting;
} else {
enable_kcp = false;
}
}
#endif
public:
std::string host;
int port;
TSocketChannelPtr channel;
#if WITH_KCP
bool enable_kcp;
kcp_setting_t kcp_setting;
#endif
// Callback
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// NOTE: Use Channel::isWriteComplete in onWriteComplete callback to determine whether all data has been written.
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
private:
std::mutex sendto_mutex;
EventLoopPtr loop_;
};
template<class TSocketChannel = SocketChannel>
class UdpServerTmpl : private EventLoopThread, public UdpServerEventLoopTmpl<TSocketChannel> {
public:
UdpServerTmpl(EventLoopPtr loop = NULL)
: EventLoopThread(loop)
, UdpServerEventLoopTmpl<TSocketChannel>(EventLoopThread::loop())
, is_loop_owner(loop == NULL)
{}
virtual ~UdpServerTmpl() {
stop(true);
}
const EventLoopPtr& loop() {
return EventLoopThread::loop();
}
// start thread-safe
void start(bool wait_threads_started = true) {
if (isRunning()) {
UdpServerEventLoopTmpl<TSocketChannel>::start();
} else {
EventLoopThread::start(wait_threads_started, std::bind(&UdpServerTmpl::startRecv, this));
}
}
// stop thread-safe
void stop(bool wait_threads_stopped = true) {
UdpServerEventLoopTmpl<TSocketChannel>::closesocket();
if (is_loop_owner) {
EventLoopThread::stop(wait_threads_stopped);
}
}
private:
bool is_loop_owner;
};
typedef UdpServerTmpl<SocketChannel> UdpServer;
}
#endif // HV_UDP_SERVER_HPP_

View File

@ -1,55 +0,0 @@
#ifndef HV_WEBSOCKET_CHANNEL_H_
#define HV_WEBSOCKET_CHANNEL_H_
#include <mutex>
#include "Channel.h"
#include "wsdef.h"
#include "hmath.h"
namespace hv {
class HV_EXPORT WebSocketChannel : public SocketChannel {
public:
ws_session_type type;
WebSocketChannel(hio_t* io, ws_session_type type = WS_CLIENT)
: SocketChannel(io)
, type(type)
, opcode(WS_OPCODE_CLOSE)
{}
~WebSocketChannel() {}
// isConnected, send, close
int send(const std::string& msg, enum ws_opcode opcode = WS_OPCODE_TEXT, bool fin = true) {
return send(msg.c_str(), msg.size(), opcode, fin);
}
int send(const char* buf, int len, enum ws_opcode opcode = WS_OPCODE_BINARY, bool fin = true);
// websocket fragment
int send(const char* buf, int len, int fragment, enum ws_opcode opcode = WS_OPCODE_BINARY);
int sendPing();
int sendPong();
int close() {
return SocketChannel::close(type == WS_SERVER);
}
protected:
int sendFrame(const char* buf, int len, enum ws_opcode opcode = WS_OPCODE_BINARY, bool fin = true);
public:
enum ws_opcode opcode;
private:
Buffer sendbuf_;
std::mutex mutex_;
};
}
typedef std::shared_ptr<hv::WebSocketChannel> WebSocketChannelPtr;
#endif // HV_WEBSOCKET_CHANNEL_H_

View File

@ -1,71 +0,0 @@
#ifndef HV_WEBSOCKET_CLIENT_H_
#define HV_WEBSOCKET_CLIENT_H_
/*
* @demo examples/websocket_client_test.cpp
*/
#include "hexport.h"
#include "TcpClient.h"
#include "WebSocketChannel.h"
#include "HttpParser.h"
#include "WebSocketParser.h"
namespace hv {
class HV_EXPORT WebSocketClient : public TcpClientTmpl<WebSocketChannel> {
public:
std::string url;
std::function<void()> onopen;
std::function<void()> onclose;
std::function<void(const std::string& msg)> onmessage;
// PATCH: onmessage not given opcode
enum ws_opcode opcode() { return channel ? channel->opcode : WS_OPCODE_CLOSE; }
WebSocketClient(EventLoopPtr loop = NULL);
virtual ~WebSocketClient();
// url = ws://ip:port/path
// url = wss://ip:port/path
int open(const char* url, const http_headers& headers = DefaultHeaders);
int close();
int send(const std::string& msg);
int send(const char* buf, int len, enum ws_opcode opcode = WS_OPCODE_BINARY);
// setConnectTimeout / setPingInterval / setReconnect
void setPingInterval(int ms) {
ping_interval = ms;
}
// NOTE: call before open
void setHttpRequest(const HttpRequestPtr& req) {
http_req_ = req;
}
// NOTE: call when onopen
const HttpResponsePtr& getHttpResponse() {
return http_resp_;
}
private:
enum State {
CONNECTING,
CONNECTED,
WS_UPGRADING,
WS_OPENED,
WS_CLOSED,
} state;
HttpParserPtr http_parser_;
HttpRequestPtr http_req_;
HttpResponsePtr http_resp_;
WebSocketParserPtr ws_parser_;
// ping/pong
int ping_interval;
int ping_cnt;
};
}
#endif // HV_WEBSOCKET_CLIENT_H_

View File

@ -1,35 +0,0 @@
#ifndef HV_WEBSOCKET_PARSER_H_
#define HV_WEBSOCKET_PARSER_H_
#include "hexport.h"
#include <string>
#include <memory>
#include <functional>
enum websocket_parser_state {
WS_FRAME_BEGIN,
WS_FRAME_HEADER,
WS_FRAME_BODY,
WS_FRAME_END,
WS_FRAME_FIN,
};
struct websocket_parser;
class HV_EXPORT WebSocketParser {
public:
websocket_parser* parser;
websocket_parser_state state;
int opcode;
std::string message;
std::function<void(int opcode, const std::string& msg)> onMessage;
WebSocketParser();
~WebSocketParser();
int FeedRecvData(const char* data, size_t len);
};
typedef std::shared_ptr<WebSocketParser> WebSocketParserPtr;
#endif // HV_WEBSOCKET_PARSER_H_

View File

@ -1,46 +0,0 @@
#ifndef HV_WEBSOCKET_SERVER_H_
#define HV_WEBSOCKET_SERVER_H_
/*
* @demo examples/websocket_server_test.cpp
*/
#include "HttpServer.h"
#include "WebSocketChannel.h"
#define websocket_server_t http_server_t
#define websocket_server_run http_server_run
#define websocket_server_stop http_server_stop
namespace hv {
struct WebSocketService {
std::function<void(const WebSocketChannelPtr&, const HttpRequestPtr&)> onopen;
std::function<void(const WebSocketChannelPtr&, const std::string&)> onmessage;
std::function<void(const WebSocketChannelPtr&)> onclose;
int ping_interval;
WebSocketService() : ping_interval(0) {}
void setPingInterval(int ms) {
ping_interval = ms;
}
};
class WebSocketServer : public HttpServer {
public:
WebSocketServer(WebSocketService* service = NULL)
: HttpServer()
{
this->ws = service;
}
~WebSocketServer() { stop(); }
void registerWebSocketService(WebSocketService* service) {
this->ws = service;
}
};
}
#endif // HV_WEBSOCKET_SERVER_H_

View File

@ -1,192 +0,0 @@
#ifndef HV_AXIOS_H_
#include "json.hpp"
#include "requests.h"
/*
* Inspired by js axios
*
* @code
#include "axios.h"
int main() {
const char* strReq = R"(
{
"method": "POST",
"url": "http://127.0.0.1:8080/echo",
"timeout": 10,
"params": {
"page_no": "1",
"page_size": "10"
},
"headers": {
"Content-Type": "application/json"
},
"body": {
"app_id": "123456",
"app_secret": "abcdefg"
}
}
)";
// sync
auto resp = axios::axios(strReq);
if (resp == NULL) {
printf("request failed!\n");
} else {
printf("%s\n", resp->body.c_str());
}
// async
int finished = 0;
axios::axios(strReq, [&finished](const HttpResponsePtr& resp) {
if (resp == NULL) {
printf("request failed!\n");
} else {
printf("%s\n", resp->body.c_str());
}
finished = 1;
});
// wait async finished
while (!finished) hv_sleep(1);
return 0;
}
**/
using nlohmann::json;
using requests::Request;
using requests::Response;
using requests::ResponseCallback;
namespace axios {
HV_INLINE Request newRequestFromJson(const json& jreq) {
Request req(new HttpRequest);
// url
if (jreq.contains("url")) {
req->url = jreq["url"];
}
// params
if (jreq.contains("params")) {
req->query_params = jreq["params"].get<hv::QueryParams>();
}
// headers
if (jreq.contains("headers")) {
req->headers = jreq["headers"].get<http_headers>();
}
// body/data
const char* body_field = nullptr;
if (jreq.contains("body")) {
body_field = "body";
} else if (jreq.contains("data")) {
body_field = "data";
}
if (body_field) {
const json& jbody = jreq[body_field];
if (jbody.is_object() || jbody.is_array()) {
req->json = jbody;
} else if (jbody.is_string()) {
req->body = jbody;
}
}
// method
if (jreq.contains("method")) {
std::string method = jreq["method"];
req->method = http_method_enum(method.c_str());
} else if (body_field) {
req->method = HTTP_POST;
} else {
req->method = HTTP_GET;
}
// timeout
if (jreq.contains("timeout")) {
req->timeout = jreq["timeout"];
}
return req;
}
HV_INLINE Request newRequestFromJsonString(const char* req_str) {
return newRequestFromJson(json::parse(req_str));
}
// sync
HV_INLINE Response axios(const json& jreq, http_method method = HTTP_GET, const char* url = nullptr) {
auto req = newRequestFromJson(jreq);
if (method != HTTP_GET) {
req->method = method;
}
if (url) {
req->url = url;
}
return req ? requests::request(req) : nullptr;
}
HV_INLINE Response axios(const char* req_str, http_method method = HTTP_GET, const char* url = nullptr) {
return req_str ? axios(json::parse(req_str), method, url)
: requests::request(method, url);
}
HV_INLINE Response head(const char* url, const json& jreq) {
return axios(jreq, HTTP_HEAD, url);
}
HV_INLINE Response head(const char* url, const char* req_str = nullptr) {
return axios(req_str, HTTP_HEAD, url);
}
HV_INLINE Response get(const char* url, const json& jreq) {
return axios(jreq, HTTP_GET, url);
}
HV_INLINE Response get(const char* url, const char* req_str = nullptr) {
return axios(req_str, HTTP_GET, url);
}
HV_INLINE Response post(const char* url, const json& jreq) {
return axios(jreq, HTTP_POST, url);
}
HV_INLINE Response post(const char* url, const char* req_str = nullptr) {
return axios(req_str, HTTP_POST, url);
}
HV_INLINE Response put(const char* url, const json& jreq) {
return axios(jreq, HTTP_PUT, url);
}
HV_INLINE Response put(const char* url, const char* req_str = nullptr) {
return axios(req_str, HTTP_PUT, url);
}
HV_INLINE Response patch(const char* url, const json& jreq) {
return axios(jreq, HTTP_PATCH, url);
}
HV_INLINE Response patch(const char* url, const char* req_str = nullptr) {
return axios(req_str, HTTP_PATCH, url);
}
HV_INLINE Response Delete(const char* url, const json& jreq) {
return axios(jreq, HTTP_DELETE, url);
}
HV_INLINE Response Delete(const char* url, const char* req_str = nullptr) {
return axios(req_str, HTTP_DELETE, url);
}
// async
HV_INLINE int axios(const json& jreq, ResponseCallback resp_cb) {
auto req = newRequestFromJson(jreq);
return req ? requests::async(req, std::move(resp_cb)) : -1;
}
HV_INLINE int axios(const char* req_str, ResponseCallback resp_cb) {
return axios(json::parse(req_str), std::move(resp_cb));
}
}
#endif // HV_AXIOS_H_

View File

@ -1,46 +0,0 @@
#ifndef HV_BASE64_H_
#define HV_BASE64_H_
#include "hexport.h"
#define BASE64_ENCODE_OUT_SIZE(s) (((s) + 2) / 3 * 4)
#define BASE64_DECODE_OUT_SIZE(s) (((s)) / 4 * 3)
BEGIN_EXTERN_C
// @return encoded size
HV_EXPORT int hv_base64_encode(const unsigned char *in, unsigned int inlen, char *out);
// @return decoded size
HV_EXPORT int hv_base64_decode(const char *in, unsigned int inlen, unsigned char *out);
END_EXTERN_C
#ifdef __cplusplus
#include <string.h>
#include <string>
namespace hv {
HV_INLINE std::string Base64Encode(const unsigned char* data, unsigned int len) {
int encoded_size = BASE64_ENCODE_OUT_SIZE(len);
std::string encoded_str(encoded_size + 1, 0);
encoded_size = hv_base64_encode(data, len, (char*)encoded_str.data());
encoded_str.resize(encoded_size);
return encoded_str;
}
HV_INLINE std::string Base64Decode(const char* str, unsigned int len = 0) {
if (len == 0) len = strlen(str);
int decoded_size = BASE64_DECODE_OUT_SIZE(len);
std::string decoded_buf(decoded_size + 1, 0);
decoded_size = hv_base64_decode(str, len, (unsigned char*)decoded_buf.data());
decoded_buf.resize(decoded_size);
return decoded_buf;
}
}
#endif
#endif // HV_BASE64_H_

View File

@ -1,49 +0,0 @@
#ifndef HV_ASYNC_H_
#define HV_ASYNC_H_
#include "hexport.h"
#include "hthreadpool.h"
#include "singleton.h"
namespace hv {
class HV_EXPORT GlobalThreadPool : public HThreadPool {
SINGLETON_DECL(GlobalThreadPool)
protected:
GlobalThreadPool() : HThreadPool() {}
~GlobalThreadPool() {}
};
/*
* return a future, calling future.get() will wait task done and return RetType.
* async(fn, args...)
* async(std::bind(&Class::mem_fn, &obj))
* async(std::mem_fn(&Class::mem_fn, &obj))
*
*/
template<class Fn, class... Args>
auto async(Fn&& fn, Args&&... args) -> std::future<decltype(fn(args...))> {
return GlobalThreadPool::instance()->commit(std::forward<Fn>(fn), std::forward<Args>(args)...);
}
class async {
public:
static void startup(int min_threads = DEFAULT_THREAD_POOL_MIN_THREAD_NUM,
int max_threads = DEFAULT_THREAD_POOL_MAX_THREAD_NUM,
int max_idle_ms = DEFAULT_THREAD_POOL_MAX_IDLE_TIME) {
GlobalThreadPool* gtp = GlobalThreadPool::instance();
if (gtp->isStarted()) return;
gtp->setMinThreadNum(min_threads);
gtp->setMaxThreadNum(max_threads);
gtp->setMaxIdleTime(max_idle_ms);
gtp->start();
}
static void cleanup() {
GlobalThreadPool::exitInstance();
}
};
} // end namespace hv
#endif // HV_ASYNC_H_

View File

@ -1,130 +0,0 @@
#ifndef HV_ATOMIC_H_
#define HV_ATOMIC_H_
#ifdef __cplusplus
// c++11
#include <atomic>
using std::atomic_flag;
using std::atomic_long;
#define ATOMIC_FLAG_TEST_AND_SET(p) ((p)->test_and_set())
#define ATOMIC_FLAG_CLEAR(p) ((p)->clear())
#else
#include "hplatform.h" // for HAVE_STDATOMIC_H
#if HAVE_STDATOMIC_H
// c11
#include <stdatomic.h>
#define ATOMIC_FLAG_TEST_AND_SET atomic_flag_test_and_set
#define ATOMIC_FLAG_CLEAR atomic_flag_clear
#define ATOMIC_ADD atomic_fetch_add
#define ATOMIC_SUB atomic_fetch_sub
#define ATOMIC_INC(p) ATOMIC_ADD(p, 1)
#define ATOMIC_DEC(p) ATOMIC_SUB(p, 1)
#else
typedef volatile bool atomic_bool;
typedef volatile char atomic_char;
typedef volatile unsigned char atomic_uchar;
typedef volatile short atomic_short;
typedef volatile unsigned short atomic_ushort;
typedef volatile int atomic_int;
typedef volatile unsigned int atomic_uint;
typedef volatile long atomic_long;
typedef volatile unsigned long atomic_ulong;
typedef volatile long long atomic_llong;
typedef volatile unsigned long long atomic_ullong;
typedef volatile size_t atomic_size_t;
typedef struct atomic_flag { atomic_bool _Value; } atomic_flag;
#ifdef _WIN32
#define ATOMIC_FLAG_TEST_AND_SET atomic_flag_test_and_set
static inline bool atomic_flag_test_and_set(atomic_flag* p) {
// return InterlockedIncrement((LONG*)&p->_Value, 1);
return InterlockedCompareExchange((LONG*)&p->_Value, 1, 0);
}
#define ATOMIC_ADD InterlockedAdd
#define ATOMIC_SUB(p, n) InterlockedAdd(p, -n)
#define ATOMIC_INC InterlockedIncrement
#define ATOMIC_DEC InterlockedDecrement
#elif defined(__GNUC__)
#define ATOMIC_FLAG_TEST_AND_SET atomic_flag_test_and_set
static inline bool atomic_flag_test_and_set(atomic_flag* p) {
return !__sync_bool_compare_and_swap(&p->_Value, 0, 1);
}
#define ATOMIC_ADD __sync_fetch_and_add
#define ATOMIC_SUB __sync_fetch_and_sub
#define ATOMIC_INC(p) ATOMIC_ADD(p, 1)
#define ATOMIC_DEC(p) ATOMIC_SUB(p, 1)
#endif
#endif // HAVE_STDATOMIC_H
#endif // __cplusplus
#ifndef ATOMIC_FLAG_INIT
#define ATOMIC_FLAG_INIT { 0 }
#endif
#ifndef ATOMIC_VAR_INIT
#define ATOMIC_VAR_INIT(value) (value)
#endif
#ifndef ATOMIC_FLAG_TEST_AND_SET
#define ATOMIC_FLAG_TEST_AND_SET atomic_flag_test_and_set
static inline bool atomic_flag_test_and_set(atomic_flag* p) {
bool ret = p->_Value;
p->_Value = 1;
return ret;
}
#endif
#ifndef ATOMIC_FLAG_CLEAR
#define ATOMIC_FLAG_CLEAR atomic_flag_clear
static inline void atomic_flag_clear(atomic_flag* p) {
p->_Value = 0;
}
#endif
#ifndef ATOMIC_ADD
#define ATOMIC_ADD(p, n) (*(p) += (n))
#endif
#ifndef ATOMIC_SUB
#define ATOMIC_SUB(p, n) (*(p) -= (n))
#endif
#ifndef ATOMIC_INC
#define ATOMIC_INC(p) ((*(p))++)
#endif
#ifndef ATOMIC_DEC
#define ATOMIC_DEC(p) ((*(p))--)
#endif
typedef atomic_flag hatomic_flag_t;
#define HATOMIC_FLAG_INIT ATOMIC_FLAG_INIT
#define hatomic_flag_test_and_set ATOMIC_FLAG_TEST_AND_SET
#define hatomic_flag_clear ATOMIC_FLAG_CLEAR
typedef atomic_long hatomic_t;
#define HATOMIC_VAR_INIT ATOMIC_VAR_INIT
#define hatomic_add ATOMIC_ADD
#define hatomic_sub ATOMIC_SUB
#define hatomic_inc ATOMIC_INC
#define hatomic_dec ATOMIC_DEC
#endif // HV_ATOMIC_H_

View File

@ -1,143 +0,0 @@
#ifndef HV_BASE_H_
#define HV_BASE_H_
#include "hexport.h"
#include "hplatform.h" // for bool
#include "hdef.h" // for printd
BEGIN_EXTERN_C
//--------------------alloc/free---------------------------
HV_EXPORT void* hv_malloc(size_t size);
HV_EXPORT void* hv_realloc(void* oldptr, size_t newsize, size_t oldsize);
HV_EXPORT void* hv_calloc(size_t nmemb, size_t size);
HV_EXPORT void* hv_zalloc(size_t size);
HV_EXPORT void hv_free(void* ptr);
#define HV_ALLOC(ptr, size)\
do {\
*(void**)&(ptr) = hv_zalloc(size);\
printd("alloc(%p, size=%llu)\tat [%s:%d:%s]\n", ptr, (unsigned long long)size, __FILE__, __LINE__, __FUNCTION__);\
} while(0)
#define HV_ALLOC_SIZEOF(ptr) HV_ALLOC(ptr, sizeof(*(ptr)))
#define HV_FREE(ptr)\
do {\
if (ptr) {\
hv_free(ptr);\
printd("free( %p )\tat [%s:%d:%s]\n", ptr, __FILE__, __LINE__, __FUNCTION__);\
ptr = NULL;\
}\
} while(0)
#define STACK_OR_HEAP_ALLOC(ptr, size, stack_size)\
unsigned char _stackbuf_[stack_size] = { 0 };\
if ((size) > (stack_size)) {\
HV_ALLOC(ptr, size);\
} else {\
*(unsigned char**)&(ptr) = _stackbuf_;\
}
#define STACK_OR_HEAP_FREE(ptr)\
if ((unsigned char*)(ptr) != _stackbuf_) {\
HV_FREE(ptr);\
}
#define HV_DEFAULT_STACKBUF_SIZE 1024
#define HV_STACK_ALLOC(ptr, size) STACK_OR_HEAP_ALLOC(ptr, size, HV_DEFAULT_STACKBUF_SIZE)
#define HV_STACK_FREE(ptr) STACK_OR_HEAP_FREE(ptr)
HV_EXPORT long hv_alloc_cnt();
HV_EXPORT long hv_free_cnt();
HV_INLINE void hv_memcheck(void) {
printf("Memcheck => alloc:%ld free:%ld\n", hv_alloc_cnt(), hv_free_cnt());
}
#define HV_MEMCHECK atexit(hv_memcheck);
//--------------------string-------------------------------
HV_EXPORT char* hv_strupper(char* str);
HV_EXPORT char* hv_strlower(char* str);
HV_EXPORT char* hv_strreverse(char* str);
HV_EXPORT bool hv_strstartswith(const char* str, const char* start);
HV_EXPORT bool hv_strendswith(const char* str, const char* end);
HV_EXPORT bool hv_strcontains(const char* str, const char* sub);
// strncpy n = sizeof(dest_buf)-1
// hv_strncpy n = sizeof(dest_buf)
HV_EXPORT char* hv_strncpy(char* dest, const char* src, size_t n);
// strncat n = sizeof(dest_buf)-1-strlen(dest)
// hv_strncpy n = sizeof(dest_buf)
HV_EXPORT char* hv_strncat(char* dest, const char* src, size_t n);
#if !HAVE_STRLCPY
#define strlcpy hv_strncpy
#endif
#if !HAVE_STRLCAT
#define strlcat hv_strncat
#endif
HV_EXPORT char* hv_strnchr(const char* s, char c, size_t n);
#define hv_strrchr_dot(str) strrchr(str, '.')
HV_EXPORT char* hv_strrchr_dir(const char* filepath);
// basename
HV_EXPORT const char* hv_basename(const char* filepath);
HV_EXPORT const char* hv_suffixname(const char* filename);
// mkdir -p
HV_EXPORT int hv_mkdir_p(const char* dir);
// rmdir -p
HV_EXPORT int hv_rmdir_p(const char* dir);
// path
HV_EXPORT bool hv_exists(const char* path);
HV_EXPORT bool hv_isdir(const char* path);
HV_EXPORT bool hv_isfile(const char* path);
HV_EXPORT bool hv_islink(const char* path);
HV_EXPORT size_t hv_filesize(const char* filepath);
HV_EXPORT char* get_executable_path(char* buf, int size);
HV_EXPORT char* get_executable_dir(char* buf, int size);
HV_EXPORT char* get_executable_file(char* buf, int size);
HV_EXPORT char* get_run_dir(char* buf, int size);
// random
HV_EXPORT int hv_rand(int min, int max);
HV_EXPORT char* hv_random_string(char *buf, int len);
// 1 y on yes true enable => true
HV_EXPORT bool hv_getboolean(const char* str);
// 1T2G3M4K5B => ?B
HV_EXPORT size_t hv_parse_size(const char* str);
// 1w2d3h4m5s => ?s
HV_EXPORT time_t hv_parse_time(const char* str);
// scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
typedef enum {
HV_URL_SCHEME,
HV_URL_USERNAME,
HV_URL_PASSWORD,
HV_URL_HOST,
HV_URL_PORT,
HV_URL_PATH,
HV_URL_QUERY,
HV_URL_FRAGMENT,
HV_URL_FIELD_NUM,
} hurl_field_e;
typedef struct hurl_s {
struct {
unsigned short off;
unsigned short len;
} fields[HV_URL_FIELD_NUM];
unsigned short port;
} hurl_t;
HV_EXPORT int hv_parse_url(hurl_t* stURL, const char* strURL);
END_EXTERN_C
#endif // HV_BASE_H_

View File

@ -1,257 +0,0 @@
#ifndef HV_BUF_H_
#define HV_BUF_H_
#include "hdef.h" // for MAX
#include "hbase.h" // for HV_ALLOC, HV_FREE
typedef struct hbuf_s {
char* base;
size_t len;
#ifdef __cplusplus
hbuf_s() {
base = NULL;
len = 0;
}
hbuf_s(void* data, size_t len) {
this->base = (char*)data;
this->len = len;
}
#endif
} hbuf_t;
typedef struct offset_buf_s {
char* base;
size_t len;
size_t offset;
#ifdef __cplusplus
offset_buf_s() {
base = NULL;
len = 0;
offset = 0;
}
offset_buf_s(void* data, size_t len) {
this->base = (char*)data;
this->len = len;
offset = 0;
}
#endif
} offset_buf_t;
typedef struct fifo_buf_s {
char* base;
size_t len;
size_t head;
size_t tail;
#ifdef __cplusplus
fifo_buf_s() {
base = NULL;
len = 0;
head = tail = 0;
}
fifo_buf_s(void* data, size_t len) {
this->base = (char*)data;
this->len = len;
head = tail = 0;
}
#endif
} fifo_buf_t;
#ifdef __cplusplus
class HBuf : public hbuf_t {
public:
HBuf() : hbuf_t() {
cleanup_ = false;
}
HBuf(void* data, size_t len) : hbuf_t(data, len) {
cleanup_ = false;
}
HBuf(size_t cap) { resize(cap); }
virtual ~HBuf() {
cleanup();
}
void* data() { return base; }
size_t size() { return len; }
bool isNull() { return base == NULL || len == 0; }
void cleanup() {
if (cleanup_) {
HV_FREE(base);
len = 0;
cleanup_ = false;
}
}
void resize(size_t cap) {
if (cap == len) return;
if (base == NULL) {
HV_ALLOC(base, cap);
}
else {
base = (char*)hv_realloc(base, cap, len);
}
len = cap;
cleanup_ = true;
}
void copy(void* data, size_t len) {
resize(len);
memcpy(base, data, len);
}
void copy(hbuf_t* buf) {
copy(buf->base, buf->len);
}
private:
bool cleanup_;
};
// VL: Variable-Length
class HVLBuf : public HBuf {
public:
HVLBuf() : HBuf() {_offset = _size = 0;}
HVLBuf(void* data, size_t len) : HBuf(data, len) {_offset = 0; _size = len;}
HVLBuf(size_t cap) : HBuf(cap) {_offset = _size = 0;}
virtual ~HVLBuf() {}
char* data() { return base + _offset; }
size_t size() { return _size; }
void push_front(void* ptr, size_t len) {
if (len > this->len - _size) {
size_t newsize = MAX(this->len, len)*2;
resize(newsize);
}
if (_offset < len) {
// move => end
memmove(base+this->len-_size, data(), _size);
_offset = this->len-_size;
}
memcpy(data()-len, ptr, len);
_offset -= len;
_size += len;
}
void push_back(void* ptr, size_t len) {
if (len > this->len - _size) {
size_t newsize = MAX(this->len, len)*2;
resize(newsize);
}
else if (len > this->len - _offset - _size) {
// move => start
memmove(base, data(), _size);
_offset = 0;
}
memcpy(data()+_size, ptr, len);
_size += len;
}
void pop_front(void* ptr, size_t len) {
if (len <= _size) {
if (ptr) {
memcpy(ptr, data(), len);
}
_offset += len;
if (_offset >= this->len) _offset = 0;
_size -= len;
}
}
void pop_back(void* ptr, size_t len) {
if (len <= _size) {
if (ptr) {
memcpy(ptr, data()+_size-len, len);
}
_size -= len;
}
}
void clear() {
_offset = _size = 0;
}
void prepend(void* ptr, size_t len) {
push_front(ptr, len);
}
void append(void* ptr, size_t len) {
push_back(ptr, len);
}
void insert(void* ptr, size_t len) {
push_back(ptr, len);
}
void remove(size_t len) {
pop_front(NULL, len);
}
private:
size_t _offset;
size_t _size;
};
class HRingBuf : public HBuf {
public:
HRingBuf() : HBuf() {_head = _tail = _size = 0;}
HRingBuf(size_t cap) : HBuf(cap) {_head = _tail = _size = 0;}
virtual ~HRingBuf() {}
char* alloc(size_t len) {
char* ret = NULL;
if (_head < _tail || _size == 0) {
// [_tail, this->len) && [0, _head)
if (this->len - _tail >= len) {
ret = base + _tail;
_tail += len;
if (_tail == this->len) _tail = 0;
}
else if (_head >= len) {
ret = base;
_tail = len;
}
}
else {
// [_tail, _head)
if (_head - _tail >= len) {
ret = base + _tail;
_tail += len;
}
}
_size += ret ? len : 0;
return ret;
}
void free(size_t len) {
_size -= len;
if (len <= this->len - _head) {
_head += len;
if (_head == this->len) _head = 0;
}
else {
_head = len;
}
}
void clear() {_head = _tail = _size = 0;}
size_t size() {return _size;}
private:
size_t _head;
size_t _tail;
size_t _size;
};
#endif
#endif // HV_BUF_H_

View File

@ -1,101 +0,0 @@
#ifndef HV_CONFIG_H_
#define HV_CONFIG_H_
#ifndef HAVE_STDBOOL_H
#define HAVE_STDBOOL_H 1
#endif
#ifndef HAVE_STDINT_H
#define HAVE_STDINT_H 1
#endif
#ifndef HAVE_STDATOMIC_H
#define HAVE_STDATOMIC_H 0
#endif
#ifndef HAVE_SYS_TYPES_H
#define HAVE_SYS_TYPES_H 1
#endif
#ifndef HAVE_SYS_STAT_H
#define HAVE_SYS_STAT_H 1
#endif
#ifndef HAVE_SYS_TIME_H
#define HAVE_SYS_TIME_H 0
#endif
#ifndef HAVE_FCNTL_H
#define HAVE_FCNTL_H 1
#endif
#ifndef HAVE_PTHREAD_H
#define HAVE_PTHREAD_H 0
#endif
#ifndef HAVE_ENDIAN_H
#define HAVE_ENDIAN_H 0
#endif
#ifndef HAVE_SYS_ENDIAN_H
#define HAVE_SYS_ENDIAN_H 0
#endif
#ifndef HAVE_GETTID
#define HAVE_GETTID 0
#endif
#ifndef HAVE_STRLCPY
#define HAVE_STRLCPY 0
#endif
#ifndef HAVE_STRLCAT
#define HAVE_STRLCAT 0
#endif
#ifndef HAVE_CLOCK_GETTIME
#define HAVE_CLOCK_GETTIME 0
#endif
#ifndef HAVE_GETTIMEOFDAY
#define HAVE_GETTIMEOFDAY 0
#endif
#ifndef HAVE_PTHREAD_SPIN_LOCK
#define HAVE_PTHREAD_SPIN_LOCK 0
#endif
#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
#define HAVE_PTHREAD_MUTEX_TIMEDLOCK 0
#endif
#ifndef HAVE_SEM_TIMEDWAIT
#define HAVE_SEM_TIMEDWAIT 0
#endif
#ifndef HAVE_PIPE
#define HAVE_PIPE 0
#endif
#ifndef HAVE_SOCKETPAIR
#define HAVE_SOCKETPAIR 0
#endif
#ifndef HAVE_EVENTFD
#define HAVE_EVENTFD 0
#endif
#ifndef HAVE_SETPROCTITLE
#define HAVE_SETPROCTITLE 0
#endif
/* #undef WITH_OPENSSL */
/* #undef WITH_GNUTLS */
/* #undef WITH_MBEDTLS */
/* #undef ENABLE_UDS */
/* #undef USE_MULTIMAP */
/* #undef WITH_KCP */
#endif // HV_CONFIG_H_

View File

@ -1,271 +0,0 @@
#ifndef HV_DEF_H_
#define HV_DEF_H_
#include "hplatform.h"
#ifndef ABS
#define ABS(n) ((n) > 0 ? (n) : -(n))
#endif
#ifndef NABS
#define NABS(n) ((n) < 0 ? (n) : -(n))
#endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
#endif
#ifndef BITSET
#define BITSET(p, n) (*(p) |= (1u << (n)))
#endif
#ifndef BITCLR
#define BITCLR(p, n) (*(p) &= ~(1u << (n)))
#endif
#ifndef BITGET
#define BITGET(i, n) ((i) & (1u << (n)))
#endif
/*
#ifndef CR
#define CR '\r'
#endif
#ifndef LF
#define LF '\n'
#endif
#ifndef CRLF
#define CRLF "\r\n"
#endif
*/
#define FLOAT_PRECISION 1e-6
#define FLOAT_EQUAL_ZERO(f) (ABS(f) < FLOAT_PRECISION)
#ifndef INFINITE
#define INFINITE (uint32_t)-1
#endif
/*
ASCII:
[0, 0x20) control-charaters
[0x20, 0x7F) printable-charaters
0x0A => LF
0x0D => CR
0x20 => SPACE
0x7F => DEL
[0x09, 0x0D] => \t\n\v\f\r
[0x30, 0x39] => 0~9
[0x41, 0x5A] => A~Z
[0x61, 0x7A] => a~z
*/
#ifndef IS_ALPHA
#define IS_ALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
#endif
// NOTE: IS_NUM conflicts with mysql.h
#ifndef IS_DIGIT
#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
#endif
#ifndef IS_ALPHANUM
#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_DIGIT(c))
#endif
#ifndef IS_CNTRL
#define IS_CNTRL(c) ((c) >= 0 && (c) < 0x20)
#endif
#ifndef IS_GRAPH
#define IS_GRAPH(c) ((c) >= 0x20 && (c) < 0x7F)
#endif
#ifndef IS_HEX
#define IS_HEX(c) (IS_DIGIT(c) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
#endif
#ifndef IS_LOWER
#define IS_LOWER(c) (((c) >= 'a' && (c) <= 'z'))
#endif
#ifndef IS_UPPER
#define IS_UPPER(c) (((c) >= 'A' && (c) <= 'Z'))
#endif
#ifndef LOWER
#define LOWER(c) ((c) | 0x20)
#endif
#ifndef UPPER
#define UPPER(c) ((c) & ~0x20)
#endif
// LD, LU, LLD, LLU for explicit conversion of integer
// #ifndef LD
// #define LD(v) ((long)(v))
// #endif
// #ifndef LU
// #define LU(v) ((unsigned long)(v))
// #endif
#ifndef LLD
#define LLD(v) ((long long)(v))
#endif
#ifndef LLU
#define LLU(v) ((unsigned long long)(v))
#endif
#ifndef _WIN32
// MAKEWORD, HIBYTE, LOBYTE
#ifndef MAKEWORD
#define MAKEWORD(h, l) ( (((WORD)h) << 8) | (l & 0xff) )
#endif
#ifndef HIBYTE
#define HIBYTE(w) ( (BYTE)(((WORD)w) >> 8) )
#endif
#ifndef LOBYTE
#define LOBYTE(w) ( (BYTE)(w & 0xff) )
#endif
// MAKELONG, HIWORD, LOWORD
#ifndef MAKELONG
#define MAKELONG(h, l) ( ((int32_t)h) << 16 | (l & 0xffff) )
#endif
#ifndef HIWORD
#define HIWORD(n) ( (WORD)(((int32_t)n) >> 16) )
#endif
#ifndef LOWORD
#define LOWORD(n) ( (WORD)(n & 0xffff) )
#endif
#endif // _WIN32
// MAKEINT64, HIINT, LOINT
#ifndef MAKEINT64
#define MAKEINT64(h, l) ( ((int64_t)h) << 32 | (l & 0xffffffff) )
#endif
#ifndef HIINT
#define HIINT(n) ( (int32_t)(((int64_t)n) >> 32) )
#endif
#ifndef LOINT
#define LOINT(n) ( (int32_t)(n & 0xffffffff) )
#endif
#ifndef MAKE_FOURCC
#define MAKE_FOURCC(a, b, c, d) \
( ((uint32)d) | ( ((uint32)c) << 8 ) | ( ((uint32)b) << 16 ) | ( ((uint32)a) << 24 ) )
#endif
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef LIMIT
#define LIMIT(lower, v, upper) ((v) < (lower) ? (lower) : (v) > (upper) ? (upper) : (v))
#endif
#ifndef MAX_PATH
#define MAX_PATH 260
#endif
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void*)0)
#endif
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef SAFE_ALLOC
#define SAFE_ALLOC(p, size)\
do {\
void* ptr = malloc(size);\
if (!ptr) {\
fprintf(stderr, "malloc failed!\n");\
exit(-1);\
}\
memset(ptr, 0, size);\
*(void**)&(p) = ptr;\
} while(0)
#endif
#ifndef SAFE_FREE
#define SAFE_FREE(p) do {if (p) {free(p); (p) = NULL;}} while(0)
#endif
#ifndef SAFE_DELETE
#define SAFE_DELETE(p) do {if (p) {delete (p); (p) = NULL;}} while(0)
#endif
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) do {if (p) {delete[] (p); (p) = NULL;}} while(0)
#endif
#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p) do {if (p) {(p)->release(); (p) = NULL;}} while(0)
#endif
#ifndef SAFE_CLOSE
#define SAFE_CLOSE(fd) do {if ((fd) >= 0) {close(fd); (fd) = -1;}} while(0)
#endif
#define STRINGIFY(x) STRINGIFY_HELPER(x)
#define STRINGIFY_HELPER(x) #x
#define STRINGCAT(x, y) STRINGCAT_HELPER(x, y)
#define STRINGCAT_HELPER(x, y) x##y
#ifndef offsetof
#define offsetof(type, member) \
((size_t)(&((type*)0)->member))
#endif
#ifndef offsetofend
#define offsetofend(type, member) \
(offsetof(type, member) + sizeof(((type*)0)->member))
#endif
#ifndef container_of
#define container_of(ptr, type, member) \
((type*)((char*)(ptr) - offsetof(type, member)))
#endif
#ifdef PRINT_DEBUG
#define printd(...) printf(__VA_ARGS__)
#else
#define printd(...)
#endif
#ifdef PRINT_ERROR
#define printe(...) fprintf(stderr, __VA_ARGS__)
#else
#define printe(...)
#endif
#endif // HV_DEF_H_

View File

@ -1,69 +0,0 @@
#ifndef HV_DIR_H_
#define HV_DIR_H_
/*
*@code
int main(int argc, char* argv[]) {
const char* dir = ".";
if (argc > 1) {
dir = argv[1];
}
std::list<hdir_t> dirs;
listdir(dir, dirs);
for (auto& item : dirs) {
printf("%c%c%c%c%c%c%c%c%c%c\t",
item.type,
item.mode & 0400 ? 'r' : '-',
item.mode & 0200 ? 'w' : '-',
item.mode & 0100 ? 'x' : '-',
item.mode & 0040 ? 'r' : '-',
item.mode & 0020 ? 'w' : '-',
item.mode & 0010 ? 'x' : '-',
item.mode & 0004 ? 'r' : '-',
item.mode & 0002 ? 'w' : '-',
item.mode & 0001 ? 'x' : '-');
float hsize;
if (item.size < 1024) {
printf("%lu\t", item.size);
}
else if ((hsize = item.size/1024.0f) < 1024.0f) {
printf("%.1fK\t", hsize);
}
else if ((hsize /= 1024.0f) < 1024.0f) {
printf("%.1fM\t", hsize);
}
else {
hsize /= 1024.0f;
printf("%.1fG\t", hsize);
}
struct tm* tm = localtime(&item.mtime);
printf("%04d-%02d-%02d %02d:%02d:%02d\t",
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
printf("%s%s\n", item.name, item.type == 'd' ? "/" : "");
}
return 0;
}
*/
#include <string.h>
#include <time.h>
#include <list>
#include "hexport.h"
typedef struct hdir_s {
char name[256];
char type; // f:file d:dir l:link b:block c:char s:socket p:pipe
char reserverd;
unsigned short mode;
size_t size;
time_t atime;
time_t mtime;
time_t ctime;
} hdir_t;
// listdir: same as ls on unix, dir on win
HV_EXPORT int listdir(const char* dir, std::list<hdir_t>& dirs);
#endif // HV_DIR_H_

View File

@ -1,161 +0,0 @@
#ifndef HV_ENDIAN_H_
#define HV_ENDIAN_H_
#include "hplatform.h"
#if defined(OS_MAC)
#include <libkern/OSByteOrder.h>
#define htobe16(v) OSSwapHostToBigInt16(v)
#define htobe32(v) OSSwapHostToBigInt32(v)
#define htobe64(v) OSSwapHostToBigInt64(v)
#define be16toh(v) OSSwapBigToHostInt16(v)
#define be32toh(v) OSSwapBigToHostInt32(v)
#define be64toh(v) OSSwapBigToHostInt64(v)
#define htole16(v) OSSwapHostToLittleInt16(v)
#define htole32(v) OSSwapHostToLittleInt32(v)
#define htole64(v) OSSwapHostToLittleInt64(v)
#define le16toh(v) OSSwapLittleToHostInt16(v)
#define le32toh(v) OSSwapLittleToHostInt32(v)
#define le64toh(v) OSSwapLittleToHostInt64(v)
#elif defined(OS_WIN)
#define htobe16(v) htons(v)
#define htobe32(v) htonl(v)
#define htobe64(v) htonll(v)
#define be16toh(v) ntohs(v)
#define be32toh(v) ntohl(v)
#define be64toh(v) ntohll(v)
#if (BYTE_ORDER == LITTLE_ENDIAN)
#define htole16(v) (v)
#define htole32(v) (v)
#define htole64(v) (v)
#define le16toh(v) (v)
#define le32toh(v) (v)
#define le64toh(v) (v)
#elif (BYTE_ORDER == BIG_ENDIAN)
#define htole16(v) __builtin_bswap16(v)
#define htole32(v) __builtin_bswap32(v)
#define htole64(v) __builtin_bswap64(v)
#define le16toh(v) __builtin_bswap16(v)
#define le32toh(v) __builtin_bswap32(v)
#define le64toh(v) __builtin_bswap64(v)
#endif
#elif HAVE_ENDIAN_H
#include <endian.h>
#elif HAVE_SYS_ENDIAN_H
#include <sys/endian.h>
#else
#warning "Not found endian.h!"
#endif
#define PI8(p) *(int8_t*)(p)
#define PI16(p) *(int16_t*)(p)
#define PI32(p) *(int32_t*)(p)
#define PI64(p) *(int64_t*)(p)
#define PU8(p) *(uint8_t*)(p)
#define PU16(p) *(uint16_t*)(p)
#define PU32(p) *(uint32_t*)(p)
#define PU64(p) *(uint64_t*)(p)
#define PF32(p) *(float*)(p)
#define PF64(p) *(double*)(p)
#define GET_BE16(p) be16toh(PU16(p))
#define GET_BE32(p) be32toh(PU32(p))
#define GET_BE64(p) be64toh(PU64(p))
#define GET_LE16(p) le16toh(PU16(p))
#define GET_LE32(p) le32toh(PU32(p))
#define GET_LE64(p) le64toh(PU64(p))
#define PUT_BE16(p, v) PU16(p) = htobe16(v)
#define PUT_BE32(p, v) PU32(p) = htobe32(v)
#define PUT_BE64(p, v) PU64(p) = htobe64(v)
#define PUT_LE16(p, v) PU16(p) = htole16(v)
#define PUT_LE32(p, v) PU32(p) = htole32(v)
#define PUT_LE64(p, v) PU64(p) = htole64(v)
// NOTE: uint8_t* p = (uint8_t*)buf;
#define POP_BE8(p, v) v = *p; ++p
#define POP_BE16(p, v) v = be16toh(PU16(p)); p += 2
#define POP_BE32(p, v) v = be32toh(PU32(p)); p += 4
#define POP_BE64(p, v) v = be64toh(PU64(p)); p += 8
#define POP_LE8(p, v) v= *p; ++p
#define POP_LE16(p, v) v = le16toh(PU16(p)); p += 2
#define POP_LE32(p, v) v = le32toh(PU32(p)); p += 4
#define POP_LE64(p, v) v = le64toh(PU64(p)); p += 8
#define PUSH_BE8(p, v) *p = v; ++p
#define PUSH_BE16(p, v) PU16(p) = htobe16(v); p += 2
#define PUSH_BE32(p, v) PU32(p) = htobe32(v); p += 4
#define PUSH_BE64(p, v) PU64(p) = htobe64(v); p += 8
#define PUSH_LE8(p, v) *p = v; ++p
#define PUSH_LE16(p, v) PU16(p) = htole16(v); p += 2
#define PUSH_LE32(p, v) PU32(p) = htole32(v); p += 4
#define PUSH_LE64(p, v) PU64(p) = htole64(v); p += 8
// NOTE: NET_ENDIAN = BIG_ENDIAN
#define POP8(p, v) POP_BE8(p, v)
#define POP16(p, v) POP_BE16(p, v)
#define POP32(p, v) POP_BE32(p, v)
#define POP64(p, v) POP_BE64(p, v)
#define POP_N(p, v, n) memcpy(v, p, n); p += n
#define PUSH8(p, v) PUSH_BE8(p, v)
#define PUSH16(p, v) PUSH_BE16(p, v)
#define PUSH32(p, v) PUSH_BE32(p, v)
#define PUSH64(p, v) PUSH_BE64(p, v)
#define PUSH_N(p, v, n) memcpy(p, v, n); p += n
static inline int detect_endian() {
union {
char c;
short s;
} u;
u.s = 0x1122;
return u.c ==0x11 ? BIG_ENDIAN : LITTLE_ENDIAN;
}
#ifdef __cplusplus
template <typename T>
uint8_t* serialize(uint8_t* buf, T value, int host_endian = LITTLE_ENDIAN, int buf_endian = BIG_ENDIAN) {
size_t size = sizeof(T);
uint8_t* pDst = buf;
uint8_t* pSrc = (uint8_t*)&value;
if (host_endian == buf_endian) {
memcpy(pDst, pSrc, size);
}
else {
for (int i = 0; i < size; ++i) {
pDst[i] = pSrc[size-i-1];
}
}
return buf+size;
}
template <typename T>
uint8_t* deserialize(uint8_t* buf, T* value, int host_endian = LITTLE_ENDIAN, int buf_endian = BIG_ENDIAN) {
size_t size = sizeof(T);
uint8_t* pSrc = buf;
uint8_t* pDst = (uint8_t*)value;
if (host_endian == buf_endian) {
memcpy(pDst, pSrc, size);
}
else {
for (int i = 0; i < size; ++i) {
pDst[i] = pSrc[size-i-1];
}
}
return buf+size;
}
#endif // __cplusplus
#endif // HV_ENDIAN_H_

View File

@ -1,122 +0,0 @@
#ifndef HV_ERR_H_
#define HV_ERR_H_
#include <errno.h>
#include "hexport.h"
#ifndef SYS_NERR
#define SYS_NERR 133
#endif
// F(errcode, name, errmsg)
// [1, 133]
#define FOREACH_ERR_SYS(F)
// [1xx~5xx]
#define FOREACH_ERR_STATUS(F)
// [1xxx]
#define FOREACH_ERR_COMMON(F) \
F(0, OK, "OK") \
F(1000, UNKNOWN, "Unknown error") \
\
F(1001, NULL_PARAM, "Null parameter") \
F(1002, NULL_POINTER, "Null pointer") \
F(1003, NULL_DATA, "Null data") \
F(1004, NULL_HANDLE, "Null handle") \
\
F(1011, INVALID_PARAM, "Invalid parameter")\
F(1012, INVALID_POINTER, "Invalid pointer") \
F(1013, INVALID_DATA, "Invalid data") \
F(1014, INVALID_HANDLE, "Invalid handle") \
F(1015, INVALID_JSON, "Invalid json") \
F(1016, INVALID_XML, "Invalid xml") \
F(1017, INVALID_FMT, "Invalid format") \
F(1018, INVALID_PROTOCOL, "Invalid protocol") \
F(1019, INVALID_PACKAGE, "Invalid package") \
\
F(1021, OUT_OF_RANGE, "Out of range") \
F(1022, OVER_LIMIT, "Over the limit") \
F(1023, MISMATCH, "Mismatch") \
F(1024, PARSE, "Parse failed") \
\
F(1030, OPEN_FILE, "Open file failed") \
F(1031, SAVE_FILE, "Save file failed") \
F(1032, READ_FILE, "Read file failed") \
F(1033, WRITE_FILE, "Write file failed")\
\
F(1040, SSL, "SSL/TLS error") \
F(1041, NEW_SSL_CTX, "New SSL_CTX failed") \
F(1042, NEW_SSL, "New SSL failed") \
F(1043, SSL_HANDSHAKE, "SSL handshake failed") \
\
F(1100, TASK_TIMEOUT, "Task timeout") \
F(1101, TASK_QUEUE_FULL, "Task queue full") \
F(1102, TASK_QUEUE_EMPTY, "Task queue empty") \
\
F(1400, REQUEST, "Bad request") \
F(1401, RESPONSE, "Bad response") \
// [-1xxx]
#define FOREACH_ERR_FUNC(F) \
F(-1001, MALLOC, "malloc() error") \
F(-1002, REALLOC, "realloc() error") \
F(-1003, CALLOC, "calloc() error") \
F(-1004, FREE, "free() error") \
\
F(-1011, SOCKET, "socket() error") \
F(-1012, BIND, "bind() error") \
F(-1013, LISTEN, "listen() error") \
F(-1014, ACCEPT, "accept() error") \
F(-1015, CONNECT, "connect() error") \
F(-1016, RECV, "recv() error") \
F(-1017, SEND, "send() error") \
F(-1018, RECVFROM, "recvfrom() error") \
F(-1019, SENDTO, "sendto() error") \
F(-1020, SETSOCKOPT, "setsockopt() error") \
F(-1021, GETSOCKOPT, "getsockopt() error") \
// grpc [4xxx]
#define FOREACH_ERR_GRPC(F) \
F(4000, GRPC_FIRST, "grpc no error") \
F(4001, GRPC_STATUS_CANCELLED, "grpc status: cancelled") \
F(4002, GRPC_STATUS_UNKNOWN, "grpc unknown error") \
F(4003, GRPC_STATUS_INVALID_ARGUMENT, "grpc status: invalid argument")\
F(4004, GRPC_STATUS_DEADLINE, "grpc status: deadline") \
F(4005, GRPC_STATUS_NOT_FOUND, "grpc status: not found") \
F(4006, GRPC_STATUS_ALREADY_EXISTS, "grpc status: already exists") \
F(4007, GRPC_STATUS_PERMISSION_DENIED, "grpc status: permission denied") \
F(4008, GRPC_STATUS_RESOURCE_EXHAUSTED, "grpc status: resource exhausted") \
F(4009, GRPC_STATUS_FAILED_PRECONDITION,"grpc status: failed precondition") \
F(4010, GRPC_STATUS_ABORTED, "grpc status: aborted") \
F(4011, GRPC_STATUS_OUT_OF_RANGE, "grpc status: out of range") \
F(4012, GRPC_STATUS_UNIMPLEMENTED, "grpc status: unimplemented") \
F(4013, GRPC_STATUS_INTERNAL, "grpc internal error") \
F(4014, GRPC_STATUS_UNAVAILABLE, "grpc service unavailable") \
F(4015, GRPC_STATUS_DATA_LOSS, "grpc status: data loss") \
#define FOREACH_ERR(F) \
FOREACH_ERR_COMMON(F) \
FOREACH_ERR_FUNC(F) \
FOREACH_ERR_GRPC(F) \
#undef ERR_OK // prevent conflict
enum {
#define F(errcode, name, errmsg) ERR_##name = errcode,
FOREACH_ERR(F)
#undef F
};
#ifdef __cplusplus
extern "C" {
#endif
// errcode => errmsg
HV_EXPORT const char* hv_strerror(int err);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // HV_ERR_H_

View File

@ -1,157 +0,0 @@
#ifndef HV_EXPORT_H_
#define HV_EXPORT_H_
// HV_EXPORT
#if defined(HV_STATICLIB) || defined(HV_SOURCE)
#define HV_EXPORT
#elif defined(_MSC_VER)
#if defined(HV_DYNAMICLIB) || defined(HV_EXPORTS) || defined(hv_EXPORTS)
#define HV_EXPORT __declspec(dllexport)
#else
#define HV_EXPORT __declspec(dllimport)
#endif
#elif defined(__GNUC__)
#define HV_EXPORT __attribute__((visibility("default")))
#else
#define HV_EXPORT
#endif
// HV_INLINE
#define HV_INLINE static inline
// HV_DEPRECATED
#if defined(HV_NO_DEPRECATED)
#define HV_DEPRECATED
#elif defined(__GNUC__) || defined(__clang__)
#define HV_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define HV_DEPRECATED __declspec(deprecated)
#else
#define HV_DEPRECATED
#endif
// HV_UNUSED
#if defined(__GNUC__)
#define HV_UNUSED __attribute__((visibility("unused")))
#else
#define HV_UNUSED
#endif
// @param[IN | OUT | INOUT]
#ifndef IN
#define IN
#endif
#ifndef OUT
#define OUT
#endif
#ifndef INOUT
#define INOUT
#endif
// @field[OPTIONAL | REQUIRED | REPEATED]
#ifndef OPTIONAL
#define OPTIONAL
#endif
#ifndef REQUIRED
#define REQUIRED
#endif
#ifndef REPEATED
#define REPEATED
#endif
#ifdef __cplusplus
#ifndef EXTERN_C
#define EXTERN_C extern "C"
#endif
#ifndef BEGIN_EXTERN_C
#define BEGIN_EXTERN_C extern "C" {
#endif
#ifndef END_EXTERN_C
#define END_EXTERN_C } // extern "C"
#endif
#ifndef BEGIN_NAMESPACE
#define BEGIN_NAMESPACE(ns) namespace ns {
#endif
#ifndef END_NAMESPACE
#define END_NAMESPACE(ns) } // namespace ns
#endif
#ifndef USING_NAMESPACE
#define USING_NAMESPACE(ns) using namespace ns;
#endif
#ifndef DEFAULT
#define DEFAULT(x) = x
#endif
#ifndef ENUM
#define ENUM(e) enum e
#endif
#ifndef STRUCT
#define STRUCT(s) struct s
#endif
#else
#define EXTERN_C extern
#define BEGIN_EXTERN_C
#define END_EXTERN_C
#define BEGIN_NAMESPACE(ns)
#define END_NAMESPACE(ns)
#define USING_NAMESPACE(ns)
#ifndef DEFAULT
#define DEFAULT(x)
#endif
#ifndef ENUM
#define ENUM(e)\
typedef enum e e;\
enum e
#endif
#ifndef STRUCT
#define STRUCT(s)\
typedef struct s s;\
struct s
#endif
#endif // __cplusplus
#define BEGIN_NAMESPACE_HV BEGIN_NAMESPACE(hv)
#define END_NAMESPACE_HV END_NAMESPACE(hv)
#define USING_NAMESPACE_HV USING_NAMESPACE(hv)
// MSVC ports
#ifdef _MSC_VER
#pragma warning (disable: 4251) // STL dll
#pragma warning (disable: 4275) // dll-interface
#if _MSC_VER < 1900 // < VS2015
#ifndef __cplusplus
#ifndef inline
#define inline __inline
#endif
#endif
#ifndef snprintf
#define snprintf _snprintf
#endif
#endif
#endif
#endif // HV_EXPORT_H_

View File

@ -1,134 +0,0 @@
#ifndef HV_FILE_H_
#define HV_FILE_H_
#include <string> // for std::string
#include "hplatform.h" // for stat
#include "hbuf.h" // for HBuf
class HFile {
public:
HFile() {
filepath[0] = '\0';
fp = NULL;
}
~HFile() {
close();
}
int open(const char* filepath, const char* mode) {
close();
strncpy(this->filepath, filepath, MAX_PATH - 1);
fp = fopen(filepath, mode);
return fp ? 0 : errno;
}
void close() {
if (fp) {
fclose(fp);
fp = NULL;
}
}
bool isopen() {
return fp != NULL;
}
int remove() {
close();
return ::remove(filepath);
}
int rename(const char* newpath) {
close();
return ::rename(filepath, newpath);
}
size_t read(void* ptr, size_t len) {
return fread(ptr, 1, len, fp);
}
size_t write(const void* ptr, size_t len) {
return fwrite(ptr, 1, len, fp);
}
size_t write(const std::string& str) {
return write(str.c_str(), str.length());
}
int seek(size_t offset, int whence = SEEK_SET) {
return fseek(fp, offset, whence);
}
int tell() {
return ftell(fp);
}
int flush() {
return fflush(fp);
}
static size_t size(const char* filepath) {
struct stat st;
memset(&st, 0, sizeof(st));
stat(filepath, &st);
return st.st_size;
}
size_t size() {
return HFile::size(filepath);
}
size_t readall(HBuf& buf) {
size_t filesize = size();
if (filesize == 0) return 0;
buf.resize(filesize);
return fread(buf.base, 1, filesize, fp);
}
size_t readall(std::string& str) {
size_t filesize = size();
if (filesize == 0) return 0;
str.resize(filesize);
return fread((void*)str.data(), 1, filesize, fp);
}
bool readline(std::string& str) {
str.clear();
char ch;
while (fread(&ch, 1, 1, fp)) {
if (ch == '\n') {
// unix: LF
return true;
}
if (ch == '\r') {
// dos: CRLF
// read LF
if (fread(&ch, 1, 1, fp) && ch != '\n') {
// mac: CR
fseek(fp, -1, SEEK_CUR);
}
return true;
}
str += ch;
}
return str.length() != 0;
}
int readrange(std::string& str, size_t from = 0, size_t to = 0) {
size_t filesize = size();
if (filesize == 0) return 0;
if (to == 0 || to >= filesize) to = filesize - 1;
size_t readbytes = to - from + 1;
str.resize(readbytes);
fseek(fp, from, SEEK_SET);
return fread((void*)str.data(), 1, readbytes, fp);
}
public:
char filepath[MAX_PATH];
FILE* fp;
};
#endif // HV_FILE_H_

View File

@ -1,176 +0,0 @@
#ifndef HV_LOG_H_
#define HV_LOG_H_
/*
* hlog is thread-safe
*/
#include <string.h>
#ifdef _WIN32
#define DIR_SEPARATOR '\\'
#define DIR_SEPARATOR_STR "\\"
#else
#define DIR_SEPARATOR '/'
#define DIR_SEPARATOR_STR "/"
#endif
#ifndef __FILENAME__
// #define __FILENAME__ (strrchr(__FILE__, DIR_SEPARATOR) ? strrchr(__FILE__, DIR_SEPARATOR) + 1 : __FILE__)
#define __FILENAME__ (strrchr(DIR_SEPARATOR_STR __FILE__, DIR_SEPARATOR) + 1)
#endif
#include "hexport.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CLR_CLR "\033[0m" /* 恢复颜色 */
#define CLR_BLACK "\033[30m" /* 黑色字 */
#define CLR_RED "\033[31m" /* 红色字 */
#define CLR_GREEN "\033[32m" /* 绿色字 */
#define CLR_YELLOW "\033[33m" /* 黄色字 */
#define CLR_BLUE "\033[34m" /* 蓝色字 */
#define CLR_PURPLE "\033[35m" /* 紫色字 */
#define CLR_SKYBLUE "\033[36m" /* 天蓝字 */
#define CLR_WHITE "\033[37m" /* 白色字 */
#define CLR_BLK_WHT "\033[40;37m" /* 黑底白字 */
#define CLR_RED_WHT "\033[41;37m" /* 红底白字 */
#define CLR_GREEN_WHT "\033[42;37m" /* 绿底白字 */
#define CLR_YELLOW_WHT "\033[43;37m" /* 黄底白字 */
#define CLR_BLUE_WHT "\033[44;37m" /* 蓝底白字 */
#define CLR_PURPLE_WHT "\033[45;37m" /* 紫底白字 */
#define CLR_SKYBLUE_WHT "\033[46;37m" /* 天蓝底白字 */
#define CLR_WHT_BLK "\033[47;30m" /* 白底黑字 */
// XXX(id, str, clr)
#define LOG_LEVEL_MAP(XXX) \
XXX(LOG_LEVEL_DEBUG, "DEBUG", CLR_WHITE) \
XXX(LOG_LEVEL_INFO, "INFO ", CLR_GREEN) \
XXX(LOG_LEVEL_WARN, "WARN ", CLR_YELLOW) \
XXX(LOG_LEVEL_ERROR, "ERROR", CLR_RED) \
XXX(LOG_LEVEL_FATAL, "FATAL", CLR_RED_WHT)
typedef enum {
LOG_LEVEL_VERBOSE = 0,
#define XXX(id, str, clr) id,
LOG_LEVEL_MAP(XXX)
#undef XXX
LOG_LEVEL_SILENT
} log_level_e;
#define DEFAULT_LOG_FILE "libhv"
#define DEFAULT_LOG_LEVEL LOG_LEVEL_INFO
#define DEFAULT_LOG_FORMAT "%y-%m-%d %H:%M:%S.%z %L %s"
#define DEFAULT_LOG_REMAIN_DAYS 1
#define DEFAULT_LOG_MAX_BUFSIZE (1<<14) // 16k
#define DEFAULT_LOG_MAX_FILESIZE (1<<24) // 16M
// logger: default file_logger
// network_logger() see event/nlog.h
typedef void (*logger_handler)(int loglevel, const char* buf, int len);
HV_EXPORT void stdout_logger(int loglevel, const char* buf, int len);
HV_EXPORT void stderr_logger(int loglevel, const char* buf, int len);
HV_EXPORT void file_logger(int loglevel, const char* buf, int len);
// network_logger implement see event/nlog.h
// HV_EXPORT void network_logger(int loglevel, const char* buf, int len);
typedef struct logger_s logger_t;
HV_EXPORT logger_t* logger_create();
HV_EXPORT void logger_destroy(logger_t* logger);
HV_EXPORT void logger_set_handler(logger_t* logger, logger_handler fn);
HV_EXPORT void logger_set_level(logger_t* logger, int level);
// level = [VERBOSE,DEBUG,INFO,WARN,ERROR,FATAL,SILENT]
HV_EXPORT void logger_set_level_by_str(logger_t* logger, const char* level);
/*
* format = "%y-%m-%d %H:%M:%S.%z %L %s"
* message = "2020-01-02 03:04:05.067 DEBUG message"
* %y year
* %m month
* %d day
* %H hour
* %M min
* %S sec
* %z ms
* %Z us
* %l First character of level
* %L All characters of level
* %s message
* %% %
*/
HV_EXPORT void logger_set_format(logger_t* logger, const char* format);
HV_EXPORT void logger_set_max_bufsize(logger_t* logger, unsigned int bufsize);
HV_EXPORT void logger_enable_color(logger_t* logger, int on);
HV_EXPORT int logger_print(logger_t* logger, int level, const char* fmt, ...);
// below for file logger
HV_EXPORT void logger_set_file(logger_t* logger, const char* filepath);
HV_EXPORT void logger_set_max_filesize(logger_t* logger, unsigned long long filesize);
// 16, 16M, 16MB
HV_EXPORT void logger_set_max_filesize_by_str(logger_t* logger, const char* filesize);
HV_EXPORT void logger_set_remain_days(logger_t* logger, int days);
HV_EXPORT void logger_enable_fsync(logger_t* logger, int on);
HV_EXPORT void logger_fsync(logger_t* logger);
HV_EXPORT const char* logger_get_cur_file(logger_t* logger);
// hlog: default logger instance
HV_EXPORT logger_t* hv_default_logger();
HV_EXPORT void hv_destroy_default_logger(void);
// macro hlog*
#define hlog hv_default_logger()
#define hlog_destory() hv_destroy_default_logger()
#define hlog_disable() logger_set_level(hlog, LOG_LEVEL_SILENT)
#define hlog_set_file(filepath) logger_set_file(hlog, filepath)
#define hlog_set_level(level) logger_set_level(hlog, level)
#define hlog_set_level_by_str(level) logger_set_level_by_str(hlog, level)
#define hlog_set_handler(fn) logger_set_handler(hlog, fn)
#define hlog_set_format(format) logger_set_format(hlog, format)
#define hlog_set_max_filesize(filesize) logger_set_max_filesize(hlog, filesize)
#define hlog_set_max_filesize_by_str(filesize) logger_set_max_filesize_by_str(hlog, filesize)
#define hlog_set_remain_days(days) logger_set_remain_days(hlog, days)
#define hlog_enable_fsync() logger_enable_fsync(hlog, 1)
#define hlog_disable_fsync() logger_enable_fsync(hlog, 0)
#define hlog_fsync() logger_fsync(hlog)
#define hlog_get_cur_file() logger_get_cur_file(hlog)
#define hlogd(fmt, ...) logger_print(hlog, LOG_LEVEL_DEBUG, fmt " [%s:%d:%s]\n", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hlogi(fmt, ...) logger_print(hlog, LOG_LEVEL_INFO, fmt " [%s:%d:%s]\n", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hlogw(fmt, ...) logger_print(hlog, LOG_LEVEL_WARN, fmt " [%s:%d:%s]\n", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hloge(fmt, ...) logger_print(hlog, LOG_LEVEL_ERROR, fmt " [%s:%d:%s]\n", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hlogf(fmt, ...) logger_print(hlog, LOG_LEVEL_FATAL, fmt " [%s:%d:%s]\n", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
// below for android
#if defined(ANDROID) || defined(__ANDROID__)
#include <android/log.h>
#define LOG_TAG "JNI"
#undef hlogd
#undef hlogi
#undef hlogw
#undef hloge
#undef hlogf
#define hlogd(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define hlogi(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define hlogw(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define hloge(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define hlogf(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)
#endif
// macro alias
#if !defined(LOGD) && !defined(LOGI) && !defined(LOGW) && !defined(LOGE) && !defined(LOGF)
#define LOGD hlogd
#define LOGI hlogi
#define LOGW hlogw
#define LOGE hloge
#define LOGF hlogf
#endif
#ifdef __cplusplus
} // extern "C"
#endif
#endif // HV_LOG_H_

View File

@ -1,745 +0,0 @@
#ifndef HV_LOOP_H_
#define HV_LOOP_H_
#include "hexport.h"
#include "hplatform.h"
#include "hdef.h"
#include "hssl.h"
typedef struct hloop_s hloop_t;
typedef struct hevent_s hevent_t;
// NOTE: The following structures are subclasses of hevent_t,
// inheriting hevent_t data members and function members.
typedef struct hidle_s hidle_t;
typedef struct htimer_s htimer_t;
typedef struct htimeout_s htimeout_t;
typedef struct hperiod_s hperiod_t;
typedef struct hio_s hio_t;
typedef void (*hevent_cb) (hevent_t* ev);
typedef void (*hidle_cb) (hidle_t* idle);
typedef void (*htimer_cb) (htimer_t* timer);
typedef void (*hio_cb) (hio_t* io);
typedef void (*haccept_cb) (hio_t* io);
typedef void (*hconnect_cb) (hio_t* io);
typedef void (*hread_cb) (hio_t* io, void* buf, int readbytes);
typedef void (*hwrite_cb) (hio_t* io, const void* buf, int writebytes);
typedef void (*hclose_cb) (hio_t* io);
typedef enum {
HLOOP_STATUS_STOP,
HLOOP_STATUS_RUNNING,
HLOOP_STATUS_PAUSE
} hloop_status_e;
typedef enum {
HEVENT_TYPE_NONE = 0,
HEVENT_TYPE_IO = 0x00000001,
HEVENT_TYPE_TIMEOUT = 0x00000010,
HEVENT_TYPE_PERIOD = 0x00000020,
HEVENT_TYPE_TIMER = HEVENT_TYPE_TIMEOUT|HEVENT_TYPE_PERIOD,
HEVENT_TYPE_IDLE = 0x00000100,
HEVENT_TYPE_CUSTOM = 0x00000400, // 1024
} hevent_type_e;
#define HEVENT_LOWEST_PRIORITY (-5)
#define HEVENT_LOW_PRIORITY (-3)
#define HEVENT_NORMAL_PRIORITY 0
#define HEVENT_HIGH_PRIORITY 3
#define HEVENT_HIGHEST_PRIORITY 5
#define HEVENT_PRIORITY_SIZE (HEVENT_HIGHEST_PRIORITY-HEVENT_LOWEST_PRIORITY+1)
#define HEVENT_PRIORITY_INDEX(priority) (priority-HEVENT_LOWEST_PRIORITY)
#define HEVENT_FLAGS \
unsigned destroy :1; \
unsigned active :1; \
unsigned pending :1;
#define HEVENT_FIELDS \
hloop_t* loop; \
hevent_type_e event_type; \
uint64_t event_id; \
hevent_cb cb; \
void* userdata; \
void* privdata; \
struct hevent_s* pending_next; \
int priority; \
HEVENT_FLAGS
// sizeof(struct hevent_s)=64 on x64
struct hevent_s {
HEVENT_FIELDS
};
#define hevent_set_id(ev, id) ((hevent_t*)(ev))->event_id = id
#define hevent_set_cb(ev, cb) ((hevent_t*)(ev))->cb = cb
#define hevent_set_priority(ev, prio) ((hevent_t*)(ev))->priority = prio
#define hevent_set_userdata(ev, udata) ((hevent_t*)(ev))->userdata = (void*)udata
#define hevent_loop(ev) (((hevent_t*)(ev))->loop)
#define hevent_type(ev) (((hevent_t*)(ev))->event_type)
#define hevent_id(ev) (((hevent_t*)(ev))->event_id)
#define hevent_cb(ev) (((hevent_t*)(ev))->cb)
#define hevent_priority(ev) (((hevent_t*)(ev))->priority)
#define hevent_userdata(ev) (((hevent_t*)(ev))->userdata)
typedef enum {
HIO_TYPE_UNKNOWN = 0,
HIO_TYPE_STDIN = 0x00000001,
HIO_TYPE_STDOUT = 0x00000002,
HIO_TYPE_STDERR = 0x00000004,
HIO_TYPE_STDIO = 0x0000000F,
HIO_TYPE_FILE = 0x00000010,
HIO_TYPE_IP = 0x00000100,
HIO_TYPE_SOCK_RAW = 0x00000F00,
HIO_TYPE_UDP = 0x00001000,
HIO_TYPE_KCP = 0x00002000,
HIO_TYPE_DTLS = 0x00010000,
HIO_TYPE_SOCK_DGRAM = 0x000FF000,
HIO_TYPE_TCP = 0x00100000,
HIO_TYPE_SSL = 0x01000000,
HIO_TYPE_TLS = HIO_TYPE_SSL,
HIO_TYPE_SOCK_STREAM= 0x0FF00000,
HIO_TYPE_SOCKET = 0x0FFFFF00,
} hio_type_e;
typedef enum {
HIO_SERVER_SIDE = 0,
HIO_CLIENT_SIDE = 1,
} hio_side_e;
#define HIO_DEFAULT_CONNECT_TIMEOUT 10000 // ms
#define HIO_DEFAULT_CLOSE_TIMEOUT 60000 // ms
#define HIO_DEFAULT_KEEPALIVE_TIMEOUT 75000 // ms
#define HIO_DEFAULT_HEARTBEAT_INTERVAL 10000 // ms
BEGIN_EXTERN_C
// loop
#define HLOOP_FLAG_RUN_ONCE 0x00000001
#define HLOOP_FLAG_AUTO_FREE 0x00000002
#define HLOOP_FLAG_QUIT_WHEN_NO_ACTIVE_EVENTS 0x00000004
HV_EXPORT hloop_t* hloop_new(int flags DEFAULT(HLOOP_FLAG_AUTO_FREE));
// WARN: Forbid to call hloop_free if HLOOP_FLAG_AUTO_FREE set.
HV_EXPORT void hloop_free(hloop_t** pp);
HV_EXPORT int hloop_process_events(hloop_t* loop, int timeout_ms DEFAULT(0));
// NOTE: when no active events, loop will quit if HLOOP_FLAG_QUIT_WHEN_NO_ACTIVE_EVENTS set.
HV_EXPORT int hloop_run(hloop_t* loop);
// NOTE: hloop_stop called in loop-thread just set flag to quit in next loop,
// if called in other thread, it will wakeup loop-thread from blocking poll system call,
// then you should join loop thread to safely exit loop thread.
HV_EXPORT int hloop_stop(hloop_t* loop);
HV_EXPORT int hloop_pause(hloop_t* loop);
HV_EXPORT int hloop_resume(hloop_t* loop);
HV_EXPORT int hloop_wakeup(hloop_t* loop);
HV_EXPORT hloop_status_e hloop_status(hloop_t* loop);
HV_EXPORT void hloop_update_time(hloop_t* loop);
HV_EXPORT uint64_t hloop_now(hloop_t* loop); // s
HV_EXPORT uint64_t hloop_now_ms(hloop_t* loop); // ms
HV_EXPORT uint64_t hloop_now_us(hloop_t* loop); // us
HV_EXPORT uint64_t hloop_now_hrtime(hloop_t* loop); // us
// export some hloop's members
// @return pid of hloop_run
HV_EXPORT long hloop_pid(hloop_t* loop);
// @return tid of hloop_run
HV_EXPORT long hloop_tid(hloop_t* loop);
// @return count of loop
HV_EXPORT uint64_t hloop_count(hloop_t* loop);
// @return number of ios
HV_EXPORT uint32_t hloop_nios(hloop_t* loop);
// @return number of timers
HV_EXPORT uint32_t hloop_ntimers(hloop_t* loop);
// @return number of idles
HV_EXPORT uint32_t hloop_nidles(hloop_t* loop);
// @return number of active events
HV_EXPORT uint32_t hloop_nactives(hloop_t* loop);
// userdata
HV_EXPORT void hloop_set_userdata(hloop_t* loop, void* userdata);
HV_EXPORT void* hloop_userdata(hloop_t* loop);
// custom_event
/*
* hevent_t ev;
* memset(&ev, 0, sizeof(hevent_t));
* ev.event_type = (hevent_type_e)(HEVENT_TYPE_CUSTOM + 1);
* ev.cb = custom_event_cb;
* ev.userdata = userdata;
* hloop_post_event(loop, &ev);
*/
// NOTE: hloop_post_event is thread-safe, used to post event from other thread to loop thread.
HV_EXPORT void hloop_post_event(hloop_t* loop, hevent_t* ev);
// idle
HV_EXPORT hidle_t* hidle_add(hloop_t* loop, hidle_cb cb, uint32_t repeat DEFAULT(INFINITE));
HV_EXPORT void hidle_del(hidle_t* idle);
// timer
HV_EXPORT htimer_t* htimer_add(hloop_t* loop, htimer_cb cb, uint32_t timeout_ms, uint32_t repeat DEFAULT(INFINITE));
/*
* minute hour day week month cb
* 0~59 0~23 1~31 0~6 1~12
* -1 -1 -1 -1 -1 cron.minutely
* 30 -1 -1 -1 -1 cron.hourly
* 30 1 -1 -1 -1 cron.daily
* 30 1 15 -1 -1 cron.monthly
* 30 1 -1 5 -1 cron.weekly
* 30 1 1 -1 10 cron.yearly
*/
HV_EXPORT htimer_t* htimer_add_period(hloop_t* loop, htimer_cb cb,
int8_t minute DEFAULT(0), int8_t hour DEFAULT(-1), int8_t day DEFAULT(-1),
int8_t week DEFAULT(-1), int8_t month DEFAULT(-1), uint32_t repeat DEFAULT(INFINITE));
HV_EXPORT void htimer_del(htimer_t* timer);
HV_EXPORT void htimer_reset(htimer_t* timer, uint32_t timeout_ms DEFAULT(0));
// io
//-----------------------low-level apis---------------------------------------
#define HV_READ 0x0001
#define HV_WRITE 0x0004
#define HV_RDWR (HV_READ|HV_WRITE)
/*
const char* hio_engine() {
#ifdef EVENT_SELECT
return "select";
#elif defined(EVENT_POLL)
return "poll";
#elif defined(EVENT_EPOLL)
return "epoll";
#elif defined(EVENT_KQUEUE)
return "kqueue";
#elif defined(EVENT_IOCP)
return "iocp";
#elif defined(EVENT_PORT)
return "evport";
#else
return "noevent";
#endif
}
*/
HV_EXPORT const char* hio_engine();
HV_EXPORT hio_t* hio_get(hloop_t* loop, int fd);
HV_EXPORT int hio_add(hio_t* io, hio_cb cb, int events DEFAULT(HV_READ));
HV_EXPORT int hio_del(hio_t* io, int events DEFAULT(HV_RDWR));
// NOTE: io detach from old loop and attach to new loop
/* @see examples/multi-thread/one-acceptor-multi-workers.c
void new_conn_event(hevent_t* ev) {
hloop_t* loop = ev->loop;
hio_t* io = (hio_t*)hevent_userdata(ev);
hio_attach(loop, io);
}
void on_accpet(hio_t* io) {
hio_detach(io);
hloop_t* worker_loop = get_one_loop();
hevent_t ev;
memset(&ev, 0, sizeof(ev));
ev.loop = worker_loop;
ev.cb = new_conn_event;
ev.userdata = io;
hloop_post_event(worker_loop, &ev);
}
*/
HV_EXPORT void hio_detach(/*hloop_t* loop,*/ hio_t* io);
HV_EXPORT void hio_attach(hloop_t* loop, hio_t* io);
HV_EXPORT bool hio_exists(hloop_t* loop, int fd);
// hio_t fields
// NOTE: fd cannot be used as unique identifier, so we provide an id.
HV_EXPORT uint32_t hio_id (hio_t* io);
HV_EXPORT int hio_fd (hio_t* io);
HV_EXPORT int hio_error (hio_t* io);
HV_EXPORT int hio_events (hio_t* io);
HV_EXPORT int hio_revents (hio_t* io);
HV_EXPORT hio_type_e hio_type (hio_t* io);
HV_EXPORT struct sockaddr* hio_localaddr(hio_t* io);
HV_EXPORT struct sockaddr* hio_peeraddr (hio_t* io);
HV_EXPORT void hio_set_context(hio_t* io, void* ctx);
HV_EXPORT void* hio_context(hio_t* io);
HV_EXPORT bool hio_is_opened(hio_t* io);
HV_EXPORT bool hio_is_connected(hio_t* io);
HV_EXPORT bool hio_is_closed(hio_t* io);
// iobuf
// #include "hbuf.h"
typedef struct fifo_buf_s hio_readbuf_t;
// NOTE: One loop per thread, one readbuf per loop.
// But you can pass in your own readbuf instead of the default readbuf to avoid memcopy.
HV_EXPORT void hio_set_readbuf(hio_t* io, void* buf, size_t len);
HV_EXPORT hio_readbuf_t* hio_get_readbuf(hio_t* io);
HV_EXPORT void hio_set_max_read_bufsize (hio_t* io, uint32_t size);
HV_EXPORT void hio_set_max_write_bufsize(hio_t* io, uint32_t size);
// NOTE: hio_write is non-blocking, so there is a write queue inside hio_t to cache unwritten data and wait for writable.
// @return current buffer size of write queue.
HV_EXPORT size_t hio_write_bufsize(hio_t* io);
#define hio_write_is_complete(io) (hio_write_bufsize(io) == 0)
HV_EXPORT uint64_t hio_last_read_time(hio_t* io); // ms
HV_EXPORT uint64_t hio_last_write_time(hio_t* io); // ms
// set callbacks
HV_EXPORT void hio_setcb_accept (hio_t* io, haccept_cb accept_cb);
HV_EXPORT void hio_setcb_connect (hio_t* io, hconnect_cb connect_cb);
HV_EXPORT void hio_setcb_read (hio_t* io, hread_cb read_cb);
HV_EXPORT void hio_setcb_write (hio_t* io, hwrite_cb write_cb);
HV_EXPORT void hio_setcb_close (hio_t* io, hclose_cb close_cb);
// get callbacks
HV_EXPORT haccept_cb hio_getcb_accept(hio_t* io);
HV_EXPORT hconnect_cb hio_getcb_connect(hio_t* io);
HV_EXPORT hread_cb hio_getcb_read(hio_t* io);
HV_EXPORT hwrite_cb hio_getcb_write(hio_t* io);
HV_EXPORT hclose_cb hio_getcb_close(hio_t* io);
// Enable SSL/TLS is so easy :)
HV_EXPORT int hio_enable_ssl(hio_t* io);
HV_EXPORT bool hio_is_ssl(hio_t* io);
HV_EXPORT int hio_set_ssl (hio_t* io, hssl_t ssl);
HV_EXPORT int hio_set_ssl_ctx(hio_t* io, hssl_ctx_t ssl_ctx);
// hssl_ctx_new(opt) -> hio_set_ssl_ctx
HV_EXPORT int hio_new_ssl_ctx(hio_t* io, hssl_ctx_opt_t* opt);
HV_EXPORT hssl_t hio_get_ssl(hio_t* io);
HV_EXPORT hssl_ctx_t hio_get_ssl_ctx(hio_t* io);
// for hssl_set_sni_hostname
HV_EXPORT int hio_set_hostname(hio_t* io, const char* hostname);
HV_EXPORT const char* hio_get_hostname(hio_t* io);
// connect timeout => hclose_cb
HV_EXPORT void hio_set_connect_timeout(hio_t* io, int timeout_ms DEFAULT(HIO_DEFAULT_CONNECT_TIMEOUT));
// close timeout => hclose_cb
HV_EXPORT void hio_set_close_timeout(hio_t* io, int timeout_ms DEFAULT(HIO_DEFAULT_CLOSE_TIMEOUT));
// read timeout => hclose_cb
HV_EXPORT void hio_set_read_timeout(hio_t* io, int timeout_ms);
// write timeout => hclose_cb
HV_EXPORT void hio_set_write_timeout(hio_t* io, int timeout_ms);
// keepalive timeout => hclose_cb
HV_EXPORT void hio_set_keepalive_timeout(hio_t* io, int timeout_ms DEFAULT(HIO_DEFAULT_KEEPALIVE_TIMEOUT));
/*
void send_heartbeat(hio_t* io) {
static char buf[] = "PING\r\n";
hio_write(io, buf, 6);
}
hio_set_heartbeat(io, 3000, send_heartbeat);
*/
typedef void (*hio_send_heartbeat_fn)(hio_t* io);
// heartbeat interval => hio_send_heartbeat_fn
HV_EXPORT void hio_set_heartbeat(hio_t* io, int interval_ms, hio_send_heartbeat_fn fn);
// Nonblocking, poll IO events in the loop to call corresponding callback.
// hio_add(io, HV_READ) => accept => haccept_cb
HV_EXPORT int hio_accept (hio_t* io);
// connect => hio_add(io, HV_WRITE) => hconnect_cb
HV_EXPORT int hio_connect(hio_t* io);
// hio_add(io, HV_READ) => read => hread_cb
HV_EXPORT int hio_read (hio_t* io);
#define hio_read_start(io) hio_read(io)
#define hio_read_stop(io) hio_del(io, HV_READ)
// hio_read_start => hread_cb => hio_read_stop
HV_EXPORT int hio_read_once (hio_t* io);
// hio_read_once => hread_cb(len)
HV_EXPORT int hio_read_until_length(hio_t* io, unsigned int len);
// hio_read_once => hread_cb(...delim)
HV_EXPORT int hio_read_until_delim (hio_t* io, unsigned char delim);
HV_EXPORT int hio_read_remain(hio_t* io);
// @see examples/tinyhttpd.c examples/tinyproxyd.c
#define hio_readline(io) hio_read_until_delim(io, '\n')
#define hio_readstring(io) hio_read_until_delim(io, '\0')
#define hio_readbytes(io, len) hio_read_until_length(io, len)
#define hio_read_until(io, len) hio_read_until_length(io, len)
// NOTE: hio_write is thread-safe, locked by recursive_mutex, allow to be called by other threads.
// hio_try_write => hio_add(io, HV_WRITE) => write => hwrite_cb
HV_EXPORT int hio_write (hio_t* io, const void* buf, size_t len);
// NOTE: hio_close is thread-safe, hio_close_async will be called actually in other thread.
// hio_del(io, HV_RDWR) => close => hclose_cb
HV_EXPORT int hio_close (hio_t* io);
// NOTE: hloop_post_event(hio_close_event)
HV_EXPORT int hio_close_async(hio_t* io);
//------------------high-level apis-------------------------------------------
// hio_get -> hio_set_readbuf -> hio_setcb_read -> hio_read
HV_EXPORT hio_t* hread (hloop_t* loop, int fd, void* buf, size_t len, hread_cb read_cb);
// hio_get -> hio_setcb_write -> hio_write
HV_EXPORT hio_t* hwrite (hloop_t* loop, int fd, const void* buf, size_t len, hwrite_cb write_cb DEFAULT(NULL));
// hio_get -> hio_close
HV_EXPORT void hclose (hloop_t* loop, int fd);
// tcp
// hio_get -> hio_setcb_accept -> hio_accept
HV_EXPORT hio_t* haccept (hloop_t* loop, int listenfd, haccept_cb accept_cb);
// hio_get -> hio_setcb_connect -> hio_connect
HV_EXPORT hio_t* hconnect (hloop_t* loop, int connfd, hconnect_cb connect_cb);
// hio_get -> hio_set_readbuf -> hio_setcb_read -> hio_read
HV_EXPORT hio_t* hrecv (hloop_t* loop, int connfd, void* buf, size_t len, hread_cb read_cb);
// hio_get -> hio_setcb_write -> hio_write
HV_EXPORT hio_t* hsend (hloop_t* loop, int connfd, const void* buf, size_t len, hwrite_cb write_cb DEFAULT(NULL));
// udp
HV_EXPORT void hio_set_type(hio_t* io, hio_type_e type);
HV_EXPORT void hio_set_localaddr(hio_t* io, struct sockaddr* addr, int addrlen);
HV_EXPORT void hio_set_peeraddr (hio_t* io, struct sockaddr* addr, int addrlen);
// NOTE: must call hio_set_peeraddr before hrecvfrom/hsendto
// hio_get -> hio_set_readbuf -> hio_setcb_read -> hio_read
HV_EXPORT hio_t* hrecvfrom (hloop_t* loop, int sockfd, void* buf, size_t len, hread_cb read_cb);
// hio_get -> hio_setcb_write -> hio_write
HV_EXPORT hio_t* hsendto (hloop_t* loop, int sockfd, const void* buf, size_t len, hwrite_cb write_cb DEFAULT(NULL));
//-----------------top-level apis---------------------------------------------
// @hio_create_socket: socket -> bind -> listen
// sockaddr_set_ipport -> socket -> hio_get(loop, sockfd) ->
// side == HIO_SERVER_SIDE ? bind ->
// type & HIO_TYPE_SOCK_STREAM ? listen ->
HV_EXPORT hio_t* hio_create_socket(hloop_t* loop, const char* host, int port,
hio_type_e type DEFAULT(HIO_TYPE_TCP),
hio_side_e side DEFAULT(HIO_SERVER_SIDE));
// @tcp_server: hio_create_socket(loop, host, port, HIO_TYPE_TCP, HIO_SERVER_SIDE) -> hio_setcb_accept -> hio_accept
// @see examples/tcp_echo_server.c
HV_EXPORT hio_t* hloop_create_tcp_server (hloop_t* loop, const char* host, int port, haccept_cb accept_cb);
// @tcp_client: hio_create_socket(loop, host, port, HIO_TYPE_TCP, HIO_CLIENT_SIDE) -> hio_setcb_connect -> hio_setcb_close -> hio_connect
// @see examples/nc.c
HV_EXPORT hio_t* hloop_create_tcp_client (hloop_t* loop, const char* host, int port, hconnect_cb connect_cb, hclose_cb close_cb);
// @ssl_server: hio_create_socket(loop, host, port, HIO_TYPE_SSL, HIO_SERVER_SIDE) -> hio_setcb_accept -> hio_accept
// @see examples/tcp_echo_server.c => #define TEST_SSL 1
HV_EXPORT hio_t* hloop_create_ssl_server (hloop_t* loop, const char* host, int port, haccept_cb accept_cb);
// @ssl_client: hio_create_socket(loop, host, port, HIO_TYPE_SSL, HIO_CLIENT_SIDE) -> hio_setcb_connect -> hio_setcb_close -> hio_connect
// @see examples/nc.c => #define TEST_SSL 1
HV_EXPORT hio_t* hloop_create_ssl_client (hloop_t* loop, const char* host, int port, hconnect_cb connect_cb, hclose_cb close_cb);
// @udp_server: hio_create_socket(loop, host, port, HIO_TYPE_UDP, HIO_SERVER_SIDE)
// @see examples/udp_echo_server.c
HV_EXPORT hio_t* hloop_create_udp_server (hloop_t* loop, const char* host, int port);
// @udp_server: hio_create_socket(loop, host, port, HIO_TYPE_UDP, HIO_CLIENT_SIDE)
// @see examples/nc.c
HV_EXPORT hio_t* hloop_create_udp_client (hloop_t* loop, const char* host, int port);
//-----------------upstream---------------------------------------------
// hio_read(io)
// hio_read(io->upstream_io)
HV_EXPORT void hio_read_upstream(hio_t* io);
// on_write(io) -> hio_write_is_complete(io) -> hio_read(io->upstream_io)
HV_EXPORT void hio_read_upstream_on_write_complete(hio_t* io, const void* buf, int writebytes);
// hio_write(io->upstream_io, buf, bytes)
HV_EXPORT void hio_write_upstream(hio_t* io, void* buf, int bytes);
// hio_close(io->upstream_io)
HV_EXPORT void hio_close_upstream(hio_t* io);
// io1->upstream_io = io2;
// io2->upstream_io = io1;
// @see examples/socks5_proxy_server.c
HV_EXPORT void hio_setup_upstream(hio_t* io1, hio_t* io2);
// @return io->upstream_io
HV_EXPORT hio_t* hio_get_upstream(hio_t* io);
// @tcp_upstream: hio_create_socket -> hio_setup_upstream -> hio_connect -> on_connect -> hio_read_upstream
// @return upstream_io
// @see examples/tcp_proxy_server.c
HV_EXPORT hio_t* hio_setup_tcp_upstream(hio_t* io, const char* host, int port, int ssl DEFAULT(0));
#define hio_setup_ssl_upstream(io, host, port) hio_setup_tcp_upstream(io, host, port, 1)
// @udp_upstream: hio_create_socket -> hio_setup_upstream -> hio_read_upstream
// @return upstream_io
// @see examples/udp_proxy_server.c
HV_EXPORT hio_t* hio_setup_udp_upstream(hio_t* io, const char* host, int port);
//-----------------unpack---------------------------------------------
typedef enum {
UNPACK_MODE_NONE = 0,
UNPACK_BY_FIXED_LENGTH = 1, // Not recommended
UNPACK_BY_DELIMITER = 2, // Suitable for text protocol
UNPACK_BY_LENGTH_FIELD = 3, // Suitable for binary protocol
} unpack_mode_e;
#define DEFAULT_PACKAGE_MAX_LENGTH (1 << 21) // 2M
// UNPACK_BY_DELIMITER
#define PACKAGE_MAX_DELIMITER_BYTES 8
// UNPACK_BY_LENGTH_FIELD
typedef enum {
ENCODE_BY_VARINT = 17, // 1 MSB + 7 bits
ENCODE_BY_LITTEL_ENDIAN = LITTLE_ENDIAN, // 1234
ENCODE_BY_BIG_ENDIAN = BIG_ENDIAN, // 4321
} unpack_coding_e;
typedef struct unpack_setting_s {
unpack_mode_e mode;
unsigned int package_max_length;
union {
// UNPACK_BY_FIXED_LENGTH
struct {
unsigned int fixed_length;
};
// UNPACK_BY_DELIMITER
struct {
unsigned char delimiter[PACKAGE_MAX_DELIMITER_BYTES];
unsigned short delimiter_bytes;
};
/*
* UNPACK_BY_LENGTH_FIELD
*
* package_len = head_len + body_len + length_adjustment
*
* if (length_field_coding == ENCODE_BY_VARINT) head_len = body_offset + varint_bytes - length_field_bytes;
* else head_len = body_offset;
*
* length_field stores body length, exclude head length,
* if length_field = head_len + body_len, then length_adjustment should be set to -head_len.
*
*/
struct {
unsigned short body_offset; // Equal to head length usually
unsigned short length_field_offset;
unsigned short length_field_bytes;
short length_adjustment;
unpack_coding_e length_field_coding;
};
};
#ifdef __cplusplus
unpack_setting_s() {
// Recommended setting:
// head = flags:1byte + length:4bytes = 5bytes
mode = UNPACK_BY_LENGTH_FIELD;
package_max_length = DEFAULT_PACKAGE_MAX_LENGTH;
fixed_length = 0;
delimiter_bytes = 0;
body_offset = 5;
length_field_offset = 1;
length_field_bytes = 4;
length_field_coding = ENCODE_BY_BIG_ENDIAN;
length_adjustment = 0;
}
#endif
} unpack_setting_t;
/*
* @see examples/jsonrpc examples/protorpc
*
* NOTE: unpack_setting_t of multiple IOs of the same function also are same,
* so only the pointer of unpack_setting_t is stored in hio_t,
* the life time of unpack_setting_t shoud be guaranteed by caller.
*/
HV_EXPORT void hio_set_unpack(hio_t* io, unpack_setting_t* setting);
HV_EXPORT void hio_unset_unpack(hio_t* io);
// unpack examples
/*
unpack_setting_t ftp_unpack_setting;
memset(&ftp_unpack_setting, 0, sizeof(unpack_setting_t));
ftp_unpack_setting.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH;
ftp_unpack_setting.mode = UNPACK_BY_DELIMITER;
ftp_unpack_setting.delimiter[0] = '\r';
ftp_unpack_setting.delimiter[1] = '\n';
ftp_unpack_setting.delimiter_bytes = 2;
unpack_setting_t mqtt_unpack_setting = {
.mode = UNPACK_BY_LENGTH_FIELD,
.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH,
.body_offset = 2,
.length_field_offset = 1,
.length_field_bytes = 1,
.length_field_coding = ENCODE_BY_VARINT,
};
unpack_setting_t grpc_unpack_setting = {
.mode = UNPACK_BY_LENGTH_FIELD,
.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH,
.body_offset = 5,
.length_field_offset = 1,
.length_field_bytes = 4,
.length_field_coding = ENCODE_BY_BIG_ENDIAN,
};
*/
//-----------------reconnect----------------------------------------
#define DEFAULT_RECONNECT_MIN_DELAY 1000 // ms
#define DEFAULT_RECONNECT_MAX_DELAY 60000 // ms
#define DEFAULT_RECONNECT_DELAY_POLICY 2 // exponential
#define DEFAULT_RECONNECT_MAX_RETRY_CNT INFINITE
typedef struct reconn_setting_s {
uint32_t min_delay; // ms
uint32_t max_delay; // ms
uint32_t cur_delay; // ms
/*
* @delay_policy
* 0: fixed
* min_delay=3s => 3,3,3...
* 1: linear
* min_delay=3s max_delay=10s => 3,6,9,10,10...
* other: exponential
* min_delay=3s max_delay=60s delay_policy=2 => 3,6,12,24,48,60,60...
*/
uint32_t delay_policy;
uint32_t max_retry_cnt;
uint32_t cur_retry_cnt;
#ifdef __cplusplus
reconn_setting_s() {
min_delay = DEFAULT_RECONNECT_MIN_DELAY;
max_delay = DEFAULT_RECONNECT_MAX_DELAY;
cur_delay = 0;
// 1,2,4,8,16,32,60,60...
delay_policy = DEFAULT_RECONNECT_DELAY_POLICY;
max_retry_cnt = DEFAULT_RECONNECT_MAX_RETRY_CNT;
cur_retry_cnt = 0;
}
#endif
} reconn_setting_t;
HV_INLINE void reconn_setting_init(reconn_setting_t* reconn) {
reconn->min_delay = DEFAULT_RECONNECT_MIN_DELAY;
reconn->max_delay = DEFAULT_RECONNECT_MAX_DELAY;
reconn->cur_delay = 0;
// 1,2,4,8,16,32,60,60...
reconn->delay_policy = DEFAULT_RECONNECT_DELAY_POLICY;
reconn->max_retry_cnt = DEFAULT_RECONNECT_MAX_RETRY_CNT;
reconn->cur_retry_cnt = 0;
}
HV_INLINE void reconn_setting_reset(reconn_setting_t* reconn) {
reconn->cur_delay = 0;
reconn->cur_retry_cnt = 0;
}
HV_INLINE bool reconn_setting_can_retry(reconn_setting_t* reconn) {
++reconn->cur_retry_cnt;
return reconn->max_retry_cnt == INFINITE ||
reconn->cur_retry_cnt < reconn->max_retry_cnt;
}
HV_INLINE uint32_t reconn_setting_calc_delay(reconn_setting_t* reconn) {
if (reconn->delay_policy == 0) {
// fixed
reconn->cur_delay = reconn->min_delay;
} else if (reconn->delay_policy == 1) {
// linear
reconn->cur_delay += reconn->min_delay;
} else {
// exponential
reconn->cur_delay *= reconn->delay_policy;
}
reconn->cur_delay = MAX(reconn->cur_delay, reconn->min_delay);
reconn->cur_delay = MIN(reconn->cur_delay, reconn->max_delay);
return reconn->cur_delay;
}
//-----------------LoadBalance-------------------------------------
typedef enum {
LB_RoundRobin,
LB_Random,
LB_LeastConnections,
LB_IpHash,
LB_UrlHash,
} load_balance_e;
//-----------------rudp---------------------------------------------
#if WITH_KCP
#define WITH_RUDP 1
#endif
#if WITH_RUDP
// NOTE: hio_close_rudp is thread-safe.
HV_EXPORT int hio_close_rudp(hio_t* io, struct sockaddr* peeraddr DEFAULT(NULL));
#endif
#if WITH_KCP
typedef struct kcp_setting_s {
// ikcp_create(conv, ...)
unsigned int conv;
// ikcp_nodelay(kcp, nodelay, interval, fastresend, nocwnd)
int nodelay;
int interval;
int fastresend;
int nocwnd;
// ikcp_wndsize(kcp, sndwnd, rcvwnd)
int sndwnd;
int rcvwnd;
// ikcp_setmtu(kcp, mtu)
int mtu;
// ikcp_update
int update_interval;
#ifdef __cplusplus
kcp_setting_s() {
conv = 0x11223344;
// normal mode
nodelay = 0;
interval = 40;
fastresend = 0;
nocwnd = 0;
// fast mode
// nodelay = 1;
// interval = 10;
// fastresend = 2;
// nocwnd = 1;
sndwnd = 0;
rcvwnd = 0;
mtu = 1400;
update_interval = 10; // ms
}
#endif
} kcp_setting_t;
HV_INLINE void kcp_setting_init_with_normal_mode(kcp_setting_t* setting) {
memset(setting, 0, sizeof(kcp_setting_t));
setting->nodelay = 0;
setting->interval = 40;
setting->fastresend = 0;
setting->nocwnd = 0;
}
HV_INLINE void kcp_setting_init_with_fast_mode(kcp_setting_t* setting) {
memset(setting, 0, sizeof(kcp_setting_t));
setting->nodelay = 0;
setting->interval = 30;
setting->fastresend = 2;
setting->nocwnd = 1;
}
HV_INLINE void kcp_setting_init_with_fast2_mode(kcp_setting_t* setting) {
memset(setting, 0, sizeof(kcp_setting_t));
setting->nodelay = 1;
setting->interval = 20;
setting->fastresend = 2;
setting->nocwnd = 1;
}
HV_INLINE void kcp_setting_init_with_fast3_mode(kcp_setting_t* setting) {
memset(setting, 0, sizeof(kcp_setting_t));
setting->nodelay = 1;
setting->interval = 10;
setting->fastresend = 2;
setting->nocwnd = 1;
}
// @see examples/udp_echo_server.c => #define TEST_KCP 1
HV_EXPORT int hio_set_kcp(hio_t* io, kcp_setting_t* setting DEFAULT(NULL));
#endif
END_EXTERN_C
#endif // HV_LOOP_H_

View File

@ -1,117 +0,0 @@
#ifndef HV_MAIN_H_
#define HV_MAIN_H_
#include "hexport.h"
#include "hplatform.h"
#include "hdef.h"
#include "hproc.h"
#ifdef _MSC_VER
#pragma comment(lib, "winmm.lib") // for timeSetEvent
#endif
BEGIN_EXTERN_C
typedef struct main_ctx_s {
char run_dir[MAX_PATH];
char program_name[MAX_PATH];
char confile[MAX_PATH]; // default etc/${program}.conf
char pidfile[MAX_PATH]; // default logs/${program}.pid
char logfile[MAX_PATH]; // default logs/${program}.log
pid_t pid; // getpid
pid_t oldpid; // getpid_from_pidfile
// arg
int argc;
int arg_len;
char** os_argv;
char** save_argv;
char* cmdline;
// parsed arg
int arg_kv_size;
char** arg_kv;
int arg_list_size;
char** arg_list;
// env
int envc;
int env_len;
char** os_envp;
char** save_envp;
// signals
procedure_t reload_fn;
void* reload_userdata;
// master workers model
int worker_processes;
int worker_threads;
procedure_t worker_fn;
void* worker_userdata;
proc_ctx_t* proc_ctxs;
} main_ctx_t;
// arg_type
#define NO_ARGUMENT 0
#define REQUIRED_ARGUMENT 1
#define OPTIONAL_ARGUMENT 2
// option define
#define OPTION_PREFIX '-'
#define OPTION_DELIM '='
#define OPTION_ENABLE "1"
#define OPTION_DISABLE "0"
typedef struct option_s {
char short_opt;
const char* long_opt;
int arg_type;
} option_t;
HV_EXPORT int main_ctx_init(int argc, char** argv);
HV_EXPORT void main_ctx_free(void);
// ls -a -l
// ls -al
// watch -n 10 ls
// watch -n10 ls
HV_EXPORT int parse_opt(int argc, char** argv, const char* opt);
// gcc -g -Wall -O3 -std=cpp main.c
HV_EXPORT int parse_opt_long(int argc, char** argv, const option_t* long_options, int size);
HV_EXPORT const char* get_arg(const char* key);
HV_EXPORT const char* get_env(const char* key);
#if defined(OS_UNIX) && !HAVE_SETPROCTITLE
HV_EXPORT void setproctitle(const char* fmt, ...);
#endif
// pidfile
HV_EXPORT int create_pidfile();
HV_EXPORT void delete_pidfile(void);
HV_EXPORT pid_t getpid_from_pidfile();
// signal=[start,stop,restart,status,reload]
HV_EXPORT int signal_init(procedure_t reload_fn DEFAULT(NULL), void* reload_userdata DEFAULT(NULL));
HV_EXPORT void signal_handle(const char* signal);
#ifdef OS_UNIX
// we use SIGTERM to quit process, SIGUSR1 to reload confile
#define SIGNAL_TERMINATE SIGTERM
#define SIGNAL_RELOAD SIGUSR1
void signal_handler(int signo);
#endif
// global var
#define DEFAULT_WORKER_PROCESSES 4
#define MAXNUM_WORKER_PROCESSES 256
HV_EXPORT extern main_ctx_t g_main_ctx;
// master-workers processes
HV_EXPORT int master_workers_run(
procedure_t worker_fn,
void* worker_userdata DEFAULT(NULL),
int worker_processes DEFAULT(DEFAULT_WORKER_PROCESSES),
int worker_threads DEFAULT(0),
bool wait DEFAULT(true));
END_EXTERN_C
#endif // HV_MAIN_H_

View File

@ -1,55 +0,0 @@
#ifndef HV_MAP_H_
#define HV_MAP_H_
#include "hconfig.h"
#include <map>
#include <string>
// MultiMap
namespace std {
/*
int main() {
std::MultiMap<std::string, std::string> kvs;
kvs["name"] = "hw";
kvs["filename"] = "1.jpg";
kvs["filename"] = "2.jpg";
//kvs.insert(std::pair<std::string,std::string>("name", "hw"));
//kvs.insert(std::pair<std::string,std::string>("filename", "1.jpg"));
//kvs.insert(std::pair<std::string,std::string>("filename", "2.jpg"));
for (auto& pair : kvs) {
printf("%s:%s\n", pair.first.c_str(), pair.second.c_str());
}
auto iter = kvs.find("filename");
if (iter != kvs.end()) {
for (int i = 0; i < kvs.count("filename"); ++i, ++iter) {
printf("%s:%s\n", iter->first.c_str(), iter->second.c_str());
}
}
return 0;
}
*/
template<typename Key,typename Value>
class MultiMap : public multimap<Key, Value> {
public:
Value& operator[](Key key) {
auto iter = this->insert(std::pair<Key,Value>(key,Value()));
return (*iter).second;
}
};
}
#ifdef USE_MULTIMAP
#define HV_MAP std::MultiMap
#else
#define HV_MAP std::map
#endif
// KeyValue
namespace hv {
typedef std::map<std::string, std::string> keyval_t;
typedef std::MultiMap<std::string, std::string> multi_keyval_t;
typedef HV_MAP<std::string, std::string> KeyValue;
}
#endif // HV_MAP_H_

View File

@ -1,68 +0,0 @@
#ifndef HV_MATH_H_
#define HV_MATH_H_
#include <math.h>
static inline unsigned long floor2e(unsigned long num) {
unsigned long n = num;
int e = 0;
while (n>>=1) ++e;
unsigned long ret = 1;
while (e--) ret<<=1;
return ret;
}
static inline unsigned long ceil2e(unsigned long num) {
// 2**0 = 1
if (num == 0 || num == 1) return 1;
unsigned long n = num - 1;
int e = 1;
while (n>>=1) ++e;
unsigned long ret = 1;
while (e--) ret<<=1;
return ret;
}
// varint little-endian
// MSB
static inline int varint_encode(long long value, unsigned char* buf) {
unsigned char ch;
unsigned char *p = buf;
int bytes = 0;
do {
ch = value & 0x7F;
value >>= 7;
*p++ = value == 0 ? ch : (ch | 0x80);
++bytes;
} while (value);
return bytes;
}
// @param[IN|OUT] len: in=>buflen, out=>varint bytesize
static inline long long varint_decode(const unsigned char* buf, int* len) {
long long ret = 0;
int bytes = 0, bits = 0;
const unsigned char *p = buf;
do {
if (len && *len && bytes == *len) {
// Not enough length
*len = 0;
return 0;
}
ret |= ((long long)(*p & 0x7F)) << bits;
++bytes;
if ((*p & 0x80) == 0) {
// Found end
if (len) *len = bytes;
return ret;
}
++p;
bits += 7;
} while(bytes < 10);
// Not found end
if (len) *len = -1;
return ret;
}
#endif // HV_MATH_H_

View File

@ -1,268 +0,0 @@
#ifndef HV_MUTEX_H_
#define HV_MUTEX_H_
#include "hexport.h"
#include "hplatform.h"
#include "htime.h"
BEGIN_EXTERN_C
#ifdef _MSC_VER
#define hmutex_t CRITICAL_SECTION
#define hmutex_init InitializeCriticalSection
#define hmutex_destroy DeleteCriticalSection
#define hmutex_lock EnterCriticalSection
#define hmutex_unlock LeaveCriticalSection
#define hrecursive_mutex_t CRITICAL_SECTION
#define hrecursive_mutex_init InitializeCriticalSection
#define hrecursive_mutex_destroy DeleteCriticalSection
#define hrecursive_mutex_lock EnterCriticalSection
#define hrecursive_mutex_unlock LeaveCriticalSection
#define HSPINLOCK_COUNT -1
#define hspinlock_t CRITICAL_SECTION
#define hspinlock_init(pspin) InitializeCriticalSectionAndSpinCount(pspin, HSPINLOCK_COUNT)
#define hspinlock_destroy DeleteCriticalSection
#define hspinlock_lock EnterCriticalSection
#define hspinlock_unlock LeaveCriticalSection
#define hrwlock_t SRWLOCK
#define hrwlock_init InitializeSRWLock
#define hrwlock_destroy(plock)
#define hrwlock_rdlock AcquireSRWLockShared
#define hrwlock_rdunlock ReleaseSRWLockShared
#define hrwlock_wrlock AcquireSRWLockExclusive
#define hrwlock_wrunlock ReleaseSRWLockExclusive
#define htimed_mutex_t HANDLE
#define htimed_mutex_init(pmutex) *(pmutex) = CreateMutex(NULL, FALSE, NULL)
#define htimed_mutex_destroy(pmutex) CloseHandle(*(pmutex))
#define htimed_mutex_lock(pmutex) WaitForSingleObject(*(pmutex), INFINITE)
#define htimed_mutex_unlock(pmutex) ReleaseMutex(*(pmutex))
// true: WAIT_OBJECT_0
// false: WAIT_OBJECT_TIMEOUT
#define htimed_mutex_lock_for(pmutex, ms) ( WaitForSingleObject(*(pmutex), ms) == WAIT_OBJECT_0 )
#define hcondvar_t CONDITION_VARIABLE
#define hcondvar_init InitializeConditionVariable
#define hcondvar_destroy(pcond)
#define hcondvar_wait(pcond, pmutex) SleepConditionVariableCS(pcond, pmutex, INFINITE)
#define hcondvar_wait_for(pcond, pmutex, ms) SleepConditionVariableCS(pcond, pmutex, ms)
#define hcondvar_signal WakeConditionVariable
#define hcondvar_broadcast WakeAllConditionVariable
#define honce_t INIT_ONCE
#define HONCE_INIT INIT_ONCE_STATIC_INIT
typedef void (*honce_fn)();
static inline BOOL WINAPI s_once_func(INIT_ONCE* once, PVOID arg, PVOID* _) {
honce_fn fn = (honce_fn)arg;
fn();
return TRUE;
}
static inline void honce(honce_t* once, honce_fn fn) {
PVOID dummy = NULL;
InitOnceExecuteOnce(once, s_once_func, (PVOID)fn, &dummy);
}
#define hsem_t HANDLE
#define hsem_init(psem, value) *(psem) = CreateSemaphore(NULL, value, value+100000, NULL)
#define hsem_destroy(psem) CloseHandle(*(psem))
#define hsem_wait(psem) WaitForSingleObject(*(psem), INFINITE)
#define hsem_post(psem) ReleaseSemaphore(*(psem), 1, NULL)
// true: WAIT_OBJECT_0
// false: WAIT_OBJECT_TIMEOUT
#define hsem_wait_for(psem, ms) ( WaitForSingleObject(*(psem), ms) == WAIT_OBJECT_0 )
#else
#define hmutex_t pthread_mutex_t
#define hmutex_init(pmutex) pthread_mutex_init(pmutex, NULL)
#define hmutex_destroy pthread_mutex_destroy
#define hmutex_lock pthread_mutex_lock
#define hmutex_unlock pthread_mutex_unlock
#define hrecursive_mutex_t pthread_mutex_t
#define hrecursive_mutex_init(pmutex) \
do {\
pthread_mutexattr_t attr;\
pthread_mutexattr_init(&attr);\
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);\
pthread_mutex_init(pmutex, &attr);\
} while(0)
#define hrecursive_mutex_destroy pthread_mutex_destroy
#define hrecursive_mutex_lock pthread_mutex_lock
#define hrecursive_mutex_unlock pthread_mutex_unlock
#if HAVE_PTHREAD_SPIN_LOCK
#define hspinlock_t pthread_spinlock_t
#define hspinlock_init(pspin) pthread_spin_init(pspin, PTHREAD_PROCESS_PRIVATE)
#define hspinlock_destroy pthread_spin_destroy
#define hspinlock_lock pthread_spin_lock
#define hspinlock_unlock pthread_spin_unlock
#else
#define hspinlock_t pthread_mutex_t
#define hspinlock_init(pmutex) pthread_mutex_init(pmutex, NULL)
#define hspinlock_destroy pthread_mutex_destroy
#define hspinlock_lock pthread_mutex_lock
#define hspinlock_unlock pthread_mutex_unlock
#endif
#define hrwlock_t pthread_rwlock_t
#define hrwlock_init(prwlock) pthread_rwlock_init(prwlock, NULL)
#define hrwlock_destroy pthread_rwlock_destroy
#define hrwlock_rdlock pthread_rwlock_rdlock
#define hrwlock_rdunlock pthread_rwlock_unlock
#define hrwlock_wrlock pthread_rwlock_wrlock
#define hrwlock_wrunlock pthread_rwlock_unlock
#define htimed_mutex_t pthread_mutex_t
#define htimed_mutex_init(pmutex) pthread_mutex_init(pmutex, NULL)
#define htimed_mutex_destroy pthread_mutex_destroy
#define htimed_mutex_lock pthread_mutex_lock
#define htimed_mutex_unlock pthread_mutex_unlock
static inline void timespec_after(struct timespec* ts, unsigned int ms) {
struct timeval tv;
gettimeofday(&tv, NULL);
ts->tv_sec = tv.tv_sec + ms / 1000;
ts->tv_nsec = tv.tv_usec * 1000 + ms % 1000 * 1000000;
if (ts->tv_nsec >= 1000000000) {
ts->tv_nsec -= 1000000000;
ts->tv_sec += 1;
}
}
// true: OK
// false: ETIMEDOUT
static inline int htimed_mutex_lock_for(htimed_mutex_t* mutex, unsigned int ms) {
#if HAVE_PTHREAD_MUTEX_TIMEDLOCK
struct timespec ts;
timespec_after(&ts, ms);
return pthread_mutex_timedlock(mutex, &ts) != ETIMEDOUT;
#else
int ret = 0;
unsigned int end = gettick_ms() + ms;
while ((ret = pthread_mutex_trylock(mutex)) != 0) {
if (gettick_ms() >= end) {
break;
}
hv_msleep(1);
}
return ret == 0;
#endif
}
#define hcondvar_t pthread_cond_t
#define hcondvar_init(pcond) pthread_cond_init(pcond, NULL)
#define hcondvar_destroy pthread_cond_destroy
#define hcondvar_wait pthread_cond_wait
#define hcondvar_signal pthread_cond_signal
#define hcondvar_broadcast pthread_cond_broadcast
// true: OK
// false: ETIMEDOUT
static inline int hcondvar_wait_for(hcondvar_t* cond, hmutex_t* mutex, unsigned int ms) {
struct timespec ts;
timespec_after(&ts, ms);
return pthread_cond_timedwait(cond, mutex, &ts) != ETIMEDOUT;
}
#define honce_t pthread_once_t
#define HONCE_INIT PTHREAD_ONCE_INIT
#define honce pthread_once
#include <semaphore.h>
#define hsem_t sem_t
#define hsem_init(psem, value) sem_init(psem, 0, value)
#define hsem_destroy sem_destroy
#define hsem_wait sem_wait
#define hsem_post sem_post
// true: OK
// false: ETIMEDOUT
static inline int hsem_wait_for(hsem_t* sem, unsigned int ms) {
#if HAVE_SEM_TIMEDWAIT
struct timespec ts;
timespec_after(&ts, ms);
return sem_timedwait(sem, &ts) != ETIMEDOUT;
#else
int ret = 0;
unsigned int end = gettick_ms() + ms;
while ((ret = sem_trywait(sem)) != 0) {
if (gettick_ms() >= end) {
break;
}
hv_msleep(1);
}
return ret == 0;
#endif
}
#endif
END_EXTERN_C
#ifdef __cplusplus
#include <mutex>
#include <condition_variable>
// using std::mutex;
// NOTE: test std::timed_mutex incorrect in some platforms, use htimed_mutex_t
// using std::timed_mutex;
using std::condition_variable;
using std::lock_guard;
using std::unique_lock;
BEGIN_NAMESPACE_HV
class MutexLock {
public:
MutexLock() { hmutex_init(&_mutex); }
~MutexLock() { hmutex_destroy(&_mutex); }
void lock() { hmutex_lock(&_mutex); }
void unlock() { hmutex_unlock(&_mutex); }
protected:
hmutex_t _mutex;
};
class SpinLock {
public:
SpinLock() { hspinlock_init(&_spin); }
~SpinLock() { hspinlock_destroy(&_spin); }
void lock() { hspinlock_lock(&_spin); }
void unlock() { hspinlock_unlock(&_spin); }
protected:
hspinlock_t _spin;
};
class RWLock {
public:
RWLock() { hrwlock_init(&_rwlock); }
~RWLock() { hrwlock_destroy(&_rwlock); }
void rdlock() { hrwlock_rdlock(&_rwlock); }
void rdunlock() { hrwlock_rdunlock(&_rwlock); }
void wrlock() { hrwlock_wrlock(&_rwlock); }
void wrunlock() { hrwlock_wrunlock(&_rwlock); }
void lock() { rdlock(); }
void unlock() { rdunlock(); }
protected:
hrwlock_t _rwlock;
};
template<class T>
class LockGuard {
public:
LockGuard(T& t) : _lock(t) { _lock.lock(); }
~LockGuard() { _lock.unlock(); }
protected:
T& _lock;
};
END_NAMESPACE_HV
// same as java synchronized(lock) { ... }
#define synchronized(lock) for (std::lock_guard<std::mutex> _lock_(lock), *p = &_lock_; p != NULL; p = NULL)
#endif // __cplusplus
#endif // HV_MUTEX_H_

View File

@ -1,183 +0,0 @@
#ifndef HV_OBJECT_POOL_H_
#define HV_OBJECT_POOL_H_
/*
* @usage unittest/objectpool_test.cpp
*/
#include <list>
#include <memory>
#include <mutex>
#include <condition_variable>
#define DEFAULT_OBJECT_POOL_INIT_NUM 0
#define DEFAULT_OBJECT_POOL_MAX_NUM 4
#define DEFAULT_OBJECT_POOL_TIMEOUT 3000 // ms
template<class T>
class HObjectFactory {
public:
static T* create() {
return new T;
}
};
template<class T, class TFactory = HObjectFactory<T>>
class HObjectPool {
public:
HObjectPool(
int init_num = DEFAULT_OBJECT_POOL_INIT_NUM,
int max_num = DEFAULT_OBJECT_POOL_MAX_NUM,
int timeout = DEFAULT_OBJECT_POOL_TIMEOUT)
: _max_num(max_num)
, _timeout(timeout)
{
for (int i = 0; i < init_num; ++i) {
T* p = TFactory::create();
if (p) {
objects_.push_back(std::shared_ptr<T>(p));
}
}
_object_num = objects_.size();
}
~HObjectPool() {}
int ObjectNum() { return _object_num; }
int IdleNum() { return objects_.size(); }
int BorrowNum() { return ObjectNum() - IdleNum(); }
std::shared_ptr<T> TryBorrow() {
std::shared_ptr<T> pObj = NULL;
std::lock_guard<std::mutex> locker(mutex_);
if (!objects_.empty()) {
pObj = objects_.front();
objects_.pop_front();
}
return pObj;
}
std::shared_ptr<T> Borrow() {
std::shared_ptr<T> pObj = TryBorrow();
if (pObj) {
return pObj;
}
std::unique_lock<std::mutex> locker(mutex_);
if (_object_num < _max_num) {
++_object_num;
// NOTE: unlock to avoid TFactory::create block
mutex_.unlock();
T* p = TFactory::create();
mutex_.lock();
if (!p) --_object_num;
return std::shared_ptr<T>(p);
}
if (_timeout > 0) {
std::cv_status status = cond_.wait_for(locker, std::chrono::milliseconds(_timeout));
if (status == std::cv_status::timeout) {
return NULL;
}
if (!objects_.empty()) {
pObj = objects_.front();
objects_.pop_front();
return pObj;
}
else {
// WARN: No idle object
}
}
return pObj;
}
void Return(std::shared_ptr<T>& pObj) {
if (!pObj) return;
std::lock_guard<std::mutex> locker(mutex_);
objects_.push_back(pObj);
cond_.notify_one();
}
bool Add(std::shared_ptr<T>& pObj) {
std::lock_guard<std::mutex> locker(mutex_);
if (_object_num >= _max_num) {
return false;
}
objects_.push_back(pObj);
++_object_num;
cond_.notify_one();
return true;
}
bool Remove(std::shared_ptr<T>& pObj) {
std::lock_guard<std::mutex> locker(mutex_);
auto iter = objects_.begin();
while (iter != objects_.end()) {
if (*iter == pObj) {
iter = objects_.erase(iter);
--_object_num;
return true;
}
else {
++iter;
}
}
return false;
}
void Clear() {
std::lock_guard<std::mutex> locker(mutex_);
objects_.clear();
_object_num = 0;
}
int _object_num;
int _max_num;
int _timeout;
private:
std::list<std::shared_ptr<T>> objects_;
std::mutex mutex_;
std::condition_variable cond_;
};
template<class T, class TFactory = HObjectFactory<T>>
class HPoolObject {
public:
typedef HObjectPool<T, TFactory> PoolType;
HPoolObject(PoolType& pool) : pool_(pool)
{
sptr_ = pool_.Borrow();
}
~HPoolObject() {
if (sptr_) {
pool_.Return(sptr_);
}
}
HPoolObject(const HPoolObject<T>&) = delete;
HPoolObject<T>& operator=(const HPoolObject<T>&) = delete;
T* get() {
return sptr_.get();
}
operator bool() {
return sptr_.get() != NULL;
}
T* operator->() {
return sptr_.get();
}
T operator*() {
return *sptr_.get();
}
private:
PoolType& pool_;
std::shared_ptr<T> sptr_;
};
#endif // HV_OBJECT_POOL_H_

View File

@ -1,28 +0,0 @@
#ifndef HV_PATH_H_
#define HV_PATH_H_
#include <string> // for std::string
#include "hexport.h"
class HV_EXPORT HPath {
public:
static bool exists(const char* path);
static bool isdir(const char* path);
static bool isfile(const char* path);
static bool islink(const char* path);
// filepath = /mnt/share/image/test.jpg
// basename = test.jpg
// dirname = /mnt/share/image
// filename = test
// suffixname = jpg
static std::string basename(const std::string& filepath);
static std::string dirname(const std::string& filepath);
static std::string filename(const std::string& filepath);
static std::string suffixname(const std::string& filepath);
static std::string join(const std::string& dir, const std::string& filename);
};
#endif // HV_PATH_H_

View File

@ -1,330 +0,0 @@
#ifndef HV_PLATFORM_H_
#define HV_PLATFORM_H_
#include "hconfig.h"
// OS
#if defined(WIN64) || defined(_WIN64)
#define OS_WIN64
#define OS_WIN32
#elif defined(WIN32)|| defined(_WIN32)
#define OS_WIN32
#elif defined(ANDROID) || defined(__ANDROID__)
#define OS_ANDROID
#define OS_LINUX
#elif defined(linux) || defined(__linux) || defined(__linux__)
#define OS_LINUX
#elif defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__))
#include <TargetConditionals.h>
#if defined(TARGET_OS_MAC) && TARGET_OS_MAC
#define OS_MAC
#elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
#define OS_IOS
#endif
#define OS_DARWIN
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#define OS_FREEBSD
#define OS_BSD
#elif defined(__NetBSD__)
#define OS_NETBSD
#define OS_BSD
#elif defined(__OpenBSD__)
#define OS_OPENBSD
#define OS_BSD
#elif defined(sun) || defined(__sun) || defined(__sun__)
#define OS_SOLARIS
#else
#warning "Untested operating system platform!"
#endif
#if defined(OS_WIN32) || defined(OS_WIN64)
#undef OS_UNIX
#define OS_WIN
#else
#define OS_UNIX
#endif
// ARCH
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64)
#define ARCH_X64
#define ARCH_X86_64
#elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
#define ARCH_X86
#define ARCH_X86_32
#elif defined(__aarch64__) || defined(__ARM64__) || defined(_M_ARM64)
#define ARCH_ARM64
#elif defined(__arm__) || defined(_M_ARM)
#define ARCH_ARM
#elif defined(__mips64__)
#define ARCH_MIPS64
#elif defined(__mips__)
#define ARCH_MIPS
#else
#warning "Untested hardware architecture!"
#endif
// COMPILER
#if defined (_MSC_VER)
#define COMPILER_MSVC
#if (_MSC_VER < 1200) // Visual C++ 6.0
#define MSVS_VERSION 1998
#define MSVC_VERSION 60
#elif (_MSC_VER >= 1200) && (_MSC_VER < 1300) // Visual Studio 2002, MSVC++ 7.0
#define MSVS_VERSION 2002
#define MSVC_VERSION 70
#elif (_MSC_VER >= 1300) && (_MSC_VER < 1400) // Visual Studio 2003, MSVC++ 7.1
#define MSVS_VERSION 2003
#define MSVC_VERSION 71
#elif (_MSC_VER >= 1400) && (_MSC_VER < 1500) // Visual Studio 2005, MSVC++ 8.0
#define MSVS_VERSION 2005
#define MSVC_VERSION 80
#elif (_MSC_VER >= 1500) && (_MSC_VER < 1600) // Visual Studio 2008, MSVC++ 9.0
#define MSVS_VERSION 2008
#define MSVC_VERSION 90
#elif (_MSC_VER >= 1600) && (_MSC_VER < 1700) // Visual Studio 2010, MSVC++ 10.0
#define MSVS_VERSION 2010
#define MSVC_VERSION 100
#elif (_MSC_VER >= 1700) && (_MSC_VER < 1800) // Visual Studio 2012, MSVC++ 11.0
#define MSVS_VERSION 2012
#define MSVC_VERSION 110
#elif (_MSC_VER >= 1800) && (_MSC_VER < 1900) // Visual Studio 2013, MSVC++ 12.0
#define MSVS_VERSION 2013
#define MSVC_VERSION 120
#elif (_MSC_VER >= 1900) && (_MSC_VER < 1910) // Visual Studio 2015, MSVC++ 14.0
#define MSVS_VERSION 2015
#define MSVC_VERSION 140
#elif (_MSC_VER >= 1910) && (_MSC_VER < 1920) // Visual Studio 2017, MSVC++ 15.0
#define MSVS_VERSION 2017
#define MSVC_VERSION 150
#elif (_MSC_VER >= 1920) && (_MSC_VER < 2000) // Visual Studio 2019, MSVC++ 16.0
#define MSVS_VERSION 2019
#define MSVC_VERSION 160
#endif
#undef HAVE_STDATOMIC_H
#define HAVE_STDATOMIC_H 0
#undef HAVE_SYS_TIME_H
#define HAVE_SYS_TIME_H 0
#undef HAVE_PTHREAD_H
#define HAVE_PTHREAD_H 0
#pragma warning (disable: 4018) // signed/unsigned comparison
#pragma warning (disable: 4100) // unused param
#pragma warning (disable: 4102) // unreferenced label
#pragma warning (disable: 4244) // conversion loss of data
#pragma warning (disable: 4267) // size_t => int
#pragma warning (disable: 4819) // Unicode
#pragma warning (disable: 4996) // _CRT_SECURE_NO_WARNINGS
#elif defined(__GNUC__)
#define COMPILER_GCC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#elif defined(__clang__)
#define COMPILER_CLANG
#elif defined(__MINGW32__) || defined(__MINGW64__)
#define COMPILER_MINGW
#elif defined(__MSYS__)
#define COMPILER_MSYS
#elif defined(__CYGWIN__)
#define COMPILER_CYGWIN
#else
#warning "Untested compiler!"
#endif
// headers
#ifdef OS_WIN
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef _CRT_NONSTDC_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE
#endif
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#endif
#include <winsock2.h>
#include <ws2tcpip.h> // for inet_pton,inet_ntop
#include <windows.h>
#include <process.h> // for getpid,exec
#include <direct.h> // for mkdir,rmdir,chdir,getcwd
#include <io.h> // for open,close,read,write,lseek,tell
#define hv_sleep(s) Sleep((s) * 1000)
#define hv_msleep(ms) Sleep(ms)
#define hv_usleep(us) Sleep((us) / 1000)
#define hv_delay(ms) hv_msleep(ms)
#define hv_mkdir(dir) mkdir(dir)
// access
#ifndef F_OK
#define F_OK 0 /* test for existence of file */
#endif
#ifndef X_OK
#define X_OK (1<<0) /* test for execute or search permission */
#endif
#ifndef W_OK
#define W_OK (1<<1) /* test for write permission */
#endif
#ifndef R_OK
#define R_OK (1<<2) /* test for read permission */
#endif
// stat
#ifndef S_ISREG
#define S_ISREG(st_mode) (((st_mode) & S_IFMT) == S_IFREG)
#endif
#ifndef S_ISDIR
#define S_ISDIR(st_mode) (((st_mode) & S_IFMT) == S_IFDIR)
#endif
#else
#include <unistd.h>
#include <dirent.h> // for mkdir,rmdir,chdir,getcwd
// socket
#include <sys/socket.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netdb.h> // for gethostbyname
#define hv_sleep(s) sleep(s)
#define hv_msleep(ms) usleep((ms) * 1000)
#define hv_usleep(us) usleep(us)
#define hv_delay(ms) hv_msleep(ms)
#define hv_mkdir(dir) mkdir(dir, 0777)
#endif
#ifdef _MSC_VER
typedef int pid_t;
typedef int gid_t;
typedef int uid_t;
#define strcasecmp stricmp
#define strncasecmp strnicmp
#else
typedef int BOOL;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef void* HANDLE;
#include <strings.h>
#define stricmp strcasecmp
#define strnicmp strncasecmp
#endif
// ENDIAN
#ifndef BIG_ENDIAN
#define BIG_ENDIAN 4321
#endif
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN 1234
#endif
#ifndef NET_ENDIAN
#define NET_ENDIAN BIG_ENDIAN
#endif
// BYTE_ORDER
#ifndef BYTE_ORDER
#if defined(__BYTE_ORDER)
#define BYTE_ORDER __BYTE_ORDER
#elif defined(__BYTE_ORDER__)
#define BYTE_ORDER __BYTE_ORDER__
#elif defined(ARCH_X86) || defined(ARCH_X86_64) || \
defined(__ARMEL__) || defined(__AARCH64EL__) || \
defined(__MIPSEL) || defined(__MIPS64EL)
#define BYTE_ORDER LITTLE_ENDIAN
#elif defined(__ARMEB__) || defined(__AARCH64EB__) || \
defined(__MIPSEB) || defined(__MIPS64EB)
#define BYTE_ORDER BIG_ENDIAN
#elif defined(OS_WIN)
#define BYTE_ORDER LITTLE_ENDIAN
#else
#warning "Unknown byte order!"
#endif
#endif
// ANSI C
#include <assert.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <float.h>
#include <limits.h>
#include <errno.h>
#include <time.h>
#include <math.h>
#include <signal.h>
#ifndef __cplusplus
#if HAVE_STDBOOL_H
#include <stdbool.h>
#else
#ifndef bool
#define bool char
#endif
#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif
#endif
#endif
#if HAVE_STDINT_H
#include <stdint.h>
#elif defined(_MSC_VER) && _MSC_VER < 1700
typedef __int8 int8_t;
typedef __int16 int16_t;
typedef __int32 int32_t;
typedef __int64 int64_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#endif
typedef float float32_t;
typedef double float64_t;
typedef int (*method_t)(void* userdata);
typedef void (*procedure_t)(void* userdata);
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if HAVE_SYS_TIME_H
#include <sys/time.h> // for gettimeofday
#endif
#if HAVE_FCNTL_H
#include <fcntl.h>
#endif
#if HAVE_PTHREAD_H
#include <pthread.h>
#endif
#endif // HV_PLATFORM_H_

View File

@ -1,69 +0,0 @@
#ifndef HV_PROC_H_
#define HV_PROC_H_
#include "hplatform.h"
typedef struct proc_ctx_s {
pid_t pid; // tid in Windows
time_t start_time;
int spawn_cnt;
procedure_t init;
void* init_userdata;
procedure_t proc;
void* proc_userdata;
procedure_t exit;
void* exit_userdata;
} proc_ctx_t;
static inline void hproc_run(proc_ctx_t* ctx) {
if (ctx->init) {
ctx->init(ctx->init_userdata);
}
if (ctx->proc) {
ctx->proc(ctx->proc_userdata);
}
if (ctx->exit) {
ctx->exit(ctx->exit_userdata);
}
}
#ifdef OS_UNIX
// unix use multi-processes
static inline int hproc_spawn(proc_ctx_t* ctx) {
++ctx->spawn_cnt;
ctx->start_time = time(NULL);
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return -1;
} else if (pid == 0) {
// child process
ctx->pid = getpid();
hproc_run(ctx);
exit(0);
} else if (pid > 0) {
// parent process
ctx->pid = pid;
}
return pid;
}
#elif defined(OS_WIN)
// win32 use multi-threads
static void win_thread(void* userdata) {
proc_ctx_t* ctx = (proc_ctx_t*)userdata;
ctx->pid = GetCurrentThreadId(); // tid in Windows
hproc_run(ctx);
}
static inline int hproc_spawn(proc_ctx_t* ctx) {
++ctx->spawn_cnt;
ctx->start_time = time(NULL);
HANDLE h = (HANDLE)_beginthread(win_thread, 0, ctx);
if (h == NULL) {
return -1;
}
ctx->pid = GetThreadId(h); // tid in Windows
return ctx->pid;
}
#endif
#endif // HV_PROC_H_

View File

@ -1,79 +0,0 @@
#ifndef HV_SCOPE_H_
#define HV_SCOPE_H_
#include <functional>
typedef std::function<void()> Function;
#include "hdef.h"
// same as golang defer
class Defer {
public:
Defer(Function&& fn) : _fn(std::move(fn)) {}
~Defer() { if(_fn) _fn();}
private:
Function _fn;
};
#define defer(code) Defer STRINGCAT(_defer_, __LINE__)([&](){code});
class ScopeCleanup {
public:
template<typename Fn, typename... Args>
ScopeCleanup(Fn&& fn, Args&&... args) {
_cleanup = std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...);
}
~ScopeCleanup() {
_cleanup();
}
private:
Function _cleanup;
};
template<typename T>
class ScopeFree {
public:
ScopeFree(T* p) : _p(p) {}
~ScopeFree() {SAFE_FREE(_p);}
private:
T* _p;
};
template<typename T>
class ScopeDelete {
public:
ScopeDelete(T* p) : _p(p) {}
~ScopeDelete() {SAFE_DELETE(_p);}
private:
T* _p;
};
template<typename T>
class ScopeDeleteArray {
public:
ScopeDeleteArray(T* p) : _p(p) {}
~ScopeDeleteArray() {SAFE_DELETE_ARRAY(_p);}
private:
T* _p;
};
template<typename T>
class ScopeRelease {
public:
ScopeRelease(T* p) : _p(p) {}
~ScopeRelease() {SAFE_RELEASE(_p);}
private:
T* _p;
};
template<typename T>
class ScopeLock {
public:
ScopeLock(T& mutex) : _mutex(mutex) {_mutex.lock();}
~ScopeLock() {_mutex.unlock();}
private:
T& _mutex;
};
#endif // HV_SCOPE_H_

View File

@ -1,264 +0,0 @@
#ifndef HV_SOCKET_H_
#define HV_SOCKET_H_
#include "hexport.h"
#include "hplatform.h"
#ifdef ENABLE_UDS
#include <sys/un.h> // import struct sockaddr_un
#endif
#ifdef _MSC_VER
#pragma comment(lib, "ws2_32.lib")
#endif
#define LOCALHOST "127.0.0.1"
#define ANYADDR "0.0.0.0"
BEGIN_EXTERN_C
HV_INLINE int socket_errno() {
#ifdef OS_WIN
return WSAGetLastError();
#else
return errno;
#endif
}
HV_EXPORT const char* socket_strerror(int err);
#ifdef OS_WIN
typedef int socklen_t;
void WSAInit();
void WSADeinit();
HV_INLINE int blocking(int sockfd) {
unsigned long nb = 0;
return ioctlsocket(sockfd, FIONBIO, &nb);
}
HV_INLINE int nonblocking(int sockfd) {
unsigned long nb = 1;
return ioctlsocket(sockfd, FIONBIO, &nb);
}
#ifndef poll
#define poll WSAPoll
#endif
#undef EAGAIN
#define EAGAIN WSAEWOULDBLOCK
#undef EINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#undef EINTR
#define EINTR WSAEINTR
#undef ENOTSOCK
#define ENOTSOCK WSAENOTSOCK
#undef EMSGSIZE
#define EMSGSIZE WSAEMSGSIZE
#else
#define blocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) & ~O_NONBLOCK)
#define nonblocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK)
typedef int SOCKET;
#define INVALID_SOCKET -1
HV_INLINE int closesocket(int sockfd) {
return close(sockfd);
}
#endif
#ifndef SAFE_CLOSESOCKET
#define SAFE_CLOSESOCKET(fd) do {if ((fd) >= 0) {closesocket(fd); (fd) = -1;}} while(0)
#endif
//-----------------------------sockaddr_u----------------------------------------------
typedef union {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
#ifdef ENABLE_UDS
struct sockaddr_un sun;
#endif
} sockaddr_u;
HV_EXPORT bool is_ipv4(const char* host);
HV_EXPORT bool is_ipv6(const char* host);
HV_INLINE bool is_ipaddr(const char* host) {
return is_ipv4(host) || is_ipv6(host);
}
// @param host: domain or ip
// @retval 0:succeed
HV_EXPORT int ResolveAddr(const char* host, sockaddr_u* addr);
HV_EXPORT const char* sockaddr_ip(sockaddr_u* addr, char *ip, int len);
HV_EXPORT uint16_t sockaddr_port(sockaddr_u* addr);
HV_EXPORT int sockaddr_set_ip(sockaddr_u* addr, const char* host);
HV_EXPORT void sockaddr_set_port(sockaddr_u* addr, int port);
HV_EXPORT int sockaddr_set_ipport(sockaddr_u* addr, const char* host, int port);
HV_EXPORT socklen_t sockaddr_len(sockaddr_u* addr);
HV_EXPORT const char* sockaddr_str(sockaddr_u* addr, char* buf, int len);
//#define INET_ADDRSTRLEN 16
//#define INET6_ADDRSTRLEN 46
#ifdef ENABLE_UDS
#define SOCKADDR_STRLEN sizeof(((struct sockaddr_un*)(NULL))->sun_path)
HV_INLINE void sockaddr_set_path(sockaddr_u* addr, const char* path) {
addr->sa.sa_family = AF_UNIX;
strncpy(addr->sun.sun_path, path, sizeof(addr->sun.sun_path));
}
#else
#define SOCKADDR_STRLEN 64 // ipv4:port | [ipv6]:port
#endif
HV_INLINE void sockaddr_print(sockaddr_u* addr) {
char buf[SOCKADDR_STRLEN] = {0};
sockaddr_str(addr, buf, sizeof(buf));
puts(buf);
}
#define SOCKADDR_LEN(addr) sockaddr_len((sockaddr_u*)addr)
#define SOCKADDR_STR(addr, buf) sockaddr_str((sockaddr_u*)addr, buf, sizeof(buf))
#define SOCKADDR_PRINT(addr) sockaddr_print((sockaddr_u*)addr)
//=====================================================================================
// socket -> setsockopt -> bind
// @param type: SOCK_STREAM(tcp) SOCK_DGRAM(udp)
// @return sockfd
HV_EXPORT int Bind(int port, const char* host DEFAULT(ANYADDR), int type DEFAULT(SOCK_STREAM));
// Bind -> listen
// @return listenfd
HV_EXPORT int Listen(int port, const char* host DEFAULT(ANYADDR));
// @return connfd
// ResolveAddr -> socket -> nonblocking -> connect
HV_EXPORT int Connect(const char* host, int port, int nonblock DEFAULT(0));
// Connect(host, port, 1)
HV_EXPORT int ConnectNonblock(const char* host, int port);
// Connect(host, port, 1) -> select -> blocking
#define DEFAULT_CONNECT_TIMEOUT 10000 // ms
HV_EXPORT int ConnectTimeout(const char* host, int port, int ms DEFAULT(DEFAULT_CONNECT_TIMEOUT));
#ifdef ENABLE_UDS
HV_EXPORT int BindUnix(const char* path, int type DEFAULT(SOCK_STREAM));
HV_EXPORT int ListenUnix(const char* path);
HV_EXPORT int ConnectUnix(const char* path, int nonblock DEFAULT(0));
HV_EXPORT int ConnectUnixNonblock(const char* path);
HV_EXPORT int ConnectUnixTimeout(const char* path, int ms DEFAULT(DEFAULT_CONNECT_TIMEOUT));
#endif
// Just implement Socketpair(AF_INET, SOCK_STREAM, 0, sv);
HV_EXPORT int Socketpair(int family, int type, int protocol, int sv[2]);
HV_INLINE int tcp_nodelay(int sockfd, int on DEFAULT(1)) {
return setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(int));
}
HV_INLINE int tcp_nopush(int sockfd, int on DEFAULT(1)) {
#ifdef TCP_NOPUSH
return setsockopt(sockfd, IPPROTO_TCP, TCP_NOPUSH, (const char*)&on, sizeof(int));
#elif defined(TCP_CORK)
return setsockopt(sockfd, IPPROTO_TCP, TCP_CORK, (const char*)&on, sizeof(int));
#else
return 0;
#endif
}
HV_INLINE int tcp_keepalive(int sockfd, int on DEFAULT(1), int delay DEFAULT(60)) {
if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (const char*)&on, sizeof(int)) != 0) {
return socket_errno();
}
#ifdef TCP_KEEPALIVE
return setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, (const char*)&delay, sizeof(int));
#elif defined(TCP_KEEPIDLE)
// TCP_KEEPIDLE => tcp_keepalive_time
// TCP_KEEPCNT => tcp_keepalive_probes
// TCP_KEEPINTVL => tcp_keepalive_intvl
return setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, (const char*)&delay, sizeof(int));
#else
return 0;
#endif
}
HV_INLINE int udp_broadcast(int sockfd, int on DEFAULT(1)) {
return setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(int));
}
// send timeout
HV_INLINE int so_sndtimeo(int sockfd, int timeout) {
#ifdef OS_WIN
return setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(int));
#else
struct timeval tv = {timeout/1000, (timeout%1000)*1000};
return setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
#endif
}
// recv timeout
HV_INLINE int so_rcvtimeo(int sockfd, int timeout) {
#ifdef OS_WIN
return setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(int));
#else
struct timeval tv = {timeout/1000, (timeout%1000)*1000};
return setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
#endif
}
// send buffer size
HV_INLINE int so_sndbuf(int sockfd, int len) {
return setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char*)&len, sizeof(int));
}
// recv buffer size
HV_INLINE int so_rcvbuf(int sockfd, int len) {
return setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (const char*)&len, sizeof(int));
}
HV_INLINE int so_reuseaddr(int sockfd, int on DEFAULT(1)) {
#ifdef SO_REUSEADDR
// NOTE: SO_REUSEADDR allow to reuse sockaddr of TIME_WAIT status
return setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(int));
#else
return 0;
#endif
}
HV_INLINE int so_reuseport(int sockfd, int on DEFAULT(1)) {
#ifdef SO_REUSEPORT
// NOTE: SO_REUSEPORT allow multiple sockets to bind same port
return setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const char*)&on, sizeof(int));
#else
return 0;
#endif
}
HV_INLINE int so_linger(int sockfd, int timeout DEFAULT(1)) {
#ifdef SO_LINGER
struct linger linger;
if (timeout >= 0) {
linger.l_onoff = 1;
linger.l_linger = timeout;
} else {
linger.l_onoff = 0;
linger.l_linger = 0;
}
// NOTE: SO_LINGER change the default behavior of close, send RST, avoid TIME_WAIT
return setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (const char*)&linger, sizeof(linger));
#else
return 0;
#endif
}
END_EXTERN_C
#endif // HV_SOCKET_H_

View File

@ -1,87 +0,0 @@
#ifndef HV_SSL_H_
#define HV_SSL_H_
#include "hexport.h"
#include "hplatform.h"
#if !defined(WITH_OPENSSL) && \
!defined(WITH_GNUTLS) && \
!defined(WITH_MBEDTLS)
#ifdef OS_WIN
#define WITH_WINTLS
#elif defined(OS_DARWIN)
#define WITH_APPLETLS
#else
#define HV_WITHOUT_SSL
#endif
#endif
typedef void* hssl_ctx_t; ///> SSL_CTX
typedef void* hssl_t; ///> SSL
enum {
HSSL_SERVER = 0,
HSSL_CLIENT = 1,
};
enum {
HSSL_OK = 0,
HSSL_ERROR = -1,
HSSL_WANT_READ = -2,
HSSL_WANT_WRITE = -3,
HSSL_WOULD_BLOCK = -4,
};
typedef struct {
const char* crt_file;
const char* key_file;
const char* ca_file;
const char* ca_path;
short verify_peer;
short endpoint; // HSSL_SERVER / HSSL_CLIENT
} hssl_ctx_opt_t, hssl_ctx_init_param_t;
BEGIN_EXTERN_C
/*
const char* hssl_backend() {
#ifdef WITH_OPENSSL
return "openssl";
#elif defined(WITH_GNUTLS)
return "gnutls";
#elif defined(WITH_MBEDTLS)
return "mbedtls";
#else
return "nossl";
#endif
}
*/
HV_EXPORT const char* hssl_backend();
#define HV_WITH_SSL (strcmp(hssl_backend(), "nossl") != 0)
HV_EXPORT extern hssl_ctx_t g_ssl_ctx;
HV_EXPORT hssl_ctx_t hssl_ctx_init(hssl_ctx_init_param_t* param);
HV_EXPORT void hssl_ctx_cleanup(hssl_ctx_t ssl_ctx);
HV_EXPORT hssl_ctx_t hssl_ctx_instance();
HV_EXPORT hssl_ctx_t hssl_ctx_new(hssl_ctx_opt_t* opt);
HV_EXPORT void hssl_ctx_free(hssl_ctx_t ssl_ctx);
HV_EXPORT hssl_t hssl_new(hssl_ctx_t ssl_ctx, int fd);
HV_EXPORT void hssl_free(hssl_t ssl);
HV_EXPORT int hssl_accept(hssl_t ssl);
HV_EXPORT int hssl_connect(hssl_t ssl);
HV_EXPORT int hssl_read(hssl_t ssl, void* buf, int len);
HV_EXPORT int hssl_write(hssl_t ssl, const void* buf, int len);
HV_EXPORT int hssl_close(hssl_t ssl);
HV_EXPORT int hssl_set_sni_hostname(hssl_t ssl, const char* hostname);
#ifdef WITH_OPENSSL
HV_EXPORT int hssl_ctx_set_alpn_protos(hssl_ctx_t ssl_ctx, const unsigned char* protos, unsigned int protos_len);
#endif
END_EXTERN_C
#endif // HV_SSL_H_

View File

@ -1,80 +0,0 @@
#ifndef HV_STRING_H_
#define HV_STRING_H_
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include "hexport.h"
#include "hplatform.h"
#include "hmap.h"
#define SPACE_CHARS " \t\r\n"
#define PAIR_CHARS "{}[]()<>\"\"\'\'``"
namespace hv {
HV_EXPORT extern std::string empty_string;
HV_EXPORT extern std::map<std::string, std::string> empty_map;
typedef std::vector<std::string> StringList;
// std::map<std::string, std::string, StringCaseLess>
class StringCaseLess : public std::less<std::string> {
public:
bool operator()(const std::string& lhs, const std::string& rhs) const {
return strcasecmp(lhs.c_str(), rhs.c_str()) < 0;
}
};
// NOTE: low-version NDK not provide std::to_string
template<typename T>
HV_INLINE std::string to_string(const T& t) {
std::ostringstream oss;
oss << t;
return oss.str();
}
template<typename T>
HV_INLINE T from_string(const std::string& str) {
T t;
std::istringstream iss(str);
iss >> t;
return t;
}
template<typename T>
HV_INLINE void print(const T& t) {
std::cout << t;
}
template<typename T>
HV_INLINE void println(const T& t) {
std::cout << t << std::endl;
}
HV_EXPORT std::string& toupper(std::string& str);
HV_EXPORT std::string& tolower(std::string& str);
HV_EXPORT std::string& reverse(std::string& str);
HV_EXPORT bool startswith(const std::string& str, const std::string& start);
HV_EXPORT bool endswith(const std::string& str, const std::string& end);
HV_EXPORT bool contains(const std::string& str, const std::string& sub);
HV_EXPORT std::string asprintf(const char* fmt, ...);
// x,y,z
HV_EXPORT StringList split(const std::string& str, char delim = ',');
// k1=v1&k2=v2
HV_EXPORT hv::KeyValue splitKV(const std::string& str, char kv_kv = '&', char k_v = '=');
HV_EXPORT std::string trim(const std::string& str, const char* chars = SPACE_CHARS);
HV_EXPORT std::string ltrim(const std::string& str, const char* chars = SPACE_CHARS);
HV_EXPORT std::string rtrim(const std::string& str, const char* chars = SPACE_CHARS);
HV_EXPORT std::string trim_pairs(const std::string& str, const char* pairs = PAIR_CHARS);
HV_EXPORT std::string replace(const std::string& str, const std::string& find, const std::string& rep);
HV_EXPORT std::string replaceAll(const std::string& str, const std::string& find, const std::string& rep);
} // end namespace hv
#endif // HV_STRING_H_

View File

@ -1,68 +0,0 @@
#ifndef HV_SYS_INFO_H_
#define HV_SYS_INFO_H_
#include "hplatform.h"
#ifdef OS_LINUX
#include <sys/sysinfo.h>
#endif
#ifdef OS_DARWIN
#include <mach/mach_host.h>
#include <sys/sysctl.h>
#endif
static inline int get_ncpu() {
#ifdef OS_WIN
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwNumberOfProcessors;
#else
//return get_nprocs();
//return get_nprocs_conf();
//return sysconf(_SC_NPROCESSORS_ONLN); // processors available
return sysconf(_SC_NPROCESSORS_CONF); // processors configured
#endif
}
typedef struct meminfo_s {
unsigned long total; // KB
unsigned long free; // KB
} meminfo_t;
static inline int get_meminfo(meminfo_t* mem) {
#ifdef OS_WIN
MEMORYSTATUSEX memstat;
memset(&memstat, 0, sizeof(memstat));
memstat.dwLength = sizeof(memstat);
GlobalMemoryStatusEx(&memstat);
mem->total = (unsigned long)(memstat.ullTotalPhys >> 10);
mem->free = (unsigned long)(memstat.ullAvailPhys >> 10);
return 0;
#elif defined(OS_LINUX)
struct sysinfo info;
if (sysinfo(&info) < 0) {
return errno;
}
mem->total = info.totalram * info.mem_unit >> 10;
mem->free = info.freeram * info.mem_unit >> 10;
return 0;
#elif defined(OS_DARWIN)
uint64_t memsize = 0;
size_t size = sizeof(memsize);
int which[2] = {CTL_HW, HW_MEMSIZE};
sysctl(which, 2, &memsize, &size, NULL, 0);
mem->total = memsize >> 10;
vm_statistics_data_t info;
mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t);
host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&info, &count);
mem->free = ((uint64_t)info.free_count * sysconf(_SC_PAGESIZE)) >> 10;
return 0;
#else
(void)(mem);
return -10;
#endif
}
#endif // HV_SYS_INFO_H_

View File

@ -1,217 +0,0 @@
#ifndef HV_THREAD_H_
#define HV_THREAD_H_
#include "hplatform.h"
#ifdef OS_WIN
#define hv_getpid (long)GetCurrentProcessId
#else
#define hv_getpid (long)getpid
#endif
#ifdef OS_WIN
#define hv_gettid (long)GetCurrentThreadId
#elif HAVE_GETTID || defined(OS_ANDROID)
#define hv_gettid (long)gettid
#elif defined(OS_LINUX)
#include <sys/syscall.h>
#define hv_gettid() (long)syscall(SYS_gettid)
#elif defined(OS_DARWIN)
static inline long hv_gettid() {
uint64_t tid = 0;
pthread_threadid_np(NULL, &tid);
return tid;
}
#elif HAVE_PTHREAD_H
#define hv_gettid (long)pthread_self
#else
#define hv_gettid hv_getpid
#endif
/*
#include "hthread.h"
HTHREAD_ROUTINE(thread_demo) {
printf("thread[%ld] start\n", hv_gettid());
hv_delay(3000);
printf("thread[%ld] end\n", hv_gettid());
return 0;
}
int main() {
hthread_t th = hthread_create(thread_demo, NULL);
hthread_join(th);
return 0;
}
*/
#ifdef OS_WIN
typedef HANDLE hthread_t;
typedef DWORD (WINAPI *hthread_routine)(void*);
#define HTHREAD_RETTYPE DWORD
#define HTHREAD_ROUTINE(fname) DWORD WINAPI fname(void* userdata)
static inline hthread_t hthread_create(hthread_routine fn, void* userdata) {
return CreateThread(NULL, 0, fn, userdata, 0, NULL);
}
static inline int hthread_join(hthread_t th) {
WaitForSingleObject(th, INFINITE);
CloseHandle(th);
return 0;
}
#else
typedef pthread_t hthread_t;
typedef void* (*hthread_routine)(void*);
#define HTHREAD_RETTYPE void*
#define HTHREAD_ROUTINE(fname) void* fname(void* userdata)
static inline hthread_t hthread_create(hthread_routine fn, void* userdata) {
pthread_t th;
pthread_create(&th, NULL, fn, userdata);
return th;
}
static inline int hthread_join(hthread_t th) {
return pthread_join(th, NULL);
}
#endif
#ifdef __cplusplus
/************************************************
* HThread
* Status: STOP,RUNNING,PAUSE
* Control: start,stop,pause,resume
* first-level virtual: doTask
* second-level virtual: run
************************************************/
#include <thread>
#include <atomic>
#include <chrono>
class HThread {
public:
enum Status {
STOP,
RUNNING,
PAUSE,
};
enum SleepPolicy {
YIELD,
SLEEP_FOR,
SLEEP_UNTIL,
NO_SLEEP,
};
HThread() {
status = STOP;
status_changed = false;
dotask_cnt = 0;
sleep_policy = YIELD;
sleep_ms = 0;
}
virtual ~HThread() {}
void setStatus(Status stat) {
status_changed = true;
status = stat;
}
void setSleepPolicy(SleepPolicy policy, uint32_t ms = 0) {
sleep_policy = policy;
sleep_ms = ms;
setStatus(status);
}
virtual int start() {
if (status == STOP) {
thread = std::thread([this] {
if (!doPrepare()) return;
setStatus(RUNNING);
run();
setStatus(STOP);
if (!doFinish()) return;
});
}
return 0;
}
virtual int stop() {
if (status != STOP) {
setStatus(STOP);
}
if (thread.joinable()) {
thread.join(); // wait thread exit
}
return 0;
}
virtual int pause() {
if (status == RUNNING) {
setStatus(PAUSE);
}
return 0;
}
virtual int resume() {
if (status == PAUSE) {
setStatus(RUNNING);
}
return 0;
}
virtual void run() {
while (status != STOP) {
while (status == PAUSE) {
std::this_thread::yield();
}
doTask();
++dotask_cnt;
HThread::sleep();
}
}
virtual bool doPrepare() {return true;}
virtual void doTask() {}
virtual bool doFinish() {return true;}
std::thread thread;
std::atomic<Status> status;
uint32_t dotask_cnt;
protected:
void sleep() {
switch (sleep_policy) {
case YIELD:
std::this_thread::yield();
break;
case SLEEP_FOR:
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms));
break;
case SLEEP_UNTIL: {
if (status_changed) {
status_changed = false;
base_tp = std::chrono::steady_clock::now();
}
base_tp += std::chrono::milliseconds(sleep_ms);
std::this_thread::sleep_until(base_tp);
}
break;
default: // donothing, go all out.
break;
}
}
SleepPolicy sleep_policy;
uint32_t sleep_ms;
// for SLEEP_UNTIL
std::atomic<bool> status_changed;
std::chrono::steady_clock::time_point base_tp;
};
#endif
#endif // HV_THREAD_H_

View File

@ -1,249 +0,0 @@
#ifndef HV_THREAD_POOL_H_
#define HV_THREAD_POOL_H_
/*
* @usage unittest/threadpool_test.cpp
*/
#include <time.h>
#include <thread>
#include <list>
#include <queue>
#include <functional>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <future>
#include <memory>
#include <utility>
#include <chrono>
#define DEFAULT_THREAD_POOL_MIN_THREAD_NUM 1
#define DEFAULT_THREAD_POOL_MAX_THREAD_NUM std::thread::hardware_concurrency()
#define DEFAULT_THREAD_POOL_MAX_IDLE_TIME 60000 // ms
class HThreadPool {
public:
using Task = std::function<void()>;
HThreadPool(int min_threads = DEFAULT_THREAD_POOL_MIN_THREAD_NUM,
int max_threads = DEFAULT_THREAD_POOL_MAX_THREAD_NUM,
int max_idle_ms = DEFAULT_THREAD_POOL_MAX_IDLE_TIME)
: min_thread_num(min_threads)
, max_thread_num(max_threads)
, max_idle_time(max_idle_ms)
, status(STOP)
, cur_thread_num(0)
, idle_thread_num(0)
{}
virtual ~HThreadPool() {
stop();
}
void setMinThreadNum(int min_threads) {
min_thread_num = min_threads;
}
void setMaxThreadNum(int max_threads) {
max_thread_num = max_threads;
}
void setMaxIdleTime(int ms) {
max_idle_time = ms;
}
int currentThreadNum() {
return cur_thread_num;
}
int idleThreadNum() {
return idle_thread_num;
}
size_t taskNum() {
std::lock_guard<std::mutex> locker(task_mutex);
return tasks.size();
}
bool isStarted() {
return status != STOP;
}
bool isStopped() {
return status == STOP;
}
int start(int start_threads = 0) {
if (status != STOP) return -1;
status = RUNNING;
if (start_threads < min_thread_num) start_threads = min_thread_num;
if (start_threads > max_thread_num) start_threads = max_thread_num;
for (int i = 0; i < start_threads; ++i) {
createThread();
}
return 0;
}
int stop() {
if (status == STOP) return -1;
status = STOP;
task_cond.notify_all();
for (auto& i : threads) {
if (i.thread->joinable()) {
i.thread->join();
}
}
threads.clear();
cur_thread_num = 0;
idle_thread_num = 0;
return 0;
}
int pause() {
if (status == RUNNING) {
status = PAUSE;
}
return 0;
}
int resume() {
if (status == PAUSE) {
status = RUNNING;
}
return 0;
}
int wait() {
while (status != STOP) {
if (tasks.empty() && idle_thread_num == cur_thread_num) {
break;
}
std::this_thread::yield();
}
return 0;
}
/*
* return a future, calling future.get() will wait task done and return RetType.
* commit(fn, args...)
* commit(std::bind(&Class::mem_fn, &obj))
* commit(std::mem_fn(&Class::mem_fn, &obj))
*
*/
template<class Fn, class... Args>
auto commit(Fn&& fn, Args&&... args) -> std::future<decltype(fn(args...))> {
if (status == STOP) start();
if (idle_thread_num <= tasks.size() && cur_thread_num < max_thread_num) {
createThread();
}
using RetType = decltype(fn(args...));
auto task = std::make_shared<std::packaged_task<RetType()> >(
std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...));
std::future<RetType> future = task->get_future();
{
std::lock_guard<std::mutex> locker(task_mutex);
tasks.emplace([task]{
(*task)();
});
}
task_cond.notify_one();
return future;
}
protected:
bool createThread() {
if (cur_thread_num >= max_thread_num) return false;
std::thread* thread = new std::thread([this] {
while (status != STOP) {
while (status == PAUSE) {
std::this_thread::yield();
}
Task task;
{
std::unique_lock<std::mutex> locker(task_mutex);
task_cond.wait_for(locker, std::chrono::milliseconds(max_idle_time), [this]() {
return status == STOP || !tasks.empty();
});
if (status == STOP) return;
if (tasks.empty()) {
if (cur_thread_num > min_thread_num) {
delThread(std::this_thread::get_id());
return;
}
continue;
}
--idle_thread_num;
task = std::move(tasks.front());
tasks.pop();
}
if (task) {
task();
++idle_thread_num;
}
}
});
addThread(thread);
return true;
}
void addThread(std::thread* thread) {
thread_mutex.lock();
++cur_thread_num;
++idle_thread_num;
ThreadData data;
data.thread = std::shared_ptr<std::thread>(thread);
data.id = thread->get_id();
data.status = RUNNING;
data.start_time = time(NULL);
data.stop_time = 0;
threads.emplace_back(data);
thread_mutex.unlock();
}
void delThread(std::thread::id id) {
time_t now = time(NULL);
thread_mutex.lock();
--cur_thread_num;
--idle_thread_num;
auto iter = threads.begin();
while (iter != threads.end()) {
if (iter->status == STOP && now > iter->stop_time) {
if (iter->thread->joinable()) {
iter->thread->join();
iter = threads.erase(iter);
continue;
}
} else if (iter->id == id) {
iter->status = STOP;
iter->stop_time = time(NULL);
}
++iter;
}
thread_mutex.unlock();
}
public:
int min_thread_num;
int max_thread_num;
int max_idle_time;
protected:
enum Status {
STOP,
RUNNING,
PAUSE,
};
struct ThreadData {
std::shared_ptr<std::thread> thread;
std::thread::id id;
Status status;
time_t start_time;
time_t stop_time;
};
std::atomic<Status> status;
std::atomic<int> cur_thread_num;
std::atomic<int> idle_thread_num;
std::list<ThreadData> threads;
std::mutex thread_mutex;
std::queue<Task> tasks;
std::mutex task_mutex;
std::condition_variable task_cond;
};
#endif // HV_THREAD_POOL_H_

View File

@ -1,114 +0,0 @@
#ifndef HV_TIME_H_
#define HV_TIME_H_
#include "hexport.h"
#include "hplatform.h"
BEGIN_EXTERN_C
#define SECONDS_PER_MINUTE 60
#define SECONDS_PER_HOUR 3600
#define SECONDS_PER_DAY 86400 // 24*3600
#define SECONDS_PER_WEEK 604800 // 7*24*3600
#define IS_LEAP_YEAR(year) (((year)%4 == 0 && (year)%100 != 0) || (year)%400 == 0)
typedef struct datetime_s {
int year;
int month;
int day;
int hour;
int min;
int sec;
int ms;
} datetime_t;
#ifdef _MSC_VER
/* @see winsock2.h
// Structure used in select() call, taken from the BSD file sys/time.h
struct timeval {
long tv_sec;
long tv_usec;
};
*/
struct timezone {
int tz_minuteswest; /* of Greenwich */
int tz_dsttime; /* type of dst correction to apply */
};
#include <sys/timeb.h>
HV_INLINE int gettimeofday(struct timeval *tv, struct timezone *tz) {
struct _timeb tb;
_ftime(&tb);
if (tv) {
tv->tv_sec = (long)tb.time;
tv->tv_usec = tb.millitm * 1000;
}
if (tz) {
tz->tz_minuteswest = tb.timezone;
tz->tz_dsttime = tb.dstflag;
}
return 0;
}
#endif
HV_EXPORT unsigned int gettick_ms();
HV_INLINE unsigned long long gettimeofday_ms() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * (unsigned long long)1000 + tv.tv_usec/1000;
}
HV_INLINE unsigned long long gettimeofday_us() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * (unsigned long long)1000000 + tv.tv_usec;
}
HV_EXPORT unsigned long long gethrtime_us();
HV_EXPORT datetime_t datetime_now();
HV_EXPORT datetime_t datetime_localtime(time_t seconds);
HV_EXPORT time_t datetime_mktime(datetime_t* dt);
HV_EXPORT datetime_t* datetime_past(datetime_t* dt, int days DEFAULT(1));
HV_EXPORT datetime_t* datetime_future(datetime_t* dt, int days DEFAULT(1));
#define TIME_FMT "%02d:%02d:%02d"
#define TIME_FMT_BUFLEN 12
HV_EXPORT char* duration_fmt(int sec, char* buf);
#define DATETIME_FMT "%04d-%02d-%02d %02d:%02d:%02d"
#define DATETIME_FMT_ISO "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ"
#define DATETIME_FMT_BUFLEN 30
HV_EXPORT char* datetime_fmt(datetime_t* dt, char* buf);
HV_EXPORT char* datetime_fmt_iso(datetime_t* dt, char* buf);
#define GMTIME_FMT "%.3s, %02d %.3s %04d %02d:%02d:%02d GMT"
#define GMTIME_FMT_BUFLEN 30
HV_EXPORT char* gmtime_fmt(time_t time, char* buf);
HV_EXPORT int days_of_month(int month, int year);
HV_EXPORT int month_atoi(const char* month);
HV_EXPORT const char* month_itoa(int month);
HV_EXPORT int weekday_atoi(const char* weekday);
HV_EXPORT const char* weekday_itoa(int weekday);
HV_EXPORT datetime_t hv_compile_datetime();
/*
* minute hour day week month action
* 0~59 0~23 1~31 0~6 1~12
* -1 -1 -1 -1 -1 cron.minutely
* 30 -1 -1 -1 -1 cron.hourly
* 30 1 -1 -1 -1 cron.daily
* 30 1 15 -1 -1 cron.monthly
* 30 1 -1 0 -1 cron.weekly
* 30 1 1 -1 10 cron.yearly
*/
HV_EXPORT time_t cron_next_timeout(int minute, int hour, int day, int week, int month);
END_EXTERN_C
#endif // HV_TIME_H_

View File

@ -1,78 +0,0 @@
#ifndef HV_HTTP_CONTENT_H_
#define HV_HTTP_CONTENT_H_
#include "hexport.h"
#include "hstring.h"
// NOTE: WITHOUT_HTTP_CONTENT
// ndk-r10e no std::to_string and can't compile modern json.hpp
#ifndef WITHOUT_HTTP_CONTENT
#include "json.hpp" // https://github.com/nlohmann/json
#endif
BEGIN_NAMESPACE_HV
// QueryParams
using QueryParams = hv::KeyValue;
HV_EXPORT std::string dump_query_params(const QueryParams& query_params);
HV_EXPORT int parse_query_params(const char* query_string, QueryParams& query_params);
#ifndef WITHOUT_HTTP_CONTENT
/**************multipart/form-data*************************************
--boundary
Content-Disposition: form-data; name="user"
content
--boundary
Content-Disposition: form-data; name="avatar"; filename="user.jpg"
Content-Type: image/jpeg
content
--boundary--
***********************************************************************/
// FormData
struct FormData {
std::string filename;
std::string content;
FormData(const char* content = NULL, const char* filename = NULL) {
if (content) {
this->content = content;
}
if (filename) {
this->filename = filename;
}
}
template<typename T>
FormData(T num) {
content = hv::to_string(num);
}
};
// FormFile
struct FormFile : public FormData {
FormFile(const char* filename = NULL) {
if (filename) {
this->filename = filename;
}
}
};
// MultiPart
// name => FormData
typedef HV_MAP<std::string, FormData> MultiPart;
#define DEFAULT_MULTIPART_BOUNDARY "----WebKitFormBoundary7MA4YWxkTrZu0gW"
HV_EXPORT std::string dump_multipart(MultiPart& mp, const char* boundary = DEFAULT_MULTIPART_BOUNDARY);
HV_EXPORT int parse_multipart(const std::string& str, MultiPart& mp, const char* boundary);
// Json
using Json = nlohmann::json;
// using Json = nlohmann::ordered_json;
HV_EXPORT std::string dump_json(const hv::Json& json, int indent = -1);
HV_EXPORT int parse_json(const char* str, hv::Json& json, std::string& errmsg);
#endif
END_NAMESPACE_HV
#endif // HV_HTTP_CONTENT_H_

View File

@ -1,295 +0,0 @@
#ifndef HV_HTTP_DEF_H_
#define HV_HTTP_DEF_H_
#include "hexport.h"
#define DEFAULT_HTTP_PORT 80
#define DEFAULT_HTTPS_PORT 443
enum http_version { HTTP_V1 = 1, HTTP_V2 = 2 };
enum http_session_type { HTTP_CLIENT, HTTP_SERVER };
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
enum http_parser_state {
HP_START_REQ_OR_RES,
HP_MESSAGE_BEGIN,
HP_URL,
HP_STATUS,
HP_HEADER_FIELD,
HP_HEADER_VALUE,
HP_HEADERS_COMPLETE,
HP_CHUNK_HEADER,
HP_BODY,
HP_CHUNK_COMPLETE,
HP_MESSAGE_COMPLETE,
HP_ERROR
};
// http_status
// XX(num, name, string)
#define HTTP_STATUS_MAP(XX) \
XX(100, CONTINUE, Continue) \
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
XX(102, PROCESSING, Processing) \
XX(200, OK, OK) \
XX(201, CREATED, Created) \
XX(202, ACCEPTED, Accepted) \
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
XX(204, NO_CONTENT, No Content) \
XX(205, RESET_CONTENT, Reset Content) \
XX(206, PARTIAL_CONTENT, Partial Content) \
XX(207, MULTI_STATUS, Multi-Status) \
XX(208, ALREADY_REPORTED, Already Reported) \
XX(226, IM_USED, IM Used) \
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
XX(302, FOUND, Found) \
XX(303, SEE_OTHER, See Other) \
XX(304, NOT_MODIFIED, Not Modified) \
XX(305, USE_PROXY, Use Proxy) \
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
XX(400, BAD_REQUEST, Bad Request) \
XX(401, UNAUTHORIZED, Unauthorized) \
XX(402, PAYMENT_REQUIRED, Payment Required) \
XX(403, FORBIDDEN, Forbidden) \
XX(404, NOT_FOUND, Not Found) \
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
XX(408, REQUEST_TIMEOUT, Request Timeout) \
XX(409, CONFLICT, Conflict) \
XX(410, GONE, Gone) \
XX(411, LENGTH_REQUIRED, Length Required) \
XX(412, PRECONDITION_FAILED, Precondition Failed) \
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
XX(414, URI_TOO_LONG, URI Too Long) \
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
XX(417, EXPECTATION_FAILED, Expectation Failed) \
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
XX(423, LOCKED, Locked) \
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
XX(501, NOT_IMPLEMENTED, Not Implemented) \
XX(502, BAD_GATEWAY, Bad Gateway) \
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
XX(508, LOOP_DETECTED, Loop Detected) \
XX(510, NOT_EXTENDED, Not Extended) \
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
// HTTP_STATUS_##name
enum http_status {
#define XX(num, name, string) HTTP_STATUS_##name = num,
HTTP_STATUS_MAP(XX)
#undef XX
HTTP_CUSTOM_STATUS
};
#define HTTP_STATUS_IS_REDIRECT(status) \
( \
(status) == HTTP_STATUS_MOVED_PERMANENTLY || \
(status) == HTTP_STATUS_FOUND || \
(status) == HTTP_STATUS_SEE_OTHER || \
(status) == HTTP_STATUS_TEMPORARY_REDIRECT || \
(status) == HTTP_STATUS_PERMANENT_REDIRECT \
)
// http_mehtod
// XX(num, name, string)
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
/* pathological */ \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
/* WebDAV */ \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
/* subversion */ \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
/* upnp */ \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
/* RFC-5789 */ \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
/* CalDAV */ \
XX(30, MKCALENDAR, MKCALENDAR) \
/* RFC-2068, section 19.6.1.2 */ \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
/* icecast */ \
XX(33, SOURCE, SOURCE) \
// HTTP_##name
enum http_method {
#define XX(num, name, string) HTTP_##name = num,
HTTP_METHOD_MAP(XX)
#undef XX
HTTP_CUSTOM_METHOD
};
// MIME: https://www.iana.org/assignments/media-types/media-types.xhtml
// XX(name, mime, suffix)
#define MIME_TYPE_TEXT_MAP(XX) \
XX(TEXT_PLAIN, text/plain, txt) \
XX(TEXT_HTML, text/html, html) \
XX(TEXT_CSS, text/css, css) \
XX(TEXT_CSV, text/csv, csv) \
XX(TEXT_MARKDOWN, text/markdown, md) \
XX(TEXT_EVENT_STREAM, text/event-stream, sse) \
#define MIME_TYPE_APPLICATION_MAP(XX) \
XX(APPLICATION_JAVASCRIPT, application/javascript, js) \
XX(APPLICATION_JSON, application/json, json) \
XX(APPLICATION_XML, application/xml, xml) \
XX(APPLICATION_URLENCODED, application/x-www-form-urlencoded, kv) \
XX(APPLICATION_OCTET_STREAM,application/octet-stream, bin) \
XX(APPLICATION_ZIP, application/zip, zip) \
XX(APPLICATION_GZIP, application/gzip, gzip) \
XX(APPLICATION_7Z, application/x-7z-compressed, 7z) \
XX(APPLICATION_RAR, application/x-rar-compressed, rar) \
XX(APPLICATION_PDF, application/pdf, pdf) \
XX(APPLICATION_RTF, application/rtf, rtf) \
XX(APPLICATION_GRPC, application/grpc, grpc) \
XX(APPLICATION_WASM, application/wasm, wasm) \
XX(APPLICATION_JAR, application/java-archive, jar) \
XX(APPLICATION_XHTML, application/xhtml+xml, xhtml) \
XX(APPLICATION_ATOM, application/atom+xml, atom) \
XX(APPLICATION_RSS, application/rss+xml, rss) \
XX(APPLICATION_WORD, application/msword, doc) \
XX(APPLICATION_EXCEL, application/vnd.ms-excel, xls) \
XX(APPLICATION_PPT, application/vnd.ms-powerpoint, ppt) \
XX(APPLICATION_EOT, application/vnd.ms-fontobject, eot) \
XX(APPLICATION_M3U8, application/vnd.apple.mpegurl, m3u8) \
XX(APPLICATION_DOCX, application/vnd.openxmlformats-officedocument.wordprocessingml.document, docx) \
XX(APPLICATION_XLSX, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, xlsx) \
XX(APPLICATION_PPTX, application/vnd.openxmlformats-officedocument.presentationml.presentation, pptx) \
#define MIME_TYPE_MULTIPART_MAP(XX) \
XX(MULTIPART_FORM_DATA, multipart/form-data, mp) \
#define MIME_TYPE_IMAGE_MAP(XX) \
XX(IMAGE_JPEG, image/jpeg, jpg) \
XX(IMAGE_PNG, image/png, png) \
XX(IMAGE_GIF, image/gif, gif) \
XX(IMAGE_ICO, image/x-icon, ico) \
XX(IMAGE_BMP, image/x-ms-bmp, bmp) \
XX(IMAGE_SVG, image/svg+xml, svg) \
XX(IMAGE_TIFF, image/tiff, tiff) \
XX(IMAGE_WEBP, image/webp, webp) \
#define MIME_TYPE_VIDEO_MAP(XX) \
XX(VIDEO_MP4, video/mp4, mp4) \
XX(VIDEO_FLV, video/x-flv, flv) \
XX(VIDEO_M4V, video/x-m4v, m4v) \
XX(VIDEO_MNG, video/x-mng, mng) \
XX(VIDEO_TS, video/mp2t, ts) \
XX(VIDEO_MPEG, video/mpeg, mpeg) \
XX(VIDEO_WEBM, video/webm, webm) \
XX(VIDEO_MOV, video/quicktime, mov) \
XX(VIDEO_3GPP, video/3gpp, 3gpp) \
XX(VIDEO_AVI, video/x-msvideo, avi) \
XX(VIDEO_WMV, video/x-ms-wmv, wmv) \
XX(VIDEO_ASF, video/x-ms-asf, asf) \
#define MIME_TYPE_AUDIO_MAP(XX) \
XX(AUDIO_MP3, audio/mpeg, mp3) \
XX(AUDIO_OGG, audio/ogg, ogg) \
XX(AUDIO_M4A, audio/x-m4a, m4a) \
XX(AUDIO_AAC, audio/aac, aac) \
XX(AUDIO_PCMA, audio/PCMA, pcma) \
XX(AUDIO_OPUS, audio/opus, opus) \
#define MIME_TYPE_FONT_MAP(XX) \
XX(FONT_TTF, font/ttf, ttf) \
XX(FONT_OTF, font/otf, otf) \
XX(FONT_WOFF, font/woff, woff) \
XX(FONT_WOFF2, font/woff2, woff2) \
#define HTTP_CONTENT_TYPE_MAP(XX) \
MIME_TYPE_TEXT_MAP(XX) \
MIME_TYPE_APPLICATION_MAP(XX) \
MIME_TYPE_MULTIPART_MAP(XX) \
MIME_TYPE_IMAGE_MAP(XX) \
MIME_TYPE_VIDEO_MAP(XX) \
MIME_TYPE_AUDIO_MAP(XX) \
MIME_TYPE_FONT_MAP(XX) \
#define X_WWW_FORM_URLENCODED APPLICATION_URLENCODED // for compatibility
enum http_content_type {
#define XX(name, string, suffix) name,
CONTENT_TYPE_NONE = 0,
CONTENT_TYPE_TEXT = 100,
MIME_TYPE_TEXT_MAP(XX)
CONTENT_TYPE_APPLICATION = 200,
MIME_TYPE_APPLICATION_MAP(XX)
CONTENT_TYPE_MULTIPART = 300,
MIME_TYPE_MULTIPART_MAP(XX)
CONTENT_TYPE_IMAGE = 400,
MIME_TYPE_IMAGE_MAP(XX)
CONTENT_TYPE_VIDEO = 500,
MIME_TYPE_VIDEO_MAP(XX)
CONTENT_TYPE_AUDIO = 600,
MIME_TYPE_AUDIO_MAP(XX)
CONTENT_TYPE_FONT = 700,
MIME_TYPE_FONT_MAP(XX)
CONTENT_TYPE_UNDEFINED = 1000
#undef XX
};
BEGIN_EXTERN_C
HV_EXPORT const char* http_status_str(enum http_status status);
HV_EXPORT const char* http_method_str(enum http_method method);
HV_EXPORT const char* http_content_type_str(enum http_content_type type);
HV_EXPORT enum http_status http_status_enum(const char* str);
HV_EXPORT enum http_method http_method_enum(const char* str);
HV_EXPORT enum http_content_type http_content_type_enum(const char* str);
HV_EXPORT const char* http_content_type_suffix(enum http_content_type type);
HV_EXPORT const char* http_content_type_str_by_suffix(const char* suffix);
HV_EXPORT enum http_content_type http_content_type_enum_by_suffix(const char* suffix);
END_EXTERN_C
#endif // HV_HTTP_DEF_H_

View File

@ -1,34 +0,0 @@
#ifndef HV_URL_H_
#define HV_URL_H_
#include <string> // import std::string
#include "hexport.h"
class HV_EXPORT HUrl {
public:
static std::string escape(const std::string& str, const char* unescaped_chars = "");
static std::string unescape(const std::string& str);
static inline std::string escapeUrl(const std::string& url) {
return escape(url, ":/@?=&#+");
}
HUrl() : port(0) {}
~HUrl() {}
void reset();
bool parse(const std::string& url);
const std::string& dump();
std::string url;
std::string scheme;
std::string username;
std::string password;
std::string host;
int port;
std::string path;
std::string query;
std::string fragment;
};
#endif // HV_URL_H_

View File

@ -1,41 +0,0 @@
#ifndef HV_H_
#define HV_H_
/**
* @copyright 2018 HeWei, all rights reserved.
*/
// platform
#include "hconfig.h"
#include "hexport.h"
#include "hplatform.h"
// c
#include "hdef.h" // <stddef.h>
#include "hatomic.h"// <stdatomic.h>
#include "herr.h" // <errno.h>
#include "htime.h" // <time.h>
#include "hmath.h" // <math.h>
#include "hbase.h"
#include "hversion.h"
#include "hsysinfo.h"
#include "hproc.h"
#include "hthread.h"
#include "hmutex.h"
#include "hsocket.h"
#include "hlog.h"
#include "hbuf.h"
// cpp
#ifdef __cplusplus
#include "hmap.h" // <map>
#include "hstring.h" // <string>
#include "hfile.h"
#include "hpath.h"
#include "hdir.h"
#include "hurl.h"
#endif
#endif // HV_H_

View File

@ -1,34 +0,0 @@
#ifndef HV_VERSION_H_
#define HV_VERSION_H_
#include "hexport.h"
#include "hdef.h"
BEGIN_EXTERN_C
#define HV_VERSION_MAJOR 1
#define HV_VERSION_MINOR 3
#define HV_VERSION_PATCH 1
#define HV_VERSION_STRING STRINGIFY(HV_VERSION_MAJOR) "." \
STRINGIFY(HV_VERSION_MINOR) "." \
STRINGIFY(HV_VERSION_PATCH)
#define HV_VERSION_NUMBER ((HV_VERSION_MAJOR << 16) | (HV_VERSION_MINOR << 8) | HV_VERSION_PATCH)
HV_INLINE const char* hv_version() {
return HV_VERSION_STRING;
}
HV_EXPORT const char* hv_compile_version();
// 1.2.3.4 => 0x01020304
HV_EXPORT int version_atoi(const char* str);
// 0x01020304 => 1.2.3.4
HV_EXPORT void version_itoa(int hex, char* str);
END_EXTERN_C
#endif // HV_VERSION_H_

View File

@ -1,36 +0,0 @@
#ifndef HV_IFCONFIG_H_
#define HV_IFCONFIG_H_
#include <vector>
#include "hexport.h"
#ifdef _MSC_VER
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
#endif
typedef struct ifconfig_s {
char name[128];
char ip[16];
char mask[16];
char broadcast[16];
char mac[20];
} ifconfig_t;
/*
* @test
std::vector<ifconfig_t> ifcs;
ifconfig(ifcs);
for (auto& item : ifcs) {
printf("%s\nip: %s\nmask: %s\nbroadcast: %s\nmac: %s\n\n",
item.name,
item.ip,
item.mask,
item.broadcast,
item.mac);
}
*/
HV_EXPORT int ifconfig(std::vector<ifconfig_t>& ifcs);
#endif // HV_IFCONFIG_H_

Some files were not shown because too many files have changed in this diff Show More