├── .gitignore
├── .golangci.yaml
├── LICENSE
├── README.md
├── build
├── .gitignore
├── build-all.sh
├── build.sh
├── configs
│ ├── .gitignore
│ ├── README.md
│ ├── api_node.template.yaml
│ └── cluster.template.yaml
├── data
│ └── .gitignore
├── logs
│ └── .gitignore
├── pages
│ ├── 403.html
│ ├── 404.html
│ ├── 50x.html
│ ├── shutdown_en.html
│ ├── shutdown_upgrade_zh.html
│ └── shutdown_zh.html
├── test.sh
└── www
│ ├── .gitignore
│ └── index.html
├── cmd
└── edge-node
│ └── main.go
├── dist
└── .gitignore
├── go.mod
├── go.sum
└── internal
├── apps
├── app_cmd.go
├── directive.go
├── log_writer.go
└── main.go
├── caches
├── consts.go
├── errors.go
├── errros_test.go
├── file_dir.go
├── hot_item.go
├── item.go
├── item_test.go
├── list_file_db_sqlite.go
├── list_file_db_sqlite_test.go
├── list_file_hash_map_sqlite.go
├── list_file_hash_map_sqlite_test.go
├── list_file_kv.go
├── list_file_kv_objects.go
├── list_file_kv_objects_test.go
├── list_file_kv_store.go
├── list_file_kv_test.go
├── list_file_sqlite.go
├── list_file_sqlite_test.go
├── list_interface.go
├── list_memory.go
├── list_memory_test.go
├── manager.go
├── manager_test.go
├── open_file.go
├── open_file_cache.go
├── open_file_cache_test.go
├── open_file_pool.go
├── open_file_pool_test.go
├── partial_ranges.go
├── partial_ranges_queue.go
├── partial_ranges_queue_test.go
├── partial_ranges_test.go
├── reader.go
├── reader_file.go
├── reader_file_mmap.go
├── reader_file_test.go
├── reader_memory.go
├── reader_memory_test.go
├── reader_partial_file.go
├── stat.go
├── storage_file.go
├── storage_file_ext.go
├── storage_file_test.go
├── storage_interface.go
├── storage_memory.go
├── storage_memory_test.go
├── utils.go
├── utils_partial.go
├── utils_test.go
├── writer.go
├── writer_file.go
├── writer_memory.go
├── writer_memory_test.go
├── writer_partial_file.go
└── writer_partial_file_test.go
├── compressions
├── errors.go
├── reader.go
├── reader_base.go
├── reader_brotli.go
├── reader_brotli_test.go
├── reader_deflate.go
├── reader_deflate_test.go
├── reader_gzip.go
├── reader_gzip_test.go
├── reader_pool.go
├── reader_pool_brotli.go
├── reader_pool_deflate.go
├── reader_pool_gzip.go
├── reader_pool_zstd.go
├── reader_zstd.go
├── reader_zstd_test.go
├── utils.go
├── utils_test.go
├── writer.go
├── writer_base.go
├── writer_brotli.go
├── writer_brotli_test.go
├── writer_deflate.go
├── writer_deflate_test.go
├── writer_gzip.go
├── writer_gzip_test.go
├── writer_pool.go
├── writer_pool_brotli.go
├── writer_pool_deflate.go
├── writer_pool_gzip.go
├── writer_pool_zstd.go
├── writer_zstd.go
└── writer_zstd_test.go
├── configs
├── api_config.go
├── api_config_test.go
├── cluster_config.go
└── cluster_config_test.go
├── conns
├── linger.go
├── map.go
└── map_test_test.go
├── const
├── build.go
├── build_plus.go
├── const.go
└── vars.go
├── encrypt
├── magic_key.go
├── magic_key_test.go
├── method.go
├── method_aes_128_cfb.go
├── method_aes_128_cfb_test.go
├── method_aes_192_cfb.go
├── method_aes_192_cfb_test.go
├── method_aes_256_cfb.go
├── method_aes_256_cfb_test.go
├── method_raw.go
├── method_raw_test.go
├── method_utils.go
└── method_utils_test.go
├── errors
├── error.go
└── error_test.go
├── events
├── events.go
├── utils.go
└── utils_test.go
├── firewalls
├── ddos_protection.go
├── ddos_protection_others.go
├── firewall.go
├── firewall_base.go
├── firewall_firewalld.go
├── firewall_http.go
├── firewall_interface.go
├── firewall_mock.go
├── firewall_nftables.go
├── firewall_nftables_others.go
├── nftables
│ ├── .gitignore
│ ├── chain.go
│ ├── chain_policy.go
│ ├── chain_test.go
│ ├── conn.go
│ ├── conn_test.go
│ ├── element.go
│ ├── errors.go
│ ├── expration.go
│ ├── expration_test.go
│ ├── family.go
│ ├── installer.go
│ ├── rule.go
│ ├── set.go
│ ├── set_batch.go
│ ├── set_data_type.go
│ ├── set_ext.go
│ ├── set_test.go
│ ├── table.go
│ └── table_test.go
└── utils.go
├── goman
├── instance.go
├── lib.go
├── lib_test.go
├── task_group.go
└── task_group_test.go
├── iplibrary
├── README.md
├── action_base.go
├── action_errors.go
├── action_firewalld.go
├── action_firewalld_test.go
├── action_html.go
├── action_http_api.go
├── action_http_api_test.go
├── action_interface.go
├── action_ipset.go
├── action_ipset_test.go
├── action_iptables.go
├── action_iptables_test.go
├── action_manager.go
├── action_manager_test.go
├── action_script.go
├── action_script_test.go
├── action_utils.go
├── action_utils_test.go
├── init.go
├── ip_item.go
├── ip_item_test.go
├── ip_list.go
├── ip_list_db.go
├── ip_list_kv.go
├── ip_list_kv_objects.go
├── ip_list_kv_test.go
├── ip_list_sqlite.go
├── ip_list_sqlite_test.go
├── ip_list_test.go
├── list_type.go
├── list_utils.go
├── list_utils_test.go
├── manager_ip_list.go
├── manager_ip_list_test.go
├── result.go
└── server_list_manager.go
├── js
└── .gitignore
├── metrics
├── manager.go
├── manager_test.go
├── metric_interface.go
├── stat.go
├── stat_test.go
├── sum_test.go
├── task.go
├── task_base.go
├── task_kv.go
├── task_kv_objects.go
├── task_kv_test.go
└── task_sqlite.go
├── monitor
├── value.go
├── value_queue.go
└── value_queue_test.go
├── nodes
├── api_stream.go
├── api_stream_test.go
├── client_conn.go
├── client_conn_base.go
├── client_conn_interface.go
├── client_conn_limiter.go
├── client_conn_limiter_test.go
├── client_conn_traffic.go
├── client_conn_utils.go
├── client_listener.go
├── client_tls_conn.go
├── conn_linger.go
├── http_access_log_queue.go
├── http_access_log_queue_test.go
├── http_access_log_viewer.go
├── http_cache_task_manager.go
├── http_cache_task_manager_test.go
├── http_client.go
├── http_client_pool.go
├── http_client_pool_test.go
├── http_client_transport.go
├── http_request.go
├── http_request_acme.go
├── http_request_auth.go
├── http_request_cache.go
├── http_request_cc.go
├── http_request_error.go
├── http_request_events.go
├── http_request_fastcgi.go
├── http_request_health_check.go
├── http_request_hls.go
├── http_request_host_redirect.go
├── http_request_http3.go
├── http_request_limit.go
├── http_request_ln.go
├── http_request_log.go
├── http_request_metrics.go
├── http_request_mismatch.go
├── http_request_oss.go
├── http_request_page.go
├── http_request_plan_before.go
├── http_request_redirect_https.go
├── http_request_referers.go
├── http_request_reverse_proxy.go
├── http_request_rewrite.go
├── http_request_root.go
├── http_request_shutdown.go
├── http_request_stat.go
├── http_request_sub.go
├── http_request_test.go
├── http_request_traffic_limit.go
├── http_request_uam.go
├── http_request_url.go
├── http_request_user_agent.go
├── http_request_utils.go
├── http_request_utils_test.go
├── http_request_waf.go
├── http_request_websocket.go
├── http_writer.go
├── http_writer_empty.go
├── http_writer_ext.go
├── ip_library_updater.go
├── listener.go
├── listener_base.go
├── listener_base_ext.go
├── listener_base_test.go
├── listener_http.go
├── listener_interface.go
├── listener_manager.go
├── listener_manager_test.go
├── listener_tcp.go
├── listener_test.go
├── listener_udp.go
├── node.go
├── node_ext.go
├── node_panic.go
├── node_panic_arm64.go
├── node_status_executor.go
├── node_status_executor_test.go
├── node_status_executor_unix.go
├── node_status_executor_windows.go
├── node_tasks.go
├── node_tasks_ext.go
├── node_test.go
├── origin_conn.go
├── origin_state.go
├── origin_state_manager.go
├── origin_state_manager_test.go
├── origin_utils.go
├── system_services.go
├── task_ocsp_update.go
├── task_ocsp_update_test.go
├── task_sync_api_nodes.go
├── task_trim_disks.go
├── toa_manager.go
├── upgrade_manager.go
├── upgrade_manager_test.go
└── user_manager.go
├── re
├── regexp.go
├── regexp_test.go
├── rune_tree.go
└── rune_tree_test.go
├── remotelogs
└── utils.go
├── rpc
├── call_stat.go
├── call_stat_test.go
├── rpc_client.go
├── rpc_test.go
└── rpc_utils.go
├── stats
├── bandwidth_stat_manager.go
├── bandwidth_stat_manager_test.go
├── dau_manager.go
├── dau_manager_test.go
├── http_request_stat_manager.go
├── http_request_stat_manager_test.go
├── traffic_stat_manager.go
├── traffic_stat_manager_test.go
├── user_agent_parser.go
├── user_agent_parser_result.go
└── user_agent_parser_test.go
├── trackers
├── label.go
├── manager.go
└── manager_test.go
├── ttlcache
├── cache.go
├── cache_test.go
├── item.go
├── manager.go
├── option.go
├── piece.go
├── piece_test.go
├── utils.go
└── utils_test.go
├── utils
├── agents
│ ├── agent.go
│ ├── agent_ip.go
│ ├── agents.go
│ ├── agents_test.go
│ ├── db.go
│ ├── db_kv.go
│ ├── db_kv_objects.go
│ ├── db_kv_test.go
│ ├── db_sqlite.go
│ ├── ip_cache_map.go
│ ├── ip_cache_map_test.go
│ ├── manager.go
│ ├── manager_test.go
│ ├── queue.go
│ └── queue_test.go
├── bfs
│ ├── .gitignore
│ ├── block_info.go
│ ├── blocks_file.go
│ ├── blocks_file_options.go
│ ├── blocks_file_test.go
│ ├── errors.go
│ ├── file_header.go
│ ├── file_header_lazy.go
│ ├── file_header_lazy_test.go
│ ├── file_header_test.go
│ ├── file_reader.go
│ ├── file_reader_test.go
│ ├── file_writer.go
│ ├── file_writer_test.go
│ ├── fs.go
│ ├── fs_options.go
│ ├── fs_test.go
│ ├── gzip_reader_pool.go
│ ├── gzip_writer_pool.go
│ ├── hash.go
│ ├── hash_test.go
│ ├── meta_block.go
│ ├── meta_block_test.go
│ ├── meta_file.go
│ ├── meta_file_test.go
│ └── threads_limiter.go
├── buffer_pool.go
├── buffer_pool_test.go
├── byte
│ ├── utils.go
│ └── utils_test.go
├── bytepool
│ ├── byte_pool.go
│ └── byte_pool_test.go
├── cachehits
│ ├── stat.go
│ └── stat_test.go
├── clock
│ ├── manager.go
│ ├── manager_test.go
│ └── ntp_packet.go
├── common_files.go
├── common_files_test.go
├── conns
│ └── conn_no_stat.go
├── counters
│ ├── counter.go
│ ├── counter_test.go
│ ├── item.go
│ └── item_test.go
├── dbs
│ ├── batch.go
│ ├── db.go
│ ├── db_test.go
│ ├── query_label.go
│ ├── query_stat.go
│ ├── query_stat_manager.go
│ ├── query_stat_manager_test.go
│ ├── stmt.go
│ └── utils.go
├── encrypt.go
├── encrypt_test.go
├── errors.go
├── exec
│ ├── cmd.go
│ ├── cmd_test.go
│ ├── look_linux.go
│ └── look_others.go
├── exit.go
├── expires
│ ├── id_key_map.go
│ ├── id_key_map_test.go
│ ├── list.go
│ ├── list_test.go
│ └── manager.go
├── fasttime
│ ├── time_fast.go
│ └── time_fast_test.go
├── fnv
│ ├── hash.go
│ └── hash_test.go
├── fs
│ ├── disk.go
│ ├── disk_test_test.go
│ ├── file.go
│ ├── file_test.go
│ ├── limiter.go
│ ├── limiter_test.go
│ ├── locker.go
│ ├── locker_test.go
│ ├── os.go
│ ├── os_test.go
│ ├── stat.go
│ ├── stat_test.go
│ ├── status.go
│ └── status_test.go
├── get.go
├── get_test.go
├── http.go
├── http_test.go
├── idles
│ ├── run.go
│ └── run_test.go
├── ip.go
├── ip_test.go
├── jsonutils
│ ├── map.go
│ ├── map_test.go
│ ├── utils.go
│ └── utils_test.go
├── kvstore
│ ├── db.go
│ ├── db_test.go
│ ├── errors.go
│ ├── item.go
│ ├── iterator_options.go
│ ├── logger.go
│ ├── options.go
│ ├── query.go
│ ├── query_test.go
│ ├── store.go
│ ├── store_test.go
│ ├── table.go
│ ├── table_counter.go
│ ├── table_counter_test.go
│ ├── table_field.go
│ ├── table_field_test.go
│ ├── table_interface.go
│ ├── table_test.go
│ ├── tx.go
│ ├── tx_test.go
│ ├── utils.go
│ ├── utils_test.go
│ ├── value_encode_int.go
│ ├── value_encoder.go
│ ├── value_encoder_bool.go
│ ├── value_encoder_bytes.go
│ ├── value_encoder_nil.go
│ ├── value_encoder_string.go
│ └── value_encoder_test.go
├── linkedlist
│ ├── item.go
│ ├── list.go
│ └── list_test.go
├── lookup.go
├── lookup_test.go
├── maps
│ ├── map_fixed.go
│ └── map_fixed_test.go
├── mem
│ ├── system.go
│ ├── system_1.19.go
│ ├── system_before_1.19.go
│ └── system_test.go
├── minifiers
│ └── minify.go
├── net.go
├── net_darwin.go
├── net_linux.go
├── net_test.go
├── net_utils.go
├── number.go
├── path.go
├── path_test.go
├── percpu
│ ├── chan.go
│ ├── chan_test.go
│ └── proc_id.go
├── ranges
│ ├── range.go
│ └── range_test.go
├── ratelimit
│ ├── bandwidth.go
│ ├── bandwidth_test.go
│ ├── counter.go
│ └── counter_test.go
├── reader_utils.go
├── readers
│ ├── .gitignore
│ ├── handlers.go
│ ├── reader_base.go
│ ├── reader_bytes_counter.go
│ ├── reader_closer_byte_ranges.go
│ ├── reader_closer_byte_ranges_test.go
│ ├── reader_closer_filter.go
│ ├── reader_closer_filter_test.go
│ ├── reader_closer_tee.go
│ ├── reader_print.go
│ └── reader_tee.go
├── rlimit_darwin.go
├── rlimit_linux.go
├── rlimit_others.go
├── runes
│ ├── runes.go
│ └── runes_test.go
├── service.go
├── service_linux.go
├── service_others.go
├── service_test.go
├── service_windows.go
├── sets
│ ├── set_fixed.go
│ └── set_fixed_test.go
├── string.go
├── string_test.go
├── sync
│ ├── .gitignore
│ ├── map_int.go
│ ├── map_int_test.go
│ ├── rw_mutex.go
│ └── rw_mutex_test.go
├── testutils
│ ├── memory.go
│ └── utils.go
├── ticker.go
├── ticker_test.go
├── ticker_utils.go
├── time.go
├── time_test.go
├── unzip.go
├── version.go
├── version_test.go
├── workspace.go
└── writers
│ ├── writer_bytes_counter.go
│ ├── writer_closer_tee.go
│ ├── writer_print.go
│ ├── writer_rate_limit.go
│ └── writer_rate_limit_test.go
├── waf
├── README.md
├── action_allow.go
├── action_base.go
├── action_block.go
├── action_captcha.go
├── action_category.go
├── action_config.go
├── action_definition.go
├── action_get_302.go
├── action_go_group.go
├── action_go_set.go
├── action_instance.go
├── action_interface.go
├── action_js_cookie.go
├── action_log.go
├── action_notify.go
├── action_page.go
├── action_post_307.go
├── action_record_ip.go
├── action_redirect.go
├── action_tag.go
├── action_types.go
├── action_utils.go
├── action_utils_test.go
├── allow_cookie_info.go
├── allow_cookie_info_test.go
├── captcha_counter.go
├── captcha_generator.go
├── captcha_generator_test.go
├── captcha_test.go
├── captcha_validator.go
├── checkpoints
│ ├── cc.go
│ ├── cc2.go
│ ├── cc_test.go
│ ├── checkpoint.go
│ ├── checkpoint_definition.go
│ ├── checkpoint_interface.go
│ ├── option.go
│ ├── option_field.go
│ ├── option_options.go
│ ├── param_option.go
│ ├── request_all.go
│ ├── request_all_test.go
│ ├── request_arg.go
│ ├── request_arg_test.go
│ ├── request_args.go
│ ├── request_body.go
│ ├── request_body_test.go
│ ├── request_cname.go
│ ├── request_content_type.go
│ ├── request_cookie.go
│ ├── request_cookies.go
│ ├── request_form_arg.go
│ ├── request_form_arg_test.go
│ ├── request_general_header_length.go
│ ├── request_geo_city_name.go
│ ├── request_geo_country_name.go
│ ├── request_geo_province_name.go
│ ├── request_header.go
│ ├── request_header_names.go
│ ├── request_header_names_test.go
│ ├── request_headers.go
│ ├── request_headers_test.go
│ ├── request_host.go
│ ├── request_host_test.go
│ ├── request_is_cname.go
│ ├── request_isp_name.go
│ ├── request_json_arg.go
│ ├── request_json_arg_test.go
│ ├── request_length.go
│ ├── request_method.go
│ ├── request_path.go
│ ├── request_path_test.go
│ ├── request_proto.go
│ ├── request_raw_remote_addr.go
│ ├── request_referer.go
│ ├── request_referer_block.go
│ ├── request_referer_origin.go
│ ├── request_referer_origin_test.go
│ ├── request_remote_addr.go
│ ├── request_remote_port.go
│ ├── request_remote_user.go
│ ├── request_scheme.go
│ ├── request_scheme_test.go
│ ├── request_upload.go
│ ├── request_upload_test.go
│ ├── request_uri.go
│ ├── request_url.go
│ ├── request_user_agent.go
│ ├── response_body.go
│ ├── response_body_test.go
│ ├── response_bytes_sent.go
│ ├── response_general_header_length.go
│ ├── response_header.go
│ ├── response_header_test.go
│ ├── response_status.go
│ ├── response_status_test.go
│ ├── sample_request.go
│ ├── sample_response.go
│ ├── utils.go
│ └── utils_test.go
├── get302_validator.go
├── info_arg.go
├── info_arg_test.go
├── injectionutils
│ ├── libinjection
│ │ ├── COPYING
│ │ ├── README.md
│ │ └── src
│ │ │ ├── libinjection.h
│ │ │ ├── libinjection_html5.c
│ │ │ ├── libinjection_html5.h
│ │ │ ├── libinjection_sqli.c
│ │ │ ├── libinjection_sqli.h
│ │ │ ├── libinjection_sqli_data.h
│ │ │ ├── libinjection_xss.c
│ │ │ ├── libinjection_xss.h
│ │ │ ├── reader.c
│ │ │ ├── sqli_cli.c
│ │ │ └── sqlparse2c.py
│ ├── libinjection_sqli.c
│ ├── libinjection_xss.c
│ ├── utils_sqli.go
│ ├── utils_sqli_test.go
│ ├── utils_xss.go
│ └── utils_xss_test.go
├── ip_list.go
├── ip_list_test.go
├── ip_lists_deleted.go
├── param_filter.go
├── requests
│ ├── request.go
│ ├── response.go
│ └── test_request.go
├── results.go
├── rule.go
├── rule_group.go
├── rule_operator.go
├── rule_set.go
├── rule_set_test.go
├── rule_test.go
├── template.go
├── template_test.go
├── utils
│ ├── utils.go
│ └── utils_test.go
├── values
│ ├── ip_range.go
│ ├── ip_range_test.go
│ ├── number_list.go
│ ├── number_list_test.go
│ ├── string_list.go
│ └── string_list_test.go
├── waf.go
├── waf_manager.go
├── waf_manager_test.go
└── waf_test.go
└── zero
├── zero.go
└── zero_test.go
/.gitignore:
--------------------------------------------------------------------------------
1 | *_plus.go
2 | *_plus_test.go
3 | *-plus.sh
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | GoEdge边缘节点源码
--------------------------------------------------------------------------------
/build/.gitignore:
--------------------------------------------------------------------------------
1 | bin/*
2 | caches
3 | upload.sh
--------------------------------------------------------------------------------
/build/build-all.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ./build.sh linux amd64
4 | #./build.sh linux 386
5 | ./build.sh linux arm64
6 | #./build.sh linux mips64
7 | #./build.sh linux mips64le
8 | #./build.sh darwin amd64
9 | #./build.sh darwin arm64
--------------------------------------------------------------------------------
/build/configs/.gitignore:
--------------------------------------------------------------------------------
1 | node.json
2 | api.yaml
3 | api_node.yaml
4 | cluster.yaml
5 | api_cluster.yaml
6 | *.cache
--------------------------------------------------------------------------------
/build/configs/README.md:
--------------------------------------------------------------------------------
1 | * `api_node.template.yaml` - API相关配置模板
2 | * `cluster.template.yaml` - 通过集群自动接入节点模板
--------------------------------------------------------------------------------
/build/configs/api_node.template.yaml:
--------------------------------------------------------------------------------
1 | rpc.endpoints: [ "" ]
2 | nodeId: ""
3 | secret: ""
--------------------------------------------------------------------------------
/build/configs/cluster.template.yaml:
--------------------------------------------------------------------------------
1 | rpc:
2 | endpoints: [ "" ]
3 | clusterId: ""
4 | secret: ""
--------------------------------------------------------------------------------
/build/data/.gitignore:
--------------------------------------------------------------------------------
1 | *
--------------------------------------------------------------------------------
/build/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
--------------------------------------------------------------------------------
/build/pages/403.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Error
5 |
6 |
7 |
8 |
9 | 403 Forbidden
10 | Sorry, your access to the page has been denied. Please try again later.
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/build/pages/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Error
5 |
6 |
7 |
8 |
9 | 404 Not Found
10 | Sorry, the page you are looking for is not found. Please try again later.
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/build/pages/50x.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Error
5 |
6 |
7 |
8 |
9 | An error occurred.
10 | Sorry, the page you are looking for is currently unavailable. Please try again later.
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/build/pages/shutdown_en.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Shutdown Notice
5 |
6 |
7 |
8 |
9 | The website is shutdown.
10 | Sorry, the page you are looking for is currently unavailable. Please try again later.
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/build/pages/shutdown_upgrade_zh.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 升级中
5 |
6 |
7 |
8 |
9 | 网站升级中
10 | 为了给您提供更好的服务,我们正在升级网站,请稍后重新访问。
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/build/pages/shutdown_zh.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 临时关闭提醒
5 |
6 |
7 |
8 |
9 | 网站暂时关闭
10 | 网站已被暂时关闭,请耐心等待我们的重新开通通知。
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/build/test.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | TAG=${1}
4 |
5 | if [ -z "$TAG" ]; then
6 | TAG="community"
7 | fi
8 |
9 | # stop node
10 | go run -tags=${TAG} ../cmd/edge-node/main.go stop
11 |
12 | # reference: https://pkg.go.dev/cmd/go/internal/test
13 | go clean -testcache
14 | go test -timeout 60s -tags="${TAG}" -cover ../...
--------------------------------------------------------------------------------
/build/www/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoEdgeLab/EdgeNode/be22116236164c895a3163331b97a3daa72414d3/build/www/.gitignore
--------------------------------------------------------------------------------
/build/www/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Welcome
5 |
6 |
7 | I am index.
8 |
9 |
--------------------------------------------------------------------------------
/dist/.gitignore:
--------------------------------------------------------------------------------
1 | *.zip
2 | edge-node
--------------------------------------------------------------------------------
/internal/apps/directive.go:
--------------------------------------------------------------------------------
1 | package apps
2 |
3 | type Directive struct {
4 | Arg string
5 | Callback func()
6 | }
7 |
--------------------------------------------------------------------------------
/internal/apps/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package apps
4 |
5 | import teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
6 |
7 | func RunMain(f func()) {
8 | if teaconst.IsMain {
9 | f()
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/internal/caches/consts.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package caches
4 |
5 | const (
6 | SuffixAll = "@GOEDGE_" // 通用后缀
7 | SuffixWebP = "@GOEDGE_WEBP" // WebP后缀
8 | SuffixCompression = "@GOEDGE_" // 压缩后缀 SuffixCompression + Encoding
9 | SuffixMethod = "@GOEDGE_" // 请求方法后缀 SuffixMethod + RequestMethod
10 | SuffixPartial = "@GOEDGE_partial" // 分区缓存后缀
11 | )
12 |
--------------------------------------------------------------------------------
/internal/caches/errros_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package caches_test
4 |
5 | import (
6 | "errors"
7 | "fmt"
8 | "github.com/TeaOSLab/EdgeNode/internal/caches"
9 | "github.com/iwind/TeaGo/assert"
10 | "testing"
11 | )
12 |
13 | func TestCanIgnoreErr(t *testing.T) {
14 | var a = assert.NewAssertion(t)
15 |
16 | a.IsTrue(caches.CanIgnoreErr(caches.ErrFileIsWriting))
17 | a.IsTrue(caches.CanIgnoreErr(fmt.Errorf("error: %w", caches.ErrFileIsWriting)))
18 | a.IsTrue(errors.Is(fmt.Errorf("error: %w", caches.ErrFileIsWriting), caches.ErrFileIsWriting))
19 | a.IsTrue(errors.Is(caches.ErrFileIsWriting, caches.ErrFileIsWriting))
20 | a.IsTrue(caches.CanIgnoreErr(caches.NewCapacityError("over capacity")))
21 | a.IsTrue(caches.CanIgnoreErr(fmt.Errorf("error: %w", caches.NewCapacityError("over capacity"))))
22 | a.IsFalse(caches.CanIgnoreErr(caches.ErrNotFound))
23 | a.IsFalse(caches.CanIgnoreErr(errors.New("test error")))
24 | }
25 |
--------------------------------------------------------------------------------
/internal/caches/file_dir.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package caches
4 |
5 | import "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
6 |
7 | type FileDir struct {
8 | Path string
9 | Capacity *shared.SizeCapacity
10 | IsFull bool
11 | }
12 |
--------------------------------------------------------------------------------
/internal/caches/hot_item.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package caches
4 |
5 | type HotItem struct {
6 | Key string
7 | Hits uint32
8 | }
9 |
--------------------------------------------------------------------------------
/internal/caches/open_file.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package caches
4 |
5 | import (
6 | "io"
7 | "os"
8 | )
9 |
10 | type OpenFile struct {
11 | fp *os.File
12 | meta []byte
13 | header []byte
14 | version int64
15 | size int64
16 | }
17 |
18 | func NewOpenFile(fp *os.File, meta []byte, header []byte, version int64, size int64) *OpenFile {
19 | return &OpenFile{
20 | fp: fp,
21 | meta: meta,
22 | header: header,
23 | version: version,
24 | size: size,
25 | }
26 | }
27 |
28 | func (this *OpenFile) SeekStart() error {
29 | _, err := this.fp.Seek(0, io.SeekStart)
30 | return err
31 | }
32 |
33 | func (this *OpenFile) Close() error {
34 | this.meta = nil
35 | return this.fp.Close()
36 | }
37 |
--------------------------------------------------------------------------------
/internal/caches/partial_ranges_queue_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package caches_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/caches"
7 | "github.com/iwind/TeaGo/assert"
8 | "testing"
9 | )
10 |
11 | func TestNewPartialRangesQueue(t *testing.T) {
12 | var a = assert.NewAssertion(t)
13 |
14 | var queue = caches.NewPartialRangesQueue()
15 | queue.Put("a", []byte{1, 2, 3})
16 | t.Log("add 'a':", queue.Len())
17 | t.Log(queue.Get("a"))
18 | a.IsTrue(queue.Len() == 1)
19 |
20 | queue.Put("a", nil)
21 | t.Log("add 'a':", queue.Len())
22 | a.IsTrue(queue.Len() == 1)
23 |
24 | queue.Put("b", nil)
25 | t.Log("add 'b':", queue.Len())
26 | a.IsTrue(queue.Len() == 2)
27 |
28 | queue.Delete("a")
29 | t.Log("delete 'a':", queue.Len())
30 | a.IsTrue(queue.Len() == 1)
31 | }
32 |
--------------------------------------------------------------------------------
/internal/caches/reader.go:
--------------------------------------------------------------------------------
1 | package caches
2 |
3 | import "github.com/TeaOSLab/EdgeNode/internal/utils/ranges"
4 |
5 | type ReaderFunc func(n int) (goNext bool, err error)
6 |
7 | type Reader interface {
8 | // Init 初始化
9 | Init() error
10 |
11 | // TypeName 类型名称
12 | TypeName() string
13 |
14 | // ExpiresAt 过期时间
15 | ExpiresAt() int64
16 |
17 | // Status 状态码
18 | Status() int
19 |
20 | // LastModified 最后修改时间
21 | LastModified() int64
22 |
23 | // ReadHeader 读取Header
24 | ReadHeader(buf []byte, callback ReaderFunc) error
25 |
26 | // ReadBody 读取Body
27 | ReadBody(buf []byte, callback ReaderFunc) error
28 |
29 | // Read 实现io.Reader接口
30 | Read(buf []byte) (int, error)
31 |
32 | // ReadBodyRange 读取某个范围内的Body
33 | ReadBodyRange(buf []byte, start int64, end int64, callback ReaderFunc) error
34 |
35 | // HeaderSize Header Size
36 | HeaderSize() int64
37 |
38 | // BodySize Body Size
39 | BodySize() int64
40 |
41 | // ContainsRange 是否包含某个区间内容
42 | ContainsRange(r rangeutils.Range) (r2 rangeutils.Range, ok bool)
43 |
44 | // Close 关闭
45 | Close() error
46 | }
47 |
--------------------------------------------------------------------------------
/internal/caches/reader_file_mmap.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 | //go:build !plus
3 |
4 | package caches
5 |
6 | import (
7 | "errors"
8 | "io"
9 | )
10 |
11 | type MMAPFileReader struct {
12 | FileReader
13 | }
14 |
15 | func (this *MMAPFileReader) CopyBodyTo(writer io.Writer) (int, error) {
16 | // stub
17 | return 0, errors.New("not implemented")
18 | }
19 |
--------------------------------------------------------------------------------
/internal/caches/stat.go:
--------------------------------------------------------------------------------
1 | package caches
2 |
3 | type Stat struct {
4 | Count int // 数量
5 | ValueSize int64 // 值占用的空间
6 | Size int64 // 占用的空间尺寸
7 | }
8 |
--------------------------------------------------------------------------------
/internal/caches/storage_file_ext.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 | //go:build !plus
3 |
4 | package caches
5 |
6 | func (this *FileStorage) tryMMAPReader(isPartial bool, estimatedSize int64, path string) (Reader, error) {
7 | // stub
8 | return nil, nil
9 | }
10 |
--------------------------------------------------------------------------------
/internal/caches/utils.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package caches
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeCommon/pkg/configutils"
7 | "net"
8 | "strings"
9 | )
10 |
11 | func ParseHost(key string) string {
12 | var schemeIndex = strings.Index(key, "://")
13 | if schemeIndex <= 0 {
14 | return ""
15 | }
16 |
17 | var firstSlashIndex = strings.Index(key[schemeIndex+3:], "/")
18 | if firstSlashIndex <= 0 {
19 | return ""
20 | }
21 |
22 | var host = key[schemeIndex+3 : schemeIndex+3+firstSlashIndex]
23 |
24 | hostPart, _, err := net.SplitHostPort(host)
25 | if err == nil && len(hostPart) > 0 {
26 | host = configutils.QuoteIP(hostPart)
27 | }
28 |
29 | return host
30 | }
31 |
--------------------------------------------------------------------------------
/internal/caches/utils_partial.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package caches
4 |
5 | import "strings"
6 |
7 | // PartialRangesFilePath 获取 ranges 文件路径
8 | func PartialRangesFilePath(path string) string {
9 | // ranges路径
10 | var dotIndex = strings.LastIndex(path, ".")
11 | var rangePath string
12 | if dotIndex < 0 {
13 | rangePath = path + "@ranges.cache"
14 | } else {
15 | rangePath = path[:dotIndex] + "@ranges" + path[dotIndex:]
16 | }
17 | return rangePath
18 | }
19 |
--------------------------------------------------------------------------------
/internal/caches/writer.go:
--------------------------------------------------------------------------------
1 | package caches
2 |
3 | // Writer 缓存内容写入接口
4 | type Writer interface {
5 | // WriteHeader 写入Header数据
6 | WriteHeader(data []byte) (n int, err error)
7 |
8 | // Write 写入Body数据
9 | Write(data []byte) (n int, err error)
10 |
11 | // WriteAt 在指定位置写入数据
12 | WriteAt(offset int64, data []byte) error
13 |
14 | // HeaderSize 写入的Header数据大小
15 | HeaderSize() int64
16 |
17 | // BodySize 写入的Body数据大小
18 | BodySize() int64
19 |
20 | // Close 关闭
21 | Close() error
22 |
23 | // Discard 丢弃
24 | Discard() error
25 |
26 | // Key Key
27 | Key() string
28 |
29 | // ExpiredAt 过期时间
30 | ExpiredAt() int64
31 |
32 | // ItemType 内容类型
33 | ItemType() ItemType
34 | }
35 |
--------------------------------------------------------------------------------
/internal/compressions/errors.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package compressions
4 |
5 | import "errors"
6 |
7 | var ErrIsBusy = errors.New("the system is busy for compression")
8 |
9 | func CanIgnore(err error) bool {
10 | if err == nil {
11 | return true
12 | }
13 | return errors.Is(err, ErrIsBusy)
14 | }
15 |
--------------------------------------------------------------------------------
/internal/compressions/reader.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions
4 |
5 | import "io"
6 |
7 | type Reader interface {
8 | Read(p []byte) (n int, err error)
9 | Reset(reader io.Reader) error
10 | RawClose() error
11 | Close() error
12 | IncreaseHit() uint32
13 |
14 | SetPool(pool *ReaderPool)
15 | ResetFinish()
16 | }
17 |
--------------------------------------------------------------------------------
/internal/compressions/reader_base.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions
4 |
5 | import "sync/atomic"
6 |
7 | type BaseReader struct {
8 | pool *ReaderPool
9 |
10 | isFinished bool
11 | hits uint32
12 | }
13 |
14 | func (this *BaseReader) SetPool(pool *ReaderPool) {
15 | this.pool = pool
16 | }
17 |
18 | func (this *BaseReader) Finish(obj Reader) error {
19 | if this.isFinished {
20 | return nil
21 | }
22 | err := obj.RawClose()
23 | if err == nil && this.pool != nil {
24 | this.pool.Put(obj)
25 | }
26 | this.isFinished = true
27 | return err
28 | }
29 |
30 | func (this *BaseReader) ResetFinish() {
31 | this.isFinished = false
32 | }
33 |
34 | func (this *BaseReader) IncreaseHit() uint32 {
35 | return atomic.AddUint32(&this.hits, 1)
36 | }
37 |
--------------------------------------------------------------------------------
/internal/compressions/reader_brotli.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build !plus || !linux
3 |
4 | package compressions
5 |
6 | import (
7 | "github.com/andybalholm/brotli"
8 | "io"
9 | "strings"
10 | )
11 |
12 | type BrotliReader struct {
13 | BaseReader
14 |
15 | reader *brotli.Reader
16 | }
17 |
18 | func NewBrotliReader(reader io.Reader) (Reader, error) {
19 | return sharedBrotliReaderPool.Get(reader)
20 | }
21 |
22 | func newBrotliReader(reader io.Reader) (Reader, error) {
23 | return &BrotliReader{reader: brotli.NewReader(reader)}, nil
24 | }
25 |
26 | func (this *BrotliReader) Read(p []byte) (n int, err error) {
27 | n, err = this.reader.Read(p)
28 | if err != nil && strings.Contains(err.Error(), "excessive") {
29 | err = io.EOF
30 | }
31 | return
32 | }
33 |
34 | func (this *BrotliReader) Reset(reader io.Reader) error {
35 | return this.reader.Reset(reader)
36 | }
37 |
38 | func (this *BrotliReader) RawClose() error {
39 | return nil
40 | }
41 |
42 | func (this *BrotliReader) Close() error {
43 | return this.Finish(this)
44 | }
45 |
--------------------------------------------------------------------------------
/internal/compressions/reader_deflate.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions
4 |
5 | import (
6 | "compress/flate"
7 | "io"
8 | )
9 |
10 | type DeflateReader struct {
11 | BaseReader
12 |
13 | reader io.ReadCloser
14 | }
15 |
16 | func NewDeflateReader(reader io.Reader) (Reader, error) {
17 | return sharedDeflateReaderPool.Get(reader)
18 | }
19 |
20 | func newDeflateReader(reader io.Reader) (Reader, error) {
21 | return &DeflateReader{reader: flate.NewReader(reader)}, nil
22 | }
23 |
24 | func (this *DeflateReader) Read(p []byte) (n int, err error) {
25 | return this.reader.Read(p)
26 | }
27 |
28 | func (this *DeflateReader) Reset(reader io.Reader) error {
29 | this.reader = flate.NewReader(reader)
30 | return nil
31 | }
32 |
33 | func (this *DeflateReader) RawClose() error {
34 | return this.reader.Close()
35 | }
36 |
37 | func (this *DeflateReader) Close() error {
38 | return this.Finish(this)
39 | }
40 |
--------------------------------------------------------------------------------
/internal/compressions/reader_gzip.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions
4 |
5 | import (
6 | "github.com/klauspost/compress/gzip"
7 | "io"
8 | )
9 |
10 | type GzipReader struct {
11 | BaseReader
12 |
13 | reader *gzip.Reader
14 | }
15 |
16 | func NewGzipReader(reader io.Reader) (Reader, error) {
17 | return sharedGzipReaderPool.Get(reader)
18 | }
19 |
20 | func newGzipReader(reader io.Reader) (Reader, error) {
21 | r, err := gzip.NewReader(reader)
22 | if err != nil {
23 | return nil, err
24 | }
25 | return &GzipReader{
26 | reader: r,
27 | }, nil
28 | }
29 |
30 | func (this *GzipReader) Read(p []byte) (n int, err error) {
31 | return this.reader.Read(p)
32 | }
33 |
34 | func (this *GzipReader) Reset(reader io.Reader) error {
35 | return this.reader.Reset(reader)
36 | }
37 |
38 | func (this *GzipReader) RawClose() error {
39 | return this.reader.Close()
40 | }
41 |
42 | func (this *GzipReader) Close() error {
43 | return this.Finish(this)
44 | }
45 |
--------------------------------------------------------------------------------
/internal/compressions/reader_pool_brotli.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions
4 |
5 | import (
6 | teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
7 | "io"
8 | )
9 |
10 | var sharedBrotliReaderPool *ReaderPool
11 |
12 | func init() {
13 | if !teaconst.IsMain {
14 | return
15 | }
16 |
17 |
18 | sharedBrotliReaderPool = NewReaderPool(CalculatePoolSize(), func(reader io.Reader) (Reader, error) {
19 | return newBrotliReader(reader)
20 | })
21 | }
22 |
--------------------------------------------------------------------------------
/internal/compressions/reader_pool_deflate.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions
4 |
5 | import (
6 | teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
7 | "io"
8 | )
9 |
10 | var sharedDeflateReaderPool *ReaderPool
11 |
12 | func init() {
13 | if !teaconst.IsMain {
14 | return
15 | }
16 |
17 | sharedDeflateReaderPool = NewReaderPool(CalculatePoolSize(), func(reader io.Reader) (Reader, error) {
18 | return newDeflateReader(reader)
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/internal/compressions/reader_pool_gzip.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions
4 |
5 | import (
6 | teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
7 | "io"
8 | )
9 |
10 | var sharedGzipReaderPool *ReaderPool
11 |
12 | func init() {
13 | if !teaconst.IsMain {
14 | return
15 | }
16 |
17 | sharedGzipReaderPool = NewReaderPool(CalculatePoolSize(), func(reader io.Reader) (Reader, error) {
18 | return newGzipReader(reader)
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/internal/compressions/reader_pool_zstd.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions
4 |
5 | import (
6 | teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
7 | "io"
8 | )
9 |
10 | var sharedZSTDReaderPool *ReaderPool
11 |
12 | func init() {
13 | if !teaconst.IsMain {
14 | return
15 | }
16 |
17 | sharedZSTDReaderPool = NewReaderPool(CalculatePoolSize(), func(reader io.Reader) (Reader, error) {
18 | return newZSTDReader(reader)
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/internal/compressions/reader_zstd.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions
4 |
5 | import (
6 | "github.com/klauspost/compress/zstd"
7 | "io"
8 | )
9 |
10 | type ZSTDReader struct {
11 | BaseReader
12 |
13 | reader *zstd.Decoder
14 | }
15 |
16 | func NewZSTDReader(reader io.Reader) (Reader, error) {
17 | return sharedZSTDReaderPool.Get(reader)
18 | }
19 |
20 | func newZSTDReader(reader io.Reader) (Reader, error) {
21 | r, err := zstd.NewReader(reader, zstd.WithDecoderMaxWindow(256<<20))
22 | if err != nil {
23 | return nil, err
24 | }
25 | return &ZSTDReader{
26 | reader: r,
27 | }, nil
28 | }
29 |
30 | func (this *ZSTDReader) Read(p []byte) (n int, err error) {
31 | return this.reader.Read(p)
32 | }
33 |
34 | func (this *ZSTDReader) Reset(reader io.Reader) error {
35 | return this.reader.Reset(reader)
36 | }
37 |
38 | func (this *ZSTDReader) RawClose() error {
39 | this.reader.Close()
40 | return nil
41 | }
42 |
43 | func (this *ZSTDReader) Close() error {
44 | return this.Finish(this)
45 | }
46 |
--------------------------------------------------------------------------------
/internal/compressions/utils_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package compressions_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/compressions"
7 | "github.com/iwind/TeaGo/assert"
8 | "testing"
9 | )
10 |
11 | func TestGenerateCompressLevel(t *testing.T) {
12 | var a = assert.NewAssertion(t)
13 |
14 | t.Log(compressions.GenerateCompressLevel(0, 10))
15 | t.Log(compressions.GenerateCompressLevel(1, 10))
16 | t.Log(compressions.GenerateCompressLevel(1, 4))
17 |
18 | {
19 | var level = compressions.GenerateCompressLevel(1, 2)
20 | t.Log(level)
21 | a.IsTrue(level >= 1 && level <= 2)
22 | }
23 | }
24 |
25 | func TestCalculatePoolSize(t *testing.T) {
26 | t.Log(compressions.CalculatePoolSize())
27 | }
28 |
--------------------------------------------------------------------------------
/internal/compressions/writer.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions
4 |
5 | import "io"
6 |
7 | type Writer interface {
8 | Write(p []byte) (int, error)
9 | Flush() error
10 | Reset(writer io.Writer)
11 | RawClose() error
12 | Close() error
13 | Level() int
14 | IncreaseHit() uint32
15 |
16 | SetPool(pool *WriterPool)
17 | ResetFinish()
18 | }
19 |
--------------------------------------------------------------------------------
/internal/compressions/writer_base.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions
4 |
5 | import (
6 | "sync/atomic"
7 | )
8 |
9 | type BaseWriter struct {
10 | pool *WriterPool
11 |
12 | isFinished bool
13 |
14 | hits uint32
15 | }
16 |
17 | func (this *BaseWriter) SetPool(pool *WriterPool) {
18 | this.pool = pool
19 | }
20 |
21 | func (this *BaseWriter) Finish(obj Writer) error {
22 | if this.isFinished {
23 | return nil
24 | }
25 | err := obj.RawClose()
26 | if err == nil && this.pool != nil {
27 | this.pool.Put(obj)
28 | }
29 | this.isFinished = true
30 | return err
31 | }
32 |
33 | func (this *BaseWriter) ResetFinish() {
34 | this.isFinished = false
35 | }
36 |
37 | func (this *BaseWriter) IncreaseHit() uint32 {
38 | return atomic.AddUint32(&this.hits, 1)
39 | }
40 |
--------------------------------------------------------------------------------
/internal/compressions/writer_deflate_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions_test
4 |
5 | import (
6 | "bytes"
7 | "github.com/TeaOSLab/EdgeNode/internal/compressions"
8 | "strings"
9 | "testing"
10 | )
11 |
12 | func BenchmarkDeflateWriter_Write(b *testing.B) {
13 | var data = []byte(strings.Repeat("A", 1024))
14 |
15 | for i := 0; i < b.N; i++ {
16 | var buf = &bytes.Buffer{}
17 | writer, err := compressions.NewDeflateWriter(buf, 5)
18 | if err != nil {
19 | b.Fatal(err)
20 | }
21 |
22 | for j := 0; j < 100; j++ {
23 | _, err = writer.Write(data)
24 | if err != nil {
25 | b.Fatal(err)
26 | }
27 |
28 | /**err = writer.Flush()
29 | if err != nil {
30 | b.Fatal(err)
31 | }**/
32 | }
33 |
34 | _ = writer.Close()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/internal/compressions/writer_pool_brotli.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions
4 |
5 | import (
6 | teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
7 | "io"
8 | )
9 |
10 | var sharedBrotliWriterPool *WriterPool
11 |
12 | func init() {
13 | if !teaconst.IsMain {
14 | return
15 | }
16 |
17 | sharedBrotliWriterPool = NewWriterPool(CalculatePoolSize(), func(writer io.Writer, level int) (Writer, error) {
18 | return newBrotliWriter(writer)
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/internal/compressions/writer_pool_deflate.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions
4 |
5 | import (
6 | teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
7 | "io"
8 | )
9 |
10 | var sharedDeflateWriterPool *WriterPool
11 |
12 | func init() {
13 | if !teaconst.IsMain {
14 | return
15 | }
16 |
17 | sharedDeflateWriterPool = NewWriterPool(CalculatePoolSize(), func(writer io.Writer, level int) (Writer, error) {
18 | return newDeflateWriter(writer)
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/internal/compressions/writer_pool_gzip.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions
4 |
5 | import (
6 | teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
7 | "io"
8 | )
9 |
10 | var sharedGzipWriterPool *WriterPool
11 |
12 | func init() {
13 | if !teaconst.IsMain {
14 | return
15 | }
16 |
17 | sharedGzipWriterPool = NewWriterPool(CalculatePoolSize(), func(writer io.Writer, level int) (Writer, error) {
18 | return newGzipWriter(writer)
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/internal/compressions/writer_pool_zstd.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package compressions
4 |
5 | import (
6 | teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
7 | "io"
8 | )
9 |
10 | var sharedZSTDWriterPool *WriterPool
11 |
12 | func init() {
13 | if !teaconst.IsMain {
14 | return
15 | }
16 |
17 | sharedZSTDWriterPool = NewWriterPool(CalculatePoolSize(), func(writer io.Writer, level int) (Writer, error) {
18 | return newZSTDWriter(writer)
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/internal/configs/api_config_test.go:
--------------------------------------------------------------------------------
1 | package configs_test
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/configs"
5 | _ "github.com/iwind/TeaGo/bootstrap"
6 | "gopkg.in/yaml.v3"
7 | "testing"
8 | )
9 |
10 | func TestLoadAPIConfig(t *testing.T) {
11 | config, err := configs.LoadAPIConfig()
12 | if err != nil {
13 | t.Fatal(err)
14 | }
15 | t.Logf("%+v", config)
16 |
17 | configData, err := yaml.Marshal(config)
18 | if err != nil {
19 | t.Fatal(err)
20 | }
21 | t.Log(string(configData))
22 | }
23 |
--------------------------------------------------------------------------------
/internal/configs/cluster_config_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package configs_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/configs"
7 | "github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
8 | "gopkg.in/yaml.v3"
9 | "testing"
10 | )
11 |
12 | func TestLoadClusterConfig(t *testing.T) {
13 | if !testutils.IsSingleTesting() {
14 | return
15 | }
16 |
17 | config, err := configs.LoadClusterConfig()
18 | if err != nil {
19 | t.Fatal(err)
20 | }
21 | t.Logf("%+v", config)
22 |
23 | configData, err := yaml.Marshal(config)
24 | if err != nil {
25 | t.Fatal(err)
26 | }
27 | t.Log(string(configData))
28 | }
29 |
--------------------------------------------------------------------------------
/internal/conns/linger.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package conns
4 |
5 | type LingerConn interface {
6 | SetLinger(sec int) error
7 | }
8 |
--------------------------------------------------------------------------------
/internal/const/build.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build !plus
3 | // +build !plus
4 |
5 | package teaconst
6 |
7 | const BuildCommunity = true
8 | const BuildPlus = false
9 | const Tag = "community"
10 |
--------------------------------------------------------------------------------
/internal/const/build_plus.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build plus
3 | // +build plus
4 |
5 | package teaconst
6 |
7 | const BuildCommunity = false
8 | const BuildPlus = true
9 | const Tag = "plus"
10 |
--------------------------------------------------------------------------------
/internal/const/const.go:
--------------------------------------------------------------------------------
1 | package teaconst
2 |
3 | const (
4 | Version = "1.3.8.2"
5 |
6 | ProductName = "Edge Node"
7 | ProcessName = "edge-node"
8 |
9 | Role = "node"
10 |
11 | EncryptMethod = "aes-256-cfb"
12 |
13 | // SystemdServiceName systemd
14 | SystemdServiceName = "edge-node"
15 |
16 | AccessLogSockName = "edge-node.accesslog"
17 | CacheGarbageSockName = "edge-node.cache.garbage"
18 |
19 | EnableKVCacheStore = true // determine store cache keys in KVStore or sqlite
20 | )
21 |
--------------------------------------------------------------------------------
/internal/const/vars.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package teaconst
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
7 | "os"
8 | "strings"
9 | )
10 |
11 | var (
12 | // 流量统计
13 |
14 | InTrafficBytes = uint64(0)
15 | OutTrafficBytes = uint64(0)
16 |
17 | NodeId int64 = 0
18 | NodeIdString = ""
19 | IsMain = checkMain()
20 |
21 | GlobalProductName = nodeconfigs.DefaultProductName
22 |
23 | IsQuiting = false // 是否正在退出
24 | EnableDBStat = false // 是否开启本地数据库统计
25 | )
26 |
27 | // 检查是否为主程序
28 | func checkMain() bool {
29 | if len(os.Args) == 1 ||
30 | (len(os.Args) >= 2 && os.Args[1] == "pprof") {
31 | return true
32 | }
33 | exe, _ := os.Executable()
34 | return strings.HasSuffix(exe, ".test") ||
35 | strings.HasSuffix(exe, ".test.exe") ||
36 | strings.Contains(exe, "___")
37 | }
38 |
--------------------------------------------------------------------------------
/internal/encrypt/magic_key.go:
--------------------------------------------------------------------------------
1 | package encrypt
2 |
3 | import (
4 | "github.com/iwind/TeaGo/logs"
5 | )
6 |
7 | const (
8 | MagicKey = "f1c8eafb543f03023e97b7be864a4e9b"
9 | )
10 |
11 | // 加密特殊信息
12 | func MagicKeyEncode(data []byte) []byte {
13 | method, err := NewMethodInstance("aes-256-cfb", MagicKey, MagicKey[:16])
14 | if err != nil {
15 | logs.Println("[MagicKeyEncode]" + err.Error())
16 | return data
17 | }
18 |
19 | dst, err := method.Encrypt(data)
20 | if err != nil {
21 | logs.Println("[MagicKeyEncode]" + err.Error())
22 | return data
23 | }
24 | return dst
25 | }
26 |
27 | // 解密特殊信息
28 | func MagicKeyDecode(data []byte) []byte {
29 | method, err := NewMethodInstance("aes-256-cfb", MagicKey, MagicKey[:16])
30 | if err != nil {
31 | logs.Println("[MagicKeyEncode]" + err.Error())
32 | return data
33 | }
34 |
35 | src, err := method.Decrypt(data)
36 | if err != nil {
37 | logs.Println("[MagicKeyEncode]" + err.Error())
38 | return data
39 | }
40 | return src
41 | }
42 |
--------------------------------------------------------------------------------
/internal/encrypt/magic_key_test.go:
--------------------------------------------------------------------------------
1 | package encrypt
2 |
3 | import "testing"
4 |
5 | func TestMagicKeyEncode(t *testing.T) {
6 | dst := MagicKeyEncode([]byte("Hello,World"))
7 | t.Log("dst:", string(dst))
8 |
9 | src := MagicKeyDecode(dst)
10 | t.Log("src:", string(src))
11 | }
12 |
--------------------------------------------------------------------------------
/internal/encrypt/method.go:
--------------------------------------------------------------------------------
1 | package encrypt
2 |
3 | type MethodInterface interface {
4 | // Init 初始化
5 | Init(key []byte, iv []byte) error
6 |
7 | // Encrypt 加密
8 | Encrypt(src []byte) (dst []byte, err error)
9 |
10 | // Decrypt 解密
11 | Decrypt(dst []byte) (src []byte, err error)
12 | }
13 |
--------------------------------------------------------------------------------
/internal/encrypt/method_aes_192_cfb_test.go:
--------------------------------------------------------------------------------
1 | package encrypt
2 |
3 | import (
4 | "runtime"
5 | "strings"
6 | "testing"
7 | )
8 |
9 | func TestAES192CFBMethod_Encrypt(t *testing.T) {
10 | method, err := NewMethodInstance("aes-192-cfb", "abc", "123")
11 | if err != nil {
12 | t.Fatal(err)
13 | }
14 | src := []byte("Hello, World")
15 | dst, err := method.Encrypt(src)
16 | if err != nil {
17 | t.Fatal(err)
18 | }
19 | dst = dst[:len(src)]
20 | t.Log("dst:", string(dst))
21 |
22 | src, err = method.Decrypt(dst)
23 | if err != nil {
24 | t.Fatal(err)
25 | }
26 | t.Log("src:", string(src))
27 | }
28 |
29 | func BenchmarkAES192CFBMethod_Encrypt(b *testing.B) {
30 | runtime.GOMAXPROCS(1)
31 |
32 | method, err := NewMethodInstance("aes-192-cfb", "abc", "123")
33 | if err != nil {
34 | b.Fatal(err)
35 | }
36 |
37 | src := []byte(strings.Repeat("Hello", 1024))
38 | for i := 0; i < b.N; i++ {
39 | dst, err := method.Encrypt(src)
40 | if err != nil {
41 | b.Fatal(err)
42 | }
43 | _ = dst
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/internal/encrypt/method_aes_256_cfb_test.go:
--------------------------------------------------------------------------------
1 | package encrypt
2 |
3 | import "testing"
4 |
5 | func TestAES256CFBMethod_Encrypt(t *testing.T) {
6 | method, err := NewMethodInstance("aes-256-cfb", "abc", "123")
7 | if err != nil {
8 | t.Fatal(err)
9 | }
10 | src := []byte("Hello, World")
11 | dst, err := method.Encrypt(src)
12 | if err != nil {
13 | t.Fatal(err)
14 | }
15 | dst = dst[:len(src)]
16 | t.Log("dst:", string(dst))
17 |
18 | src, err = method.Decrypt(dst)
19 | if err != nil {
20 | t.Fatal(err)
21 | }
22 | t.Log("src:", string(src))
23 | }
24 |
25 | func TestAES256CFBMethod_Encrypt2(t *testing.T) {
26 | method, err := NewMethodInstance("aes-256-cfb", "abc", "123")
27 | if err != nil {
28 | t.Fatal(err)
29 | }
30 | src := []byte("Hello, World")
31 | dst, err := method.Encrypt(src)
32 | if err != nil {
33 | t.Fatal(err)
34 | }
35 | t.Log("dst:", string(dst))
36 |
37 | src, err = method.Decrypt(dst)
38 | if err != nil {
39 | t.Fatal(err)
40 | }
41 | t.Log("src:", string(src))
42 | }
43 |
--------------------------------------------------------------------------------
/internal/encrypt/method_raw.go:
--------------------------------------------------------------------------------
1 | package encrypt
2 |
3 | type RawMethod struct {
4 | }
5 |
6 | func (this *RawMethod) Init(key, iv []byte) error {
7 | return nil
8 | }
9 |
10 | func (this *RawMethod) Encrypt(src []byte) (dst []byte, err error) {
11 | if len(src) == 0 {
12 | return
13 | }
14 | dst = make([]byte, len(src))
15 | copy(dst, src)
16 | return
17 | }
18 |
19 | func (this *RawMethod) Decrypt(dst []byte) (src []byte, err error) {
20 | if len(dst) == 0 {
21 | return
22 | }
23 | src = make([]byte, len(dst))
24 | copy(src, dst)
25 | return
26 | }
27 |
--------------------------------------------------------------------------------
/internal/encrypt/method_raw_test.go:
--------------------------------------------------------------------------------
1 | package encrypt
2 |
3 | import "testing"
4 |
5 | func TestRawMethod_Encrypt(t *testing.T) {
6 | method, err := NewMethodInstance("raw", "abc", "123")
7 | if err != nil {
8 | t.Fatal(err)
9 | }
10 | src := []byte("Hello, World")
11 | dst, err := method.Encrypt(src)
12 | if err != nil {
13 | t.Fatal(err)
14 | }
15 | dst = dst[:len(src)]
16 | t.Log("dst:", string(dst))
17 |
18 | src, err = method.Decrypt(dst)
19 | if err != nil {
20 | t.Fatal(err)
21 | }
22 | t.Log("src:", string(src))
23 | }
24 |
--------------------------------------------------------------------------------
/internal/encrypt/method_utils_test.go:
--------------------------------------------------------------------------------
1 | package encrypt
2 |
3 | import "testing"
4 |
5 | func TestFindMethodInstance(t *testing.T) {
6 | t.Log(NewMethodInstance("a", "b", ""))
7 | t.Log(NewMethodInstance("aes-256-cfb", "123456", ""))
8 | }
9 |
--------------------------------------------------------------------------------
/internal/errors/error_test.go:
--------------------------------------------------------------------------------
1 | package errors
2 |
3 | import (
4 | "errors"
5 | "testing"
6 | )
7 |
8 | func TestNew(t *testing.T) {
9 | t.Log(New("hello"))
10 | t.Log(Wrap(errors.New("hello")))
11 | t.Log(testError1())
12 | t.Log(Wrap(testError1()))
13 | t.Log(Wrap(testError2()))
14 | }
15 |
16 | func testError1() error {
17 | return New("test error1")
18 | }
19 |
20 | func testError2() error {
21 | return Wrap(testError1())
22 | }
23 |
--------------------------------------------------------------------------------
/internal/events/events.go:
--------------------------------------------------------------------------------
1 | package events
2 |
3 | type Event = string
4 |
5 | const (
6 | EventStart Event = "start" // start loading
7 | EventLoaded Event = "loaded" // first load
8 | EventQuit Event = "quit" // quit node gracefully
9 | EventReload Event = "reload" // reload config
10 | EventTerminated Event = "terminated" // process terminated
11 | EventNFTablesReady Event = "nftablesReady" // nftables ready
12 | EventReloadSomeServers Event = "reloadSomeServers" // reload some servers
13 | )
14 |
--------------------------------------------------------------------------------
/internal/events/utils_test.go:
--------------------------------------------------------------------------------
1 | package events_test
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/events"
5 | "testing"
6 | )
7 |
8 | func TestOn(t *testing.T) {
9 | type User struct {
10 | name string
11 | }
12 | var u = &User{name: "lily"}
13 | var u2 = &User{name: "lucy"}
14 |
15 | events.On("hello", func() {
16 | t.Log("world")
17 | })
18 | events.On("hello", func() {
19 | t.Log("world2")
20 | })
21 | events.OnKey("hello", u, func() {
22 | t.Log("world3")
23 | })
24 | events.OnKey("hello", u, func() {
25 | t.Log("world4")
26 | })
27 | events.Remove(u)
28 | events.Remove(u2)
29 | events.OnKey("hello2", nil, func() {
30 | t.Log("world2")
31 | })
32 | events.Notify("hello")
33 | }
34 |
--------------------------------------------------------------------------------
/internal/firewalls/ddos_protection_others.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build !linux
3 | // +build !linux
4 |
5 | package firewalls
6 |
7 | import (
8 | "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs"
9 | )
10 |
11 | var SharedDDoSProtectionManager = NewDDoSProtectionManager()
12 |
13 | type DDoSProtectionManager struct {
14 | }
15 |
16 | func NewDDoSProtectionManager() *DDoSProtectionManager {
17 | return &DDoSProtectionManager{}
18 | }
19 |
20 | func (this *DDoSProtectionManager) Apply(config *ddosconfigs.ProtectionConfig) error {
21 | return nil
22 | }
23 |
--------------------------------------------------------------------------------
/internal/firewalls/firewall_interface.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package firewalls
4 |
5 | // FirewallInterface 防火墙接口
6 | type FirewallInterface interface {
7 | // Name 名称
8 | Name() string
9 |
10 | // IsReady 是否已准备被调用
11 | IsReady() bool
12 |
13 | // IsMock 是否为模拟
14 | IsMock() bool
15 |
16 | // AllowPort 允许端口
17 | AllowPort(port int, protocol string) error
18 |
19 | // RemovePort 删除端口
20 | RemovePort(port int, protocol string) error
21 |
22 | // RejectSourceIP 拒绝某个源IP连接
23 | RejectSourceIP(ip string, timeoutSeconds int) error
24 |
25 | // DropSourceIP 丢弃某个源IP数据
26 | // ip 要封禁的IP
27 | // timeoutSeconds 过期时间
28 | // async 是否异步
29 | DropSourceIP(ip string, timeoutSeconds int, async bool) error
30 |
31 | // RemoveSourceIP 删除某个源IP
32 | RemoveSourceIP(ip string) error
33 | }
34 |
--------------------------------------------------------------------------------
/internal/firewalls/nftables/.gitignore:
--------------------------------------------------------------------------------
1 | build_remote.sh
--------------------------------------------------------------------------------
/internal/firewalls/nftables/chain_policy.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build linux
3 |
4 | package nftables
5 |
6 | import nft "github.com/google/nftables"
7 |
8 | type ChainPolicy = nft.ChainPolicy
9 |
10 | // Possible ChainPolicy values.
11 | const (
12 | ChainPolicyDrop = nft.ChainPolicyDrop
13 | ChainPolicyAccept = nft.ChainPolicyAccept
14 | )
15 |
--------------------------------------------------------------------------------
/internal/firewalls/nftables/element.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build linux
3 | // +build linux
4 |
5 | package nftables
6 |
7 | type Element struct {
8 | }
9 |
--------------------------------------------------------------------------------
/internal/firewalls/nftables/errors.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build linux
3 | // +build linux
4 |
5 | package nftables
6 |
7 | import (
8 | "errors"
9 | "strings"
10 | )
11 |
12 | var ErrTableNotFound = errors.New("table not found")
13 | var ErrChainNotFound = errors.New("chain not found")
14 | var ErrSetNotFound = errors.New("set not found")
15 | var ErrRuleNotFound = errors.New("rule not found")
16 |
17 | func IsNotFound(err error) bool {
18 | if err == nil {
19 | return false
20 | }
21 | return err == ErrTableNotFound || err == ErrChainNotFound || err == ErrSetNotFound || err == ErrRuleNotFound || strings.Contains(err.Error(), "no such file or directory")
22 | }
23 |
--------------------------------------------------------------------------------
/internal/firewalls/nftables/family.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build linux
3 |
4 | package nftables
5 |
6 | import (
7 | nft "github.com/google/nftables"
8 | )
9 |
10 | type TableFamily = nft.TableFamily
11 |
12 | const (
13 | TableFamilyINet TableFamily = nft.TableFamilyINet
14 | TableFamilyIPv4 TableFamily = nft.TableFamilyIPv4
15 | TableFamilyIPv6 TableFamily = nft.TableFamilyIPv6
16 | TableFamilyARP TableFamily = nft.TableFamilyARP
17 | TableFamilyNetdev TableFamily = nft.TableFamilyNetdev
18 | TableFamilyBridge TableFamily = nft.TableFamilyBridge
19 | )
20 |
--------------------------------------------------------------------------------
/internal/firewalls/nftables/rule.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build linux
3 |
4 | package nftables
5 |
6 | import (
7 | nft "github.com/google/nftables"
8 | "github.com/google/nftables/expr"
9 | )
10 |
11 | type Rule struct {
12 | rawRule *nft.Rule
13 | }
14 |
15 | func NewRule(rawRule *nft.Rule) *Rule {
16 | return &Rule{
17 | rawRule: rawRule,
18 | }
19 | }
20 |
21 | func (this *Rule) Raw() *nft.Rule {
22 | return this.rawRule
23 | }
24 |
25 | func (this *Rule) LookupSetName() string {
26 | for _, e := range this.rawRule.Exprs {
27 | exp, ok := e.(*expr.Lookup)
28 | if ok {
29 | return exp.SetName
30 | }
31 | }
32 | return ""
33 | }
34 |
35 | func (this *Rule) VerDict() expr.VerdictKind {
36 | for _, e := range this.rawRule.Exprs {
37 | exp, ok := e.(*expr.Verdict)
38 | if ok {
39 | return exp.Kind
40 | }
41 | }
42 |
43 | return -100
44 | }
45 |
46 | func (this *Rule) Handle() uint64 {
47 | return this.rawRule.Handle
48 | }
49 |
50 | func (this *Rule) UserData() []byte {
51 | return this.rawRule.UserData
52 | }
53 |
--------------------------------------------------------------------------------
/internal/firewalls/nftables/set_batch.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build linux
3 |
4 | package nftables
5 |
6 | import (
7 | nft "github.com/google/nftables"
8 | )
9 |
10 | type SetBatch struct {
11 | conn *Conn
12 | rawSet *nft.Set
13 | }
14 |
15 | func (this *SetBatch) AddElement(key []byte, options *ElementOptions) error {
16 | var rawElement = nft.SetElement{
17 | Key: key,
18 | }
19 | if options != nil {
20 | rawElement.Timeout = options.Timeout
21 | }
22 | return this.conn.Raw().SetAddElements(this.rawSet, []nft.SetElement{
23 | rawElement,
24 | })
25 | }
26 |
27 | func (this *SetBatch) DeleteElement(key []byte) error {
28 | return this.conn.Raw().SetDeleteElements(this.rawSet, []nft.SetElement{
29 | {
30 | Key: key,
31 | },
32 | })
33 | }
34 |
35 | func (this *SetBatch) Commit() error {
36 | return this.conn.Commit()
37 | }
38 |
--------------------------------------------------------------------------------
/internal/firewalls/nftables/set_ext.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 | //go:build linux && !plus
3 |
4 | package nftables
5 |
6 | func (this *Set) initElements() {
7 | // NOT IMPLEMENTED
8 | }
9 |
--------------------------------------------------------------------------------
/internal/firewalls/utils.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package firewalls
4 |
5 | import (
6 | "time"
7 | )
8 |
9 | // DropTemporaryTo 使用本地防火墙临时拦截IP数据包
10 | func DropTemporaryTo(ip string, expiresAt int64) {
11 | // 如果为0,则表示是长期有效
12 | if expiresAt <= 0 {
13 | expiresAt = time.Now().Unix() + 3600
14 | }
15 |
16 | var timeout = expiresAt - time.Now().Unix()
17 | if timeout < 1 {
18 | return
19 | }
20 | if timeout > 3600 {
21 | timeout = 3600
22 | }
23 |
24 | // 使用本地防火墙延长封禁
25 | var fw = Firewall()
26 | if fw != nil && !fw.IsMock() {
27 | // 这里 int(int64) 转换的前提是限制了 timeout <= 3600,否则将有整型溢出的风险
28 | _ = fw.DropSourceIP(ip, int(timeout), true)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/internal/goman/instance.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package goman
4 |
5 | import "time"
6 |
7 | type Instance struct {
8 | Id uint64
9 | CreatedTime time.Time
10 | File string
11 | Line int
12 | }
13 |
--------------------------------------------------------------------------------
/internal/goman/lib_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package goman_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/goman"
7 | "testing"
8 | "time"
9 | )
10 |
11 | func TestNew(t *testing.T) {
12 | goman.New(func() {
13 | t.Log("Hello")
14 |
15 | t.Log(goman.List())
16 | })
17 |
18 | time.Sleep(1 * time.Second)
19 | t.Log(goman.List())
20 |
21 | time.Sleep(1 * time.Second)
22 | }
23 |
24 | func TestNewWithArgs(t *testing.T) {
25 | goman.NewWithArgs(func(args ...interface{}) {
26 | t.Log(args[0], args[1])
27 | }, 1, 2)
28 | time.Sleep(1 * time.Second)
29 | }
30 |
--------------------------------------------------------------------------------
/internal/goman/task_group.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package goman
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/zero"
7 | "runtime"
8 | "sync"
9 | )
10 |
11 | type TaskGroup struct {
12 | semi chan zero.Zero
13 | wg *sync.WaitGroup
14 | locker *sync.RWMutex
15 | }
16 |
17 | func NewTaskGroup() *TaskGroup {
18 | var concurrent = runtime.NumCPU()
19 | if concurrent <= 1 {
20 | concurrent = 2
21 | }
22 | return &TaskGroup{
23 | semi: make(chan zero.Zero, concurrent),
24 | wg: &sync.WaitGroup{},
25 | locker: &sync.RWMutex{},
26 | }
27 | }
28 |
29 | func (this *TaskGroup) Run(f func()) {
30 | this.wg.Add(1)
31 | go func() {
32 | defer this.wg.Done()
33 |
34 | this.semi <- zero.Zero{}
35 |
36 | f()
37 |
38 | <-this.semi
39 | }()
40 | }
41 |
42 | func (this *TaskGroup) Wait() {
43 | this.wg.Wait()
44 | }
45 |
46 | func (this *TaskGroup) Lock() {
47 | this.locker.Lock()
48 | }
49 |
50 | func (this *TaskGroup) Unlock() {
51 | this.locker.Unlock()
52 | }
53 |
--------------------------------------------------------------------------------
/internal/goman/task_group_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package goman_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/goman"
7 | "runtime"
8 | "testing"
9 | )
10 |
11 | func TestNewTaskGroup(t *testing.T) {
12 | var group = goman.NewTaskGroup()
13 | var m = map[int]bool{}
14 |
15 | for i := 0; i < runtime.NumCPU()*2; i++ {
16 | var index = i
17 | group.Run(func() {
18 | t.Log("task", index)
19 |
20 | group.Lock()
21 | _, ok := m[index]
22 | if ok {
23 | t.Error("duplicated:", index)
24 | }
25 | m[index] = true
26 | group.Unlock()
27 | })
28 | }
29 | group.Wait()
30 | }
31 |
--------------------------------------------------------------------------------
/internal/iplibrary/README.md:
--------------------------------------------------------------------------------
1 | # IPList
2 | List Check Order:
3 | ~~~
4 | Global List --> Node List--> Server List --> WAF List --> Bind List
5 | ~~~
6 |
--------------------------------------------------------------------------------
/internal/iplibrary/action_base.go:
--------------------------------------------------------------------------------
1 | package iplibrary
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/iwind/TeaGo/maps"
6 | "net/http"
7 | )
8 |
9 | type BaseAction struct {
10 | }
11 |
12 | func (this *BaseAction) Close() error {
13 | return nil
14 | }
15 |
16 | // DoHTTP 处理HTTP请求
17 | func (this *BaseAction) DoHTTP(req *http.Request, resp http.ResponseWriter) (goNext bool, err error) {
18 | return true, nil
19 | }
20 |
21 | func (this *BaseAction) convertParams(params maps.Map, ptr interface{}) error {
22 | data, err := json.Marshal(params)
23 | if err != nil {
24 | return err
25 | }
26 | err = json.Unmarshal(data, ptr)
27 | if err != nil {
28 | return err
29 | }
30 | return nil
31 | }
32 |
--------------------------------------------------------------------------------
/internal/iplibrary/action_errors.go:
--------------------------------------------------------------------------------
1 | package iplibrary
2 |
3 | // FataError 是否是致命错误
4 | type FataError struct {
5 | err string
6 | }
7 |
8 | func (this *FataError) Error() string {
9 | return this.err
10 | }
11 |
12 | func NewFataError(err string) error {
13 | return &FataError{err: err}
14 | }
15 |
16 | func IsFatalError(err error) bool {
17 | if err == nil {
18 | return false
19 | }
20 | _, ok := err.(*FataError)
21 | return ok
22 | }
23 |
--------------------------------------------------------------------------------
/internal/iplibrary/action_interface.go:
--------------------------------------------------------------------------------
1 | package iplibrary
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
5 | "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
6 | "net/http"
7 | )
8 |
9 | type ActionInterface interface {
10 | // Init 初始化
11 | Init(config *firewallconfigs.FirewallActionConfig) error
12 |
13 | // AddItem 添加
14 | AddItem(listType IPListType, item *pb.IPItem) error
15 |
16 | // DeleteItem 删除
17 | DeleteItem(listType IPListType, item *pb.IPItem) error
18 |
19 | // Close 关闭
20 | Close() error
21 |
22 | // DoHTTP 处理HTTP请求
23 | DoHTTP(req *http.Request, resp http.ResponseWriter) (goNext bool, err error)
24 | }
25 |
--------------------------------------------------------------------------------
/internal/iplibrary/action_utils_test.go:
--------------------------------------------------------------------------------
1 | package iplibrary
2 |
3 | import "testing"
4 |
5 | func TestIPv4RangeToCIDRRange(t *testing.T) {
6 | t.Log(iPv4RangeToCIDRRange("192.168.0.0", "192.168.255.255"))
7 | }
8 |
--------------------------------------------------------------------------------
/internal/iplibrary/init.go:
--------------------------------------------------------------------------------
1 | package iplibrary
2 |
3 | func init() {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/internal/iplibrary/ip_list_db.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package iplibrary
4 |
5 | import "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
6 |
7 | type IPListDB interface {
8 | Name() string
9 | DeleteExpiredItems() error
10 | ReadMaxVersion() (int64, error)
11 | UpdateMaxVersion(version int64) error
12 | ReadItems(offset int64, size int64) (items []*pb.IPItem, goNext bool, err error)
13 | AddItem(item *pb.IPItem) error
14 | }
15 |
--------------------------------------------------------------------------------
/internal/iplibrary/list_type.go:
--------------------------------------------------------------------------------
1 | package iplibrary
2 |
3 | type IPListType = string
4 |
5 | const (
6 | IPListTypeWhite IPListType = "white"
7 | IPListTypeBlack IPListType = "black"
8 | )
9 |
--------------------------------------------------------------------------------
/internal/iplibrary/list_utils_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package iplibrary
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
7 | "testing"
8 | "time"
9 | )
10 |
11 | func TestIPIsAllowed(t *testing.T) {
12 | if !testutils.IsSingleTesting() {
13 | return
14 | }
15 |
16 | var manager = NewIPListManager()
17 | manager.Init()
18 |
19 | var before = time.Now()
20 | defer func() {
21 | t.Log(time.Since(before).Seconds()*1000, "ms")
22 | }()
23 | t.Log(AllowIP("127.0.0.1", 0))
24 | t.Log(AllowIP("127.0.0.1", 23))
25 | }
26 |
--------------------------------------------------------------------------------
/internal/iplibrary/result.go:
--------------------------------------------------------------------------------
1 | package iplibrary
2 |
3 | type Result struct {
4 | CityId int64
5 | Country string
6 | Region string
7 | Province string
8 | City string
9 | ISP string
10 | }
11 |
--------------------------------------------------------------------------------
/internal/js/.gitignore:
--------------------------------------------------------------------------------
1 | *
--------------------------------------------------------------------------------
/internal/metrics/metric_interface.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package metrics
4 |
5 | type MetricInterface interface {
6 | // MetricKey 指标对象
7 | MetricKey(key string) string
8 |
9 | // MetricValue 指标值
10 | MetricValue(value string) (result int64, ok bool)
11 |
12 | // MetricServerId 服务ID
13 | MetricServerId() int64
14 |
15 | // MetricCategory 指标分类
16 | MetricCategory() string
17 | }
18 |
--------------------------------------------------------------------------------
/internal/metrics/sum_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package metrics_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/metrics"
7 | timeutil "github.com/iwind/TeaGo/utils/time"
8 | "runtime"
9 | "testing"
10 | )
11 |
12 | func BenchmarkSumStat(b *testing.B) {
13 | runtime.GOMAXPROCS(2)
14 |
15 | b.RunParallel(func(pb *testing.PB) {
16 | for pb.Next() {
17 | metrics.UniqueKey(1, []string{"1.2.3.4"}, timeutil.Format("Ymd"), 1, 1)
18 | }
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/internal/metrics/task.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package metrics
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
7 | "time"
8 | )
9 |
10 | type Task interface {
11 | Init() error
12 | Item() *serverconfigs.MetricItemConfig
13 | SetItem(item *serverconfigs.MetricItemConfig)
14 | Add(obj MetricInterface)
15 | InsertStat(stat *Stat) error
16 | Upload(pauseDuration time.Duration) error
17 | Start() error
18 | Stop() error
19 | Delete() error
20 | CleanExpired() error
21 | }
22 |
--------------------------------------------------------------------------------
/internal/metrics/task_kv_objects.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package metrics
4 |
5 | import (
6 | "encoding/json"
7 | "errors"
8 | )
9 |
10 | type ItemEncoder[T interface{ *Stat }] struct {
11 | }
12 |
13 | func (this *ItemEncoder[T]) Encode(value T) ([]byte, error) {
14 | return json.Marshal(value)
15 | }
16 |
17 | func (this *ItemEncoder[T]) EncodeField(value T, fieldName string) ([]byte, error) {
18 | return nil, errors.New("invalid field name '" + fieldName + "'")
19 | }
20 |
21 | func (this *ItemEncoder[T]) Decode(valueBytes []byte) (value T, err error) {
22 | err = json.Unmarshal(valueBytes, &value)
23 | return
24 | }
25 |
--------------------------------------------------------------------------------
/internal/monitor/value.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package monitor
4 |
5 | // ItemValue 数据值定义
6 | type ItemValue struct {
7 | Item string
8 | ValueJSON []byte
9 | CreatedAt int64
10 | }
11 |
--------------------------------------------------------------------------------
/internal/monitor/value_queue_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package monitor
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
7 | "github.com/TeaOSLab/EdgeNode/internal/rpc"
8 | "github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
9 | _ "github.com/iwind/TeaGo/bootstrap"
10 | "github.com/iwind/TeaGo/logs"
11 | "google.golang.org/grpc/status"
12 | "testing"
13 | )
14 |
15 | func TestValueQueue_RPC(t *testing.T) {
16 | if !testutils.IsSingleTesting() {
17 | return
18 | }
19 |
20 | rpcClient, err := rpc.SharedRPC()
21 | if err != nil {
22 | t.Fatal(err)
23 | }
24 | _, err = rpcClient.NodeValueRPC.CreateNodeValue(rpcClient.Context(), &pb.CreateNodeValueRequest{})
25 | if err != nil {
26 | statusErr, ok := status.FromError(err)
27 | if ok {
28 | logs.Println(statusErr.Code())
29 | }
30 | t.Fatal(err)
31 | }
32 | t.Log("ok")
33 | }
34 |
--------------------------------------------------------------------------------
/internal/nodes/api_stream_test.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
5 | "testing"
6 | )
7 |
8 | func TestAPIStream_Start(t *testing.T) {
9 | if !testutils.IsSingleTesting() {
10 | return
11 | }
12 |
13 | apiStream := NewAPIStream()
14 | apiStream.Start()
15 | }
16 |
--------------------------------------------------------------------------------
/internal/nodes/client_conn_interface.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package nodes
4 |
5 | type ClientConnInterface interface {
6 | // IsClosed 是否已关闭
7 | IsClosed() bool
8 |
9 | // IsBound 是否已绑定服务
10 | IsBound() bool
11 |
12 | // Bind 绑定服务
13 | Bind(serverId int64, remoteAddr string, maxConnsPerServer int, maxConnsPerIP int) bool
14 |
15 | // ServerId 获取服务ID
16 | ServerId() int64
17 |
18 | // SetServerId 设置服务ID
19 | SetServerId(serverId int64) (goNext bool)
20 |
21 | // SetUserId 设置所属网站的用户ID
22 | SetUserId(userId int64)
23 |
24 | // SetUserPlanId 设置
25 | SetUserPlanId(userPlanId int64)
26 |
27 | // UserId 获取当前连接所属服务的用户ID
28 | UserId() int64
29 |
30 | // SetIsPersistent 设置是否为持久化
31 | SetIsPersistent(isPersistent bool)
32 |
33 | // SetFingerprint 设置指纹信息
34 | SetFingerprint(fingerprint []byte)
35 |
36 | // Fingerprint 读取指纹信息
37 | Fingerprint() []byte
38 |
39 | // LastRequestBytes 读取上一次请求发送的字节数
40 | LastRequestBytes() int64
41 | }
42 |
--------------------------------------------------------------------------------
/internal/nodes/client_conn_limiter_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package nodes
4 |
5 | import (
6 | "github.com/iwind/TeaGo/logs"
7 | "testing"
8 | )
9 |
10 | func TestClientConnLimiter_Add(t *testing.T) {
11 | var limiter = NewClientConnLimiter()
12 | {
13 | b := limiter.Add("127.0.0.1:1234", 1, "192.168.1.100", 10, 5)
14 | t.Log(b)
15 | }
16 | {
17 | b := limiter.Add("127.0.0.1:1235", 1, "192.168.1.100", 10, 5)
18 | t.Log(b)
19 | }
20 | {
21 | b := limiter.Add("127.0.0.1:1236", 1, "192.168.1.100", 10, 5)
22 | t.Log(b)
23 | }
24 | {
25 | b := limiter.Add("127.0.0.1:1237", 1, "192.168.1.101", 10, 5)
26 | t.Log(b)
27 | }
28 | {
29 | b := limiter.Add("127.0.0.1:1238", 1, "192.168.1.100", 5, 5)
30 | t.Log(b)
31 | }
32 | limiter.Remove("127.0.0.1:1238")
33 | limiter.Remove("127.0.0.1:1239")
34 | limiter.Remove("127.0.0.1:1237")
35 | logs.PrintAsJSON(limiter.remoteAddrMap, t)
36 | logs.PrintAsJSON(limiter.ipConns, t)
37 | logs.PrintAsJSON(limiter.serverConns, t)
38 | }
39 |
--------------------------------------------------------------------------------
/internal/nodes/client_conn_utils.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package nodes
4 |
5 | import (
6 | "net"
7 | )
8 |
9 | // 判断客户端连接是否已关闭
10 | func isClientConnClosed(conn net.Conn) bool {
11 | if conn == nil {
12 | return true
13 | }
14 | clientConn, ok := conn.(ClientConnInterface)
15 | if ok {
16 | return clientConn.IsClosed()
17 | }
18 |
19 | return true
20 | }
21 |
--------------------------------------------------------------------------------
/internal/nodes/conn_linger.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package nodes
4 |
5 | type LingerConn interface {
6 | SetLinger(sec int) error
7 | }
8 |
--------------------------------------------------------------------------------
/internal/nodes/http_cache_task_manager_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package nodes_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
7 | "github.com/TeaOSLab/EdgeNode/internal/caches"
8 | "github.com/TeaOSLab/EdgeNode/internal/nodes"
9 | "testing"
10 | )
11 |
12 | func TestHTTPCacheTaskManager_Loop(t *testing.T) {
13 | // initialize cache policies
14 | config, err := nodeconfigs.SharedNodeConfig()
15 | if err != nil {
16 | t.Fatal(err)
17 | }
18 | caches.SharedManager.UpdatePolicies(config.HTTPCachePolicies)
19 |
20 | var manager = nodes.NewHTTPCacheTaskManager()
21 | err = manager.Loop()
22 | if err != nil {
23 | t.Fatal(err)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/internal/nodes/http_client_transport.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package nodes
4 |
5 | import (
6 | "net/http"
7 | )
8 |
9 | const emptyHTTPLocation = "/$EmptyHTTPLocation$"
10 |
11 | type HTTPClientTransport struct {
12 | *http.Transport
13 | }
14 |
15 | func (this *HTTPClientTransport) RoundTrip(req *http.Request) (*http.Response, error) {
16 | resp, err := this.Transport.RoundTrip(req)
17 | if err != nil {
18 | return resp, err
19 | }
20 |
21 | // 检查在跳转相关状态中Location是否存在
22 | if httpStatusIsRedirect(resp.StatusCode) && len(resp.Header.Get("Location")) == 0 {
23 | resp.Header.Set("Location", emptyHTTPLocation)
24 | }
25 | return resp, nil
26 | }
27 |
--------------------------------------------------------------------------------
/internal/nodes/http_request_cc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 | //go:build !plus
3 |
4 | package nodes
5 |
6 | func (this *HTTPRequest) doCC() (block bool) {
7 | return
8 | }
9 |
--------------------------------------------------------------------------------
/internal/nodes/http_request_events.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build !script
3 | // +build !script
4 |
5 | package nodes
6 |
7 | func (this *HTTPRequest) onInit() {
8 | }
9 |
10 | func (this *HTTPRequest) onRequest() {
11 | }
12 |
--------------------------------------------------------------------------------
/internal/nodes/http_request_health_check.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package nodes
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeCommon/pkg/nodeutils"
7 | "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
8 | "github.com/TeaOSLab/EdgeNode/internal/remotelogs"
9 | )
10 |
11 | // 健康检查
12 | func (this *HTTPRequest) doHealthCheck(key string, isHealthCheck *bool) (stop bool) {
13 | this.tags = append(this.tags, "healthCheck")
14 |
15 | this.RawReq.Header.Del(serverconfigs.HealthCheckHeaderName)
16 |
17 | data, err := nodeutils.Base64DecodeMap(key)
18 | if err != nil {
19 | remotelogs.Error("HTTP_REQUEST_HEALTH_CHECK", "decode key failed: "+err.Error())
20 | return
21 | }
22 | *isHealthCheck = true
23 |
24 | this.web.StatRef = nil
25 |
26 | if !data.GetBool("accessLogIsOn") {
27 | this.disableLog = true
28 | }
29 |
30 | if data.GetBool("onlyBasicRequest") {
31 | return true
32 | }
33 |
34 | return
35 | }
36 |
--------------------------------------------------------------------------------
/internal/nodes/http_request_hls.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 | //go:build !plus
3 |
4 | package nodes
5 |
6 | import "net/http"
7 |
8 | func (this *HTTPRequest) processHLSBefore() (blocked bool) {
9 | // stub
10 | return false
11 | }
12 |
13 | func (this *HTTPRequest) processM3u8Response(resp *http.Response) error {
14 | // stub
15 | return nil
16 | }
17 |
--------------------------------------------------------------------------------
/internal/nodes/http_request_http3.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 | //go:build !plus
3 |
4 | package nodes
5 |
6 | import "net/http"
7 |
8 | func (this *HTTPRequest) processHTTP3Headers(respHeader http.Header) {
9 | // stub
10 | }
11 |
--------------------------------------------------------------------------------
/internal/nodes/http_request_ln.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build !plus
3 | // +build !plus
4 |
5 | package nodes
6 |
7 | import (
8 | "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
9 | )
10 |
11 | const (
12 | LNExpiresHeader = "X-Edge-Ln-Expires"
13 | )
14 |
15 | func existsLnNodeIP(nodeIP string) bool {
16 | return false
17 | }
18 |
19 | func (this *HTTPRequest) checkLnRequest() bool {
20 | return false
21 | }
22 |
23 | func (this *HTTPRequest) getLnOrigin(excludingNodeIds []int64, urlHash uint64) (originConfig *serverconfigs.OriginConfig, lnNodeId int64, hasMultipleNodes bool) {
24 | return nil, 0, false
25 | }
26 |
--------------------------------------------------------------------------------
/internal/nodes/http_request_oss.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 | //go:build !plus
3 |
4 | package nodes
5 |
6 | import (
7 | "errors"
8 | "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
9 | "net/http"
10 | )
11 |
12 | func (this *HTTPRequest) doOSSOrigin(origin *serverconfigs.OriginConfig) (resp *http.Response, goNext bool, errorCode string, ossBucketName string, err error) {
13 | // stub
14 | return nil, false, "", "", errors.New("not implemented")
15 | }
16 |
--------------------------------------------------------------------------------
/internal/nodes/http_request_plan_before.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build !plus
3 |
4 | package nodes
5 |
6 | // 检查套餐
7 | func (this *HTTPRequest) doPlanBefore() (blocked bool) {
8 | // stub
9 | return false
10 | }
11 |
--------------------------------------------------------------------------------
/internal/nodes/http_request_stat.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/stats"
5 | )
6 |
7 | // 统计
8 | func (this *HTTPRequest) doStat() {
9 | if this.ReqServer == nil || this.web == nil || this.web.StatRef == nil {
10 | return
11 | }
12 |
13 | // 内置的统计
14 | stats.SharedHTTPRequestStatManager.AddRemoteAddr(this.ReqServer.Id, this.requestRemoteAddr(true), this.writer.SentBodyBytes(), this.isAttack)
15 | stats.SharedHTTPRequestStatManager.AddUserAgent(this.ReqServer.Id, this.requestHeader("User-Agent"), this.remoteAddr)
16 | }
17 |
--------------------------------------------------------------------------------
/internal/nodes/http_request_sub.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package nodes
4 |
5 | import "net/http"
6 |
7 | // 执行子请求
8 | func (this *HTTPRequest) doSubRequest(writer http.ResponseWriter, rawReq *http.Request) {
9 | // 包装新请求对象
10 | req := &HTTPRequest{
11 | RawReq: rawReq,
12 | RawWriter: writer,
13 | ReqServer: this.ReqServer,
14 | ReqHost: this.ReqHost,
15 | ServerName: this.ServerName,
16 | ServerAddr: this.ServerAddr,
17 | IsHTTP: this.IsHTTP,
18 | IsHTTPS: this.IsHTTPS,
19 | }
20 | req.isSubRequest = true
21 | req.Do()
22 | }
23 |
--------------------------------------------------------------------------------
/internal/nodes/http_request_uam.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build !plus
3 | // +build !plus
4 |
5 | package nodes
6 |
7 | func (this *HTTPRequest) isUAMRequest() bool {
8 | // stub
9 | return false
10 | }
11 |
12 | // UAM
13 | func (this *HTTPRequest) doUAM() (block bool) {
14 | // stub
15 | return false
16 | }
17 |
--------------------------------------------------------------------------------
/internal/nodes/http_request_user_agent.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package nodes
4 |
5 | import (
6 | "net/http"
7 | )
8 |
9 | func (this *HTTPRequest) doCheckUserAgent() (shouldStop bool) {
10 | if this.web.UserAgent == nil || !this.web.UserAgent.IsOn {
11 | return
12 | }
13 |
14 | const cacheSeconds = "3600" // 时间不能过长,防止修改设置后长期无法生效
15 |
16 | if this.web.UserAgent.MatchURL(this.URL()) && !this.web.UserAgent.AllowRequest(this.RawReq) {
17 | this.tags = append(this.tags, "userAgentCheck")
18 | this.writer.Header().Set("Cache-Control", "max-age="+cacheSeconds)
19 | this.writeCode(http.StatusForbidden, "The User-Agent has been blocked.", "当前访问已被UA名单拦截。")
20 | return true
21 | }
22 |
23 | return
24 | }
25 |
--------------------------------------------------------------------------------
/internal/nodes/http_writer_ext.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build !plus
3 | // +build !plus
4 |
5 | package nodes
6 |
7 | import (
8 | "os"
9 | )
10 |
11 | func (this *HTTPWriter) canSendfile() (*os.File, bool) {
12 | return nil, false
13 | }
14 |
15 | func (this *HTTPWriter) checkPlanBandwidth(n int) {
16 | // stub
17 | }
18 |
--------------------------------------------------------------------------------
/internal/nodes/listener_base_ext.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 | //go:build !plus
3 |
4 | package nodes
5 |
6 | import "crypto/tls"
7 |
8 | func (this *BaseListener) calculateFingerprint(clientInfo *tls.ClientHelloInfo) []byte {
9 | return nil
10 | }
11 |
--------------------------------------------------------------------------------
/internal/nodes/listener_base_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package nodes
4 |
5 | import (
6 | "context"
7 | "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
8 | "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
9 | "github.com/iwind/TeaGo/types"
10 | "testing"
11 | "time"
12 | )
13 |
14 | func TestBaseListener_FindServer(t *testing.T) {
15 | sharedNodeConfig = &nodeconfigs.NodeConfig{}
16 |
17 | var listener = &BaseListener{}
18 | listener.Group = serverconfigs.NewServerAddressGroup("https://*:443")
19 | for i := 0; i < 1_000_000; i++ {
20 | var server = &serverconfigs.ServerConfig{
21 | IsOn: true,
22 | Name: types.String(i) + ".hello.com",
23 | ServerNames: []*serverconfigs.ServerNameConfig{
24 | {Name: types.String(i) + ".hello.com"},
25 | },
26 | }
27 | _ = server.Init(context.Background())
28 | listener.Group.Add(server)
29 | }
30 |
31 | var before = time.Now()
32 | defer func() {
33 | t.Log(time.Since(before).Seconds()*1000, "ms")
34 | }()
35 |
36 | t.Log(listener.findNamedServerMatched("855555.hello.com"))
37 | }
38 |
--------------------------------------------------------------------------------
/internal/nodes/listener_interface.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
4 |
5 | // ListenerInterface 各协议监听器的接口
6 | type ListenerInterface interface {
7 | // Init 初始化
8 | Init()
9 |
10 | // Serve 监听
11 | Serve() error
12 |
13 | // Close 关闭
14 | Close() error
15 |
16 | // Reload 重载配置
17 | Reload(serverGroup *serverconfigs.ServerAddressGroup)
18 |
19 | // CountActiveConnections 获取当前活跃的连接数
20 | CountActiveConnections() int
21 | }
22 |
--------------------------------------------------------------------------------
/internal/nodes/listener_test.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
5 | "testing"
6 | )
7 |
8 | func TestListener_Listen(t *testing.T) {
9 | listener := NewListener()
10 |
11 | group := serverconfigs.NewServerAddressGroup("https://:1234")
12 |
13 | listener.Reload(group)
14 | err := listener.Listen()
15 | if err != nil {
16 | t.Fatal(err)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/internal/nodes/node_ext.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build !script
3 | // +build !script
4 |
5 | package nodes
6 |
7 | func (this *Node) reloadCommonScripts() error {
8 | return nil
9 | }
10 |
11 | func (this *Node) reloadIPLibrary() {
12 |
13 | }
14 |
15 | func (this *Node) notifyPlusChange() error {
16 | return nil
17 | }
18 |
19 | func (this *Node) execTOAChangedTask() error {
20 | return nil
21 | }
22 |
--------------------------------------------------------------------------------
/internal/nodes/node_panic_arm64.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 | //go:build arm64
3 |
4 | package nodes
5 |
6 | // 处理异常
7 | func (this *Node) handlePanic() {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/internal/nodes/node_status_executor_test.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/shirou/gopsutil/v3/cpu"
5 | "testing"
6 | "time"
7 | )
8 |
9 | func TestNodeStatusExecutor_CPU(t *testing.T) {
10 | countLogicCPU, err := cpu.Counts(true)
11 | if err != nil {
12 | t.Fatal(err)
13 | }
14 | t.Log("logic count:", countLogicCPU)
15 |
16 | countPhysicalCPU, err := cpu.Counts(false)
17 | if err != nil {
18 | t.Fatal(err)
19 | }
20 | t.Log("physical count:", countPhysicalCPU)
21 |
22 | percents, err := cpu.Percent(100*time.Millisecond, false)
23 | if err != nil {
24 | t.Fatal(err)
25 | }
26 | t.Log(percents)
27 | }
28 |
--------------------------------------------------------------------------------
/internal/nodes/node_tasks_ext.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 | //go:build !plus
3 |
4 | package nodes
5 |
6 | import "github.com/TeaOSLab/EdgeNode/internal/rpc"
7 |
8 | func (this *Node) execScriptsChangedTask() error {
9 | // stub
10 | return nil
11 | }
12 |
13 | func (this *Node) execUAMPolicyChangedTask(rpcClient *rpc.RPCClient) error {
14 | // stub
15 | return nil
16 | }
17 |
18 | func (this *Node) execHTTPCCPolicyChangedTask(rpcClient *rpc.RPCClient) error {
19 | // stub
20 | return nil
21 | }
22 |
23 | func (this *Node) execHTTP3PolicyChangedTask(rpcClient *rpc.RPCClient) error {
24 | // stub
25 | return nil
26 | }
27 |
28 | func (this *Node) execHTTPPagesPolicyChangedTask(rpcClient *rpc.RPCClient) error {
29 | // stub
30 | return nil
31 | }
32 |
33 | func (this *Node) execNetworkSecurityPolicyChangedTask(rpcClient *rpc.RPCClient) error {
34 | // stub
35 | return nil
36 | }
37 |
38 | func (this *Node) execPlanChangedTask(rpcClient *rpc.RPCClient) error {
39 | return nil
40 | }
41 |
--------------------------------------------------------------------------------
/internal/nodes/node_test.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
5 | _ "github.com/iwind/TeaGo/bootstrap"
6 | "testing"
7 | )
8 |
9 | func TestNode_Start(t *testing.T) {
10 | if !testutils.IsSingleTesting() {
11 | return
12 | }
13 |
14 | var node = NewNode()
15 | node.Start()
16 | }
17 |
18 | func TestNode_Test(t *testing.T) {
19 | if !testutils.IsSingleTesting() {
20 | return
21 | }
22 |
23 | var node = NewNode()
24 | err := node.Test()
25 | if err != nil {
26 | t.Fatal(err)
27 | }
28 | t.Log("ok")
29 | }
30 |
--------------------------------------------------------------------------------
/internal/nodes/origin_state.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package nodes
4 |
5 | import "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
6 |
7 | type OriginState struct {
8 | CountFails int64
9 | UpdatedAt int64
10 | Config *serverconfigs.OriginConfig
11 | Addr string
12 | TLSHost string
13 | ReverseProxy *serverconfigs.ReverseProxyConfig
14 | }
15 |
--------------------------------------------------------------------------------
/internal/nodes/origin_state_manager_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package nodes
4 |
5 | import "testing"
6 |
7 | func TestOriginManager_Loop(t *testing.T) {
8 | var manager = NewOriginStateManager()
9 | err := manager.Loop()
10 | if err != nil {
11 | t.Fatal(err)
12 | }
13 |
14 | t.Log(manager.stateMap)
15 | }
16 |
--------------------------------------------------------------------------------
/internal/nodes/task_ocsp_update_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package nodes_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/nodes"
7 | "testing"
8 | )
9 |
10 | func TestOCSPUpdateTask_Loop(t *testing.T) {
11 | var task = &nodes.OCSPUpdateTask{}
12 | err := task.Loop()
13 | if err != nil {
14 | t.Fatal(err)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/internal/nodes/toa_manager.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 | //go:build !plus
3 |
4 | package nodes
5 |
6 | import "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
7 |
8 | var sharedTOAManager = NewTOAManager()
9 |
10 | type TOAManager struct {
11 | }
12 |
13 | func NewTOAManager() *TOAManager {
14 | return &TOAManager{}
15 | }
16 |
17 | func (this *TOAManager) Apply(config *nodeconfigs.TOAConfig) error {
18 | return nil
19 | }
20 |
21 | func (this *TOAManager) Config() *nodeconfigs.TOAConfig {
22 | return nil
23 | }
24 |
25 | func (this *TOAManager) Quit() error {
26 | return nil
27 | }
28 |
29 | func (this *TOAManager) SendMsg(msg string) error {
30 | return nil
31 | }
32 |
--------------------------------------------------------------------------------
/internal/nodes/upgrade_manager_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package nodes
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
7 | _ "github.com/iwind/TeaGo/bootstrap"
8 | "testing"
9 | )
10 |
11 | func TestUpgradeManager_install(t *testing.T) {
12 | if !testutils.IsSingleTesting() {
13 | return
14 | }
15 |
16 | err := NewUpgradeManager().install()
17 | if err != nil {
18 | t.Fatal(err)
19 | }
20 | t.Log("ok")
21 | }
22 |
--------------------------------------------------------------------------------
/internal/rpc/call_stat_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package rpc_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/rpc"
7 | "testing"
8 | )
9 |
10 | func TestNewCallStat(t *testing.T) {
11 | var stat = rpc.NewCallStat(10)
12 | stat.Add(true, 1)
13 | stat.Add(true, 2)
14 | stat.Add(true, 3)
15 | stat.Add(false, 4)
16 | stat.Add(true, 0)
17 | stat.Add(true, 1)
18 | t.Log(stat.Sum())
19 | }
20 |
--------------------------------------------------------------------------------
/internal/stats/user_agent_parser_result.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package stats
4 |
5 | import (
6 | "github.com/mssola/useragent"
7 | )
8 |
9 | type UserAgentParserResult struct {
10 | OS useragent.OSInfo
11 | BrowserName string
12 | BrowserVersion string
13 | IsMobile bool
14 | }
15 |
--------------------------------------------------------------------------------
/internal/trackers/label.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package trackers
4 |
5 | import "time"
6 |
7 | type tracker struct {
8 | label string
9 | startTime time.Time
10 | }
11 |
12 | func Begin(label string) *tracker {
13 | return &tracker{label: label, startTime: time.Now()}
14 | }
15 |
16 | func Run(label string, f func()) {
17 | var tr = Begin(label)
18 | f()
19 | tr.End()
20 | }
21 |
22 | func (this *tracker) End() {
23 | SharedManager.Add(this.label, time.Since(this.startTime).Seconds()*1000)
24 | }
25 |
26 | func (this *tracker) Begin(subLabel string) *tracker {
27 | return Begin(this.label + ":" + subLabel)
28 | }
29 |
30 | func (this *tracker) Add(duration time.Duration) {
31 | this.startTime = this.startTime.Add(-duration)
32 | }
33 |
--------------------------------------------------------------------------------
/internal/ttlcache/item.go:
--------------------------------------------------------------------------------
1 | package ttlcache
2 |
3 | type Item[T any] struct {
4 | Value T
5 | expiredAt int64
6 | }
7 |
--------------------------------------------------------------------------------
/internal/ttlcache/option.go:
--------------------------------------------------------------------------------
1 | package ttlcache
2 |
3 | type OptionInterface interface {
4 | }
5 |
6 | type PiecesOption struct {
7 | Count int
8 | }
9 |
10 | func NewPiecesOption(count int) *PiecesOption {
11 | return &PiecesOption{Count: count}
12 | }
13 |
14 | type MaxItemsOption struct {
15 | Count int
16 | }
17 |
18 | func NewMaxItemsOption(count int) *MaxItemsOption {
19 | return &MaxItemsOption{Count: count}
20 | }
21 |
--------------------------------------------------------------------------------
/internal/ttlcache/utils.go:
--------------------------------------------------------------------------------
1 | package ttlcache
2 |
3 | import "github.com/cespare/xxhash/v2"
4 |
5 | func HashKeyBytes(key []byte) uint64 {
6 | return xxhash.Sum64(key)
7 | }
8 |
9 | func HashKeyString(key string) uint64 {
10 | return xxhash.Sum64String(key)
11 | }
12 |
--------------------------------------------------------------------------------
/internal/utils/agents/agent.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package agents
4 |
5 | import (
6 | "regexp"
7 | "strings"
8 | )
9 |
10 | type Agent struct {
11 | Code string
12 | Keywords []string // user agent keywords
13 |
14 | suffixes []string // PTR suffixes
15 | reg *regexp.Regexp
16 | }
17 |
18 | func NewAgent(code string, suffixes []string, reg *regexp.Regexp, keywords []string) *Agent {
19 | return &Agent{
20 | Code: code,
21 | suffixes: suffixes,
22 | reg: reg,
23 | Keywords: keywords,
24 | }
25 | }
26 |
27 | func (this *Agent) Match(ptr string) bool {
28 | if len(this.suffixes) > 0 {
29 | for _, suffix := range this.suffixes {
30 | if strings.HasSuffix(ptr, suffix) {
31 | return true
32 | }
33 | }
34 | }
35 | if this.reg != nil {
36 | return this.reg.MatchString(ptr)
37 | }
38 | return false
39 | }
40 |
--------------------------------------------------------------------------------
/internal/utils/agents/agent_ip.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package agents
4 |
5 | type AgentIP struct {
6 | Id int64 `json:"id"`
7 | IP string `json:"ip"`
8 | AgentCode string `json:"agentCode"`
9 | }
10 |
--------------------------------------------------------------------------------
/internal/utils/agents/agents_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package agents_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/agents"
7 | "testing"
8 | )
9 |
10 | func TestIsAgentFromUserAgent(t *testing.T) {
11 | t.Log(agents.IsAgentFromUserAgent("Mozilla/5.0 (Linux;u;Android 4.2.2;zh-cn;) AppleWebKit/534.46 (KHTML,like Gecko) Version/5.1 Mobile Safari/10600.6.3 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"))
12 | t.Log(agents.IsAgentFromUserAgent("Mozilla/5.0 (Linux;u;Android 4.2.2;zh-cn;)"))
13 | }
14 |
15 | func BenchmarkIsAgentFromUserAgent(b *testing.B) {
16 | for i := 0; i < b.N; i++ {
17 | agents.IsAgentFromUserAgent("Mozilla/5.0 (Linux;u;Android 4.2.2;zh-cn;) AppleWebKit/534.46 (KHTML,like Gecko) Version/5.1 Mobile Safari/10600.6.3 (compatible; Yaho)")
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/internal/utils/agents/db.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package agents
4 |
5 | type DB interface {
6 | Init() error
7 | InsertAgentIP(ipId int64, ip string, agentCode string) error
8 | ListAgentIPs(offset int64, size int64) (agentIPs []*AgentIP, err error)
9 | }
10 |
--------------------------------------------------------------------------------
/internal/utils/agents/db_kv_objects.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package agents
4 |
5 | import (
6 | "encoding/binary"
7 | "encoding/json"
8 | "errors"
9 | )
10 |
11 | type AgentIPEncoder[T interface{ *AgentIP }] struct {
12 | }
13 |
14 | func (this *AgentIPEncoder[T]) Encode(value T) ([]byte, error) {
15 | return json.Marshal(value)
16 | }
17 |
18 | func (this *AgentIPEncoder[T]) EncodeField(value T, fieldName string) ([]byte, error) {
19 | return nil, errors.New("invalid field name '" + fieldName + "'")
20 | }
21 |
22 | func (this *AgentIPEncoder[T]) Decode(valueBytes []byte) (value T, err error) {
23 | err = json.Unmarshal(valueBytes, &value)
24 | return
25 | }
26 |
27 | // EncodeKey generate key for ip item
28 | func (this *AgentIPEncoder[T]) EncodeKey(item *AgentIP) string {
29 | var b = make([]byte, 8)
30 | if item.Id < 0 {
31 | item.Id = 0
32 | }
33 |
34 | binary.BigEndian.PutUint64(b, uint64(item.Id))
35 | return string(b)
36 | }
37 |
--------------------------------------------------------------------------------
/internal/utils/agents/ip_cache_map_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package agents
4 |
5 | import (
6 | "github.com/iwind/TeaGo/logs"
7 | "testing"
8 | )
9 |
10 | func TestNewIPCacheMap(t *testing.T) {
11 | var cacheMap = NewIPCacheMap(3)
12 |
13 | t.Log("====")
14 | cacheMap.Add("1")
15 | cacheMap.Add("2")
16 | logs.PrintAsJSON(cacheMap.m, t)
17 | logs.PrintAsJSON(cacheMap.list, t)
18 |
19 | t.Log("====")
20 | cacheMap.Add("3")
21 | logs.PrintAsJSON(cacheMap.m, t)
22 | logs.PrintAsJSON(cacheMap.list, t)
23 |
24 | t.Log("====")
25 | cacheMap.Add("4")
26 | logs.PrintAsJSON(cacheMap.m, t)
27 | logs.PrintAsJSON(cacheMap.list, t)
28 |
29 | t.Log("====")
30 | cacheMap.Add("3")
31 | logs.PrintAsJSON(cacheMap.m, t)
32 | logs.PrintAsJSON(cacheMap.list, t)
33 | }
34 |
--------------------------------------------------------------------------------
/internal/utils/agents/manager_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package agents_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/agents"
7 | "github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
8 | "github.com/iwind/TeaGo/Tea"
9 | _ "github.com/iwind/TeaGo/bootstrap"
10 | "testing"
11 | )
12 |
13 | func TestNewManager(t *testing.T) {
14 | if !testutils.IsSingleTesting() {
15 | return
16 | }
17 |
18 | var db = agents.NewSQLiteDB(Tea.Root + "/data/agents.db")
19 | err := db.Init()
20 | if err != nil {
21 | t.Fatal(err)
22 | }
23 |
24 | var manager = agents.NewManager()
25 | manager.SetDB(db)
26 | err = manager.Load()
27 | if err != nil {
28 | t.Fatal(err)
29 | }
30 |
31 | _, err = manager.Loop()
32 | if err != nil {
33 | t.Fatal(err)
34 | }
35 |
36 | t.Log(manager.LookupIP("192.168.3.100"))
37 | }
38 |
--------------------------------------------------------------------------------
/internal/utils/bfs/.gitignore:
--------------------------------------------------------------------------------
1 | DESIGN.md
2 | test.*
3 |
--------------------------------------------------------------------------------
/internal/utils/bfs/block_info.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package bfs
4 |
5 | type BlockInfo struct {
6 | OriginOffsetFrom int64 `json:"1,omitempty"`
7 | OriginOffsetTo int64 `json:"2,omitempty"`
8 |
9 | BFileOffsetFrom int64 `json:"3,omitempty"`
10 | BFileOffsetTo int64 `json:"4,omitempty"`
11 | }
12 |
13 | func (this BlockInfo) Contains(offset int64) bool {
14 | return this.OriginOffsetFrom <= offset && this.OriginOffsetTo > /** MUST be gt, NOT gte **/ offset
15 | }
16 |
--------------------------------------------------------------------------------
/internal/utils/bfs/blocks_file_options.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package bfs
4 |
5 | type BlockFileOptions struct {
6 | BytesPerSync int64
7 | }
8 |
9 | func (this *BlockFileOptions) EnsureDefaults() {
10 | if this.BytesPerSync <= 0 {
11 | this.BytesPerSync = 1 << 20
12 | }
13 | }
14 |
15 | var DefaultBlockFileOptions = &BlockFileOptions{
16 | BytesPerSync: 1 << 20,
17 | }
18 |
--------------------------------------------------------------------------------
/internal/utils/bfs/errors.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package bfs
4 |
5 | import (
6 | "errors"
7 | "os"
8 | )
9 |
10 | var ErrClosed = errors.New("the file closed")
11 | var ErrInvalidHash = errors.New("invalid hash")
12 | var ErrFileIsWriting = errors.New("the file is writing")
13 |
14 | func IsWritingErr(err error) bool {
15 | return err != nil && errors.Is(err, ErrFileIsWriting)
16 | }
17 |
18 | func IsNotExist(err error) bool {
19 | return err != nil && os.IsNotExist(err)
20 | }
21 |
--------------------------------------------------------------------------------
/internal/utils/bfs/hash.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package bfs
4 |
5 | import (
6 | "fmt"
7 | stringutil "github.com/iwind/TeaGo/utils/string"
8 | )
9 |
10 | var HashLen = 32
11 |
12 | // CheckHash check hash string format
13 | func CheckHash(hash string) bool {
14 | if len(hash) != HashLen {
15 | return false
16 | }
17 |
18 | for _, b := range hash {
19 | if !((b >= '0' && b <= '9') || (b >= 'a' && b <= 'f')) {
20 | return false
21 | }
22 | }
23 |
24 | return true
25 | }
26 |
27 | func CheckHashErr(hash string) error {
28 | if CheckHash(hash) {
29 | return nil
30 | }
31 | return fmt.Errorf("check hash '%s' failed: %w", hash, ErrInvalidHash)
32 | }
33 |
34 | func Hash(s string) string {
35 | return stringutil.Md5(s)
36 | }
37 |
--------------------------------------------------------------------------------
/internal/utils/bfs/hash_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package bfs_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/bfs"
7 | "github.com/iwind/TeaGo/assert"
8 | "math/rand"
9 | "strconv"
10 | "strings"
11 | "testing"
12 | )
13 |
14 | func TestCheckHash(t *testing.T) {
15 | var a = assert.NewAssertion(t)
16 |
17 | a.IsFalse(bfs.CheckHash("123456"))
18 | a.IsFalse(bfs.CheckHash(strings.Repeat("A", 32)))
19 | a.IsTrue(bfs.CheckHash(strings.Repeat("a", 32)))
20 | a.IsTrue(bfs.CheckHash(bfs.Hash("123456")))
21 | }
22 |
23 | func BenchmarkCheckHashErr(b *testing.B) {
24 | for i := 0; i < b.N; i++ {
25 | _ = bfs.CheckHash(bfs.Hash(strconv.Itoa(rand.Int())))
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/internal/utils/bfs/threads_limiter.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package bfs
4 |
5 | import "github.com/TeaOSLab/EdgeNode/internal/zero"
6 |
7 | // TODO 线程数可以根据硬盘数量动态调整?
8 | var readThreadsLimiter = make(chan zero.Zero, 8)
9 | var writeThreadsLimiter = make(chan zero.Zero, 8)
10 |
11 | func AckReadThread() {
12 | readThreadsLimiter <- zero.Zero{}
13 | }
14 |
15 | func ReleaseReadThread() {
16 | <-readThreadsLimiter
17 | }
18 |
19 | func AckWriteThread() {
20 | writeThreadsLimiter <- zero.Zero{}
21 | }
22 |
23 | func ReleaseWriteThread() {
24 | <-writeThreadsLimiter
25 | }
26 |
--------------------------------------------------------------------------------
/internal/utils/buffer_pool.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package utils
4 |
5 | import (
6 | "bytes"
7 | "sync"
8 | )
9 |
10 | var SharedBufferPool = NewBufferPool()
11 |
12 | // BufferPool pool for get byte slice
13 | type BufferPool struct {
14 | rawPool *sync.Pool
15 | }
16 |
17 | // NewBufferPool 创建新对象
18 | func NewBufferPool() *BufferPool {
19 | var pool = &BufferPool{}
20 | pool.rawPool = &sync.Pool{
21 | New: func() any {
22 | return &bytes.Buffer{}
23 | },
24 | }
25 | return pool
26 | }
27 |
28 | // Get 获取一个新的Buffer
29 | func (this *BufferPool) Get() (b *bytes.Buffer) {
30 | var buffer = this.rawPool.Get().(*bytes.Buffer)
31 | if buffer.Len() > 0 {
32 | buffer.Reset()
33 | }
34 | return buffer
35 | }
36 |
37 | // Put 放回一个使用过的byte slice
38 | func (this *BufferPool) Put(b *bytes.Buffer) {
39 | this.rawPool.Put(b)
40 | }
41 |
--------------------------------------------------------------------------------
/internal/utils/byte/utils.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package byteutils
4 |
5 | // Copy bytes
6 | func Copy(b []byte) []byte {
7 | var l = len(b)
8 | if l == 0 {
9 | return []byte{}
10 | }
11 | var d = make([]byte, l)
12 | copy(d, b)
13 | return d
14 | }
15 |
16 | // Append bytes
17 | func Append(b []byte, b2 ...byte) []byte {
18 | return append(Copy(b), b2...)
19 | }
20 |
21 | // Concat bytes
22 | func Concat(b []byte, b2 ...[]byte) []byte {
23 | b = Copy(b)
24 | for _, b3 := range b2 {
25 | b = append(b, b3...)
26 | }
27 | return b
28 | }
29 |
--------------------------------------------------------------------------------
/internal/utils/bytepool/byte_pool.go:
--------------------------------------------------------------------------------
1 | package bytepool
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | var Pool1k = NewPool(1 << 10)
8 | var Pool4k = NewPool(4 << 10)
9 | var Pool16k = NewPool(16 << 10)
10 | var Pool32k = NewPool(32 << 10)
11 |
12 | type Buf struct {
13 | Bytes []byte
14 | }
15 |
16 | // Pool for get byte slice
17 | type Pool struct {
18 | length int
19 | rawPool *sync.Pool
20 | }
21 |
22 | // NewPool 创建新对象
23 | func NewPool(length int) *Pool {
24 | if length < 0 {
25 | length = 1024
26 | }
27 | return &Pool{
28 | length: length,
29 | rawPool: &sync.Pool{
30 | New: func() any {
31 | return &Buf{
32 | Bytes: make([]byte, length),
33 | }
34 | },
35 | },
36 | }
37 | }
38 |
39 | // Get 获取一个新的byte slice
40 | func (this *Pool) Get() *Buf {
41 | return this.rawPool.Get().(*Buf)
42 | }
43 |
44 | // Put 放回一个使用过的byte slice
45 | func (this *Pool) Put(ptr *Buf) {
46 | this.rawPool.Put(ptr)
47 | }
48 |
49 | // Length 单个字节slice长度
50 | func (this *Pool) Length() int {
51 | return this.length
52 | }
53 |
--------------------------------------------------------------------------------
/internal/utils/clock/manager_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package clock_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/clock"
7 | "github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
8 | "testing"
9 | )
10 |
11 | func TestReadServer(t *testing.T) {
12 | if !testutils.IsSingleTesting() {
13 | return
14 | }
15 |
16 | t.Log(clock.NewClockManager().ReadServer("pool.ntp.org"))
17 | }
18 |
--------------------------------------------------------------------------------
/internal/utils/clock/ntp_packet.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package clock
4 |
5 | type NTPPacket struct {
6 | Settings uint8 // leap yr indicator, ver number, and mode
7 | Stratum uint8 // stratum of local clock
8 | Poll int8 // poll exponent
9 | Precision int8 // precision exponent
10 | RootDelay uint32 // root delay
11 | RootDispersion uint32 // root dispersion
12 | ReferenceID uint32 // reference id
13 | RefTimeSec uint32 // reference timestamp sec
14 | RefTimeFrac uint32 // reference timestamp fractional
15 | OrigTimeSec uint32 // origin time secs
16 | OrigTimeFrac uint32 // origin time fractional
17 | RxTimeSec uint32 // receive time secs
18 | RxTimeFrac uint32 // receive time frac
19 | TxTimeSec uint32 // transmit time secs
20 | TxTimeFrac uint32 // transmit time frac
21 | }
22 |
--------------------------------------------------------------------------------
/internal/utils/common_files.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package utils
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/zero"
7 | "strings"
8 | )
9 |
10 | var commonFileExtensionsMap = map[string]zero.Zero{
11 | ".ico": zero.New(),
12 | ".jpg": zero.New(),
13 | ".jpeg": zero.New(),
14 | ".gif": zero.New(),
15 | ".png": zero.New(),
16 | ".webp": zero.New(),
17 | ".woff2": zero.New(),
18 | ".js": zero.New(),
19 | ".css": zero.New(),
20 | ".ttf": zero.New(),
21 | ".otf": zero.New(),
22 | ".fnt": zero.New(),
23 | ".svg": zero.New(),
24 | ".map": zero.New(),
25 | ".avif": zero.New(),
26 | ".bmp": zero.New(),
27 | ".cur": zero.New(),
28 | }
29 |
30 | // IsCommonFileExtension 判断是否为常用文件扩展名
31 | // 不区分大小写,且不限于是否加点符号(.)
32 | func IsCommonFileExtension(ext string) bool {
33 | if len(ext) == 0 {
34 | return false
35 | }
36 | if ext[0] != '.' {
37 | ext = "." + ext
38 | }
39 | _, ok := commonFileExtensionsMap[strings.ToLower(ext)]
40 | return ok
41 | }
42 |
--------------------------------------------------------------------------------
/internal/utils/common_files_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package utils_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils"
7 | "github.com/iwind/TeaGo/assert"
8 | "testing"
9 | )
10 |
11 | func TestIsCommonFileExtension(t *testing.T) {
12 | var a = assert.NewAssertion(t)
13 |
14 | a.IsTrue(utils.IsCommonFileExtension(".jpg"))
15 | a.IsTrue(utils.IsCommonFileExtension("png"))
16 | a.IsTrue(utils.IsCommonFileExtension("PNG"))
17 | a.IsTrue(utils.IsCommonFileExtension(".PNG"))
18 | a.IsTrue(utils.IsCommonFileExtension("Png"))
19 | a.IsFalse(utils.IsCommonFileExtension("zip"))
20 | }
21 |
--------------------------------------------------------------------------------
/internal/utils/dbs/db_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package dbs_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/dbs"
7 | "net/url"
8 | "testing"
9 | )
10 |
11 | func TestParseDSN(t *testing.T) {
12 | var dsn = "file:/home/cache/p43/.indexes/db-3.db?cache=private&mode=ro&_journal_mode=WAL&_sync=" + dbs.SyncMode + "&_cache_size=88000"
13 | u, err := url.Parse(dsn)
14 | if err != nil {
15 | t.Fatal(err)
16 | }
17 | t.Log(u.Path) // expect: :/home/cache/p43/.indexes/db-3.db
18 | }
19 |
--------------------------------------------------------------------------------
/internal/utils/dbs/query_label.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package dbs
4 |
5 | import "time"
6 |
7 | type QueryLabel struct {
8 | manager *QueryStatManager
9 | query string
10 | before time.Time
11 | }
12 |
13 | func NewQueryLabel(manager *QueryStatManager, query string) *QueryLabel {
14 | return &QueryLabel{
15 | manager: manager,
16 | query: query,
17 | before: time.Now(),
18 | }
19 | }
20 |
21 | func (this *QueryLabel) End() {
22 | var cost = time.Since(this.before).Seconds()
23 | this.manager.AddCost(this.query, cost)
24 | }
25 |
--------------------------------------------------------------------------------
/internal/utils/dbs/query_stat.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package dbs
4 |
5 | type QueryStat struct {
6 | Query string
7 | CostMin float64
8 | CostMax float64
9 |
10 | CostTotal float64
11 | Calls int64
12 | }
13 |
14 | func NewQueryStat(query string) *QueryStat {
15 | return &QueryStat{
16 | Query: query,
17 | }
18 | }
19 |
20 | func (this *QueryStat) AddCost(cost float64) {
21 | if this.CostMin == 0 || this.CostMin > cost {
22 | this.CostMin = cost
23 | }
24 | if this.CostMax == 0 || this.CostMax < cost {
25 | this.CostMax = cost
26 | }
27 |
28 | this.CostTotal += cost
29 | this.Calls++
30 | }
31 |
--------------------------------------------------------------------------------
/internal/utils/dbs/query_stat_manager_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package dbs_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/dbs"
7 | "github.com/iwind/TeaGo/logs"
8 | "testing"
9 | "time"
10 | )
11 |
12 | func TestQueryStatManager(t *testing.T) {
13 | var manager = dbs.NewQueryStatManager()
14 | {
15 | var label = manager.AddQuery("sql 1")
16 | time.Sleep(1 * time.Second)
17 | label.End()
18 | }
19 | manager.AddQuery("sql 1").End()
20 | manager.AddQuery("sql 2").End()
21 | for _, stat := range manager.TopN(10) {
22 | logs.PrintAsJSON(stat, t)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/internal/utils/dbs/utils.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package dbs
4 |
5 | func IsClosedErr(err error) bool {
6 | return err == errDBIsClosed
7 | }
8 |
--------------------------------------------------------------------------------
/internal/utils/errors.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import "github.com/iwind/TeaGo/logs"
4 |
5 | func PrintError(err error) {
6 | // TODO 记录调用的文件名、行数
7 | logs.Println("[ERROR]" + err.Error())
8 | }
9 |
--------------------------------------------------------------------------------
/internal/utils/exec/look_others.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 | //go:build !linux
3 |
4 | package executils
5 |
6 | import "os/exec"
7 |
8 | func LookPath(file string) (string, error) {
9 | return exec.LookPath(file)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/utils/exit.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package utils
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/events"
7 | "os"
8 | )
9 |
10 | func Exit() {
11 | events.Notify(events.EventTerminated)
12 | os.Exit(0)
13 | }
14 |
--------------------------------------------------------------------------------
/internal/utils/expires/id_key_map_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package expires_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/expires"
7 | "github.com/iwind/TeaGo/assert"
8 | "github.com/iwind/TeaGo/logs"
9 | "testing"
10 | )
11 |
12 | func TestNewIdKeyMap(t *testing.T) {
13 | var a = assert.NewAssertion(t)
14 |
15 | var m = expires.NewIdKeyMap()
16 | m.Add(1, "1")
17 | m.Add(1, "2")
18 | m.Add(100, "100")
19 | logs.PrintAsJSON(m.IdKeys(), t)
20 | logs.PrintAsJSON(m.KeyIds(), t)
21 |
22 | {
23 | k, ok := m.Key(1)
24 | a.IsTrue(ok)
25 | a.IsTrue(k == "2")
26 | }
27 |
28 | {
29 | _, ok := m.Key(2)
30 | a.IsFalse(ok)
31 | }
32 |
33 | m.DeleteKey("2")
34 |
35 | {
36 | _, ok := m.Key(1)
37 | a.IsFalse(ok)
38 | }
39 |
40 | logs.PrintAsJSON(m.IdKeys(), t)
41 | logs.PrintAsJSON(m.KeyIds(), t)
42 |
43 | m.DeleteId(100)
44 |
45 | logs.PrintAsJSON(m.IdKeys(), t)
46 | logs.PrintAsJSON(m.KeyIds(), t)
47 | }
48 |
--------------------------------------------------------------------------------
/internal/utils/fnv/hash.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package fnv
4 |
5 | const (
6 | offset64 uint64 = 14695981039346656037
7 | prime64 uint64 = 1099511628211
8 | )
9 |
10 | // HashString
11 | // 非unique Hash
12 | func HashString(key string) uint64 {
13 | var hash = offset64
14 | for _, b := range key {
15 | hash ^= uint64(b)
16 | hash *= prime64
17 | }
18 | return hash
19 | }
20 |
21 | // Hash
22 | // 非unique Hash
23 | func Hash(key []byte) uint64 {
24 | var hash = offset64
25 | for _, b := range key {
26 | hash ^= uint64(b)
27 | hash *= prime64
28 | }
29 | return hash
30 | }
31 |
--------------------------------------------------------------------------------
/internal/utils/fnv/hash_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package fnv_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/fnv"
7 | "github.com/iwind/TeaGo/types"
8 | "testing"
9 | )
10 |
11 | func TestHash(t *testing.T) {
12 | for _, key := range []string{"costarring", "liquid", "hello"} {
13 | var h = fnv.HashString(key)
14 | t.Log(key + " => " + types.String(h))
15 | }
16 | }
17 |
18 | func BenchmarkHashString(b *testing.B) {
19 | b.RunParallel(func(pb *testing.PB) {
20 | for pb.Next() {
21 | _ = fnv.HashString("abcdefh")
22 | }
23 | })
24 | }
25 |
26 | func BenchmarkHashString_Long(b *testing.B) {
27 | b.RunParallel(func(pb *testing.PB) {
28 | for pb.Next() {
29 | _ = fnv.HashString("HELLO,WORLDHELLO,WORLDHELLO,WORLDHELLO,WORLDHELLO,WORLDHELLO,WORLD")
30 | }
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/internal/utils/fs/disk_test_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package fsutils_test
4 |
5 | import (
6 | fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
7 | "testing"
8 | )
9 |
10 | func TestCheckDiskWritingSpeed(t *testing.T) {
11 | t.Log(fsutils.CheckDiskWritingSpeed())
12 | }
13 |
14 | func TestCheckDiskIsFast(t *testing.T) {
15 | t.Log(fsutils.CheckDiskIsFast())
16 | }
--------------------------------------------------------------------------------
/internal/utils/fs/file_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package fsutils_test
4 |
5 | import (
6 | fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
7 | "github.com/iwind/TeaGo/assert"
8 | "testing"
9 | )
10 |
11 | func TestFileFlags(t *testing.T) {
12 | var a = assert.NewAssertion(t)
13 | a.IsTrue(fsutils.FlagRead&fsutils.FlagRead == fsutils.FlagRead)
14 | a.IsTrue(fsutils.FlagWrite&fsutils.FlagWrite != fsutils.FlagRead)
15 | a.IsTrue((fsutils.FlagWrite|fsutils.FlagRead)&fsutils.FlagRead == fsutils.FlagRead)
16 | }
17 |
--------------------------------------------------------------------------------
/internal/utils/fs/locker_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package fsutils_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
7 | "testing"
8 | )
9 |
10 | func TestLocker_Lock(t *testing.T) {
11 | var path = "/tmp/file-test"
12 | var locker = fsutils.NewLocker(path)
13 | err := locker.Lock()
14 | if err != nil {
15 | t.Fatal(err)
16 | }
17 | _ = locker.Release()
18 |
19 | var locker2 = fsutils.NewLocker(path)
20 | err = locker2.Lock()
21 | if err != nil {
22 | t.Fatal(err)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/internal/utils/fs/os_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package fsutils_test
4 |
5 | import (
6 | fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
7 | "github.com/iwind/TeaGo/assert"
8 | "os"
9 | "testing"
10 | )
11 |
12 | func TestOpenFile(t *testing.T) {
13 | f, err := fsutils.OpenFile("./os_test.go", os.O_RDONLY, 0444)
14 | if err != nil {
15 | t.Fatal(err)
16 | }
17 | _ = f.Close()
18 | }
19 |
20 | func TestExistFile(t *testing.T) {
21 | var a = assert.NewAssertion(t)
22 |
23 | {
24 | b, err := fsutils.ExistFile("./os_test.go")
25 | if err != nil {
26 | t.Fatal(err)
27 | }
28 | a.IsTrue(b)
29 | }
30 |
31 | {
32 | b, err := fsutils.ExistFile("./os_test2.go")
33 | if err != nil {
34 | t.Fatal(err)
35 | }
36 | a.IsFalse(b)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/internal/utils/fs/status_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package fsutils_test
4 |
5 | import (
6 | fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
7 | "testing"
8 | "time"
9 | )
10 |
11 | func TestWaitLoad(t *testing.T) {
12 | fsutils.WaitLoad(100, 5, 1*time.Minute)
13 | }
14 |
--------------------------------------------------------------------------------
/internal/utils/http_test.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/iwind/TeaGo/assert"
5 | "testing"
6 | "time"
7 | )
8 |
9 | func TestNewHTTPClient(t *testing.T) {
10 | a := assert.NewAssertion(t)
11 |
12 | client := NewHTTPClient(1 * time.Second)
13 | a.IsTrue(client.Timeout == 1*time.Second)
14 |
15 | client2 := NewHTTPClient(1 * time.Second)
16 | a.IsTrue(client != client2)
17 | }
18 |
19 | func TestSharedHTTPClient(t *testing.T) {
20 | a := assert.NewAssertion(t)
21 |
22 | _ = SharedHttpClient(2 * time.Second)
23 | _ = SharedHttpClient(3 * time.Second)
24 |
25 | client := SharedHttpClient(1 * time.Second)
26 | a.IsTrue(client.Timeout == 1*time.Second)
27 |
28 | client2 := SharedHttpClient(1 * time.Second)
29 | a.IsTrue(client == client2)
30 |
31 | t.Log(timeoutClientMap)
32 | }
33 |
--------------------------------------------------------------------------------
/internal/utils/jsonutils/map.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package jsonutils
4 |
5 | import (
6 | "encoding/json"
7 | "github.com/iwind/TeaGo/maps"
8 | )
9 |
10 | func MapToObject(m maps.Map, ptr any) error {
11 | if m == nil {
12 | return nil
13 | }
14 | mJSON, err := json.Marshal(m)
15 | if err != nil {
16 | return err
17 | }
18 | return json.Unmarshal(mJSON, ptr)
19 | }
20 |
21 | func ObjectToMap(ptr any) (maps.Map, error) {
22 | if ptr == nil {
23 | return maps.Map{}, nil
24 | }
25 | ptrJSON, err := json.Marshal(ptr)
26 | if err != nil {
27 | return nil, err
28 | }
29 | var result = maps.Map{}
30 | err = json.Unmarshal(ptrJSON, &result)
31 | if err != nil {
32 | return nil, err
33 | }
34 | return result, nil
35 | }
36 |
37 | func Copy(destPtr any, srcPtr any) error {
38 | data, err := json.Marshal(srcPtr)
39 | if err != nil {
40 | return err
41 | }
42 | err = json.Unmarshal(data, destPtr)
43 | return err
44 | }
45 |
--------------------------------------------------------------------------------
/internal/utils/jsonutils/map_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package jsonutils
4 |
5 | import (
6 | "github.com/iwind/TeaGo/assert"
7 | "github.com/iwind/TeaGo/maps"
8 | "testing"
9 | )
10 |
11 | func TestMapToObject(t *testing.T) {
12 | a := assert.NewAssertion(t)
13 |
14 | type typeA struct {
15 | B int `json:"b"`
16 | C bool `json:"c"`
17 | }
18 |
19 | {
20 | var obj = &typeA{B: 1, C: true}
21 | m, err := ObjectToMap(obj)
22 | if err != nil {
23 | t.Fatal(err)
24 | }
25 | PrintT(m, t)
26 | a.IsTrue(m.GetInt("b") == 1)
27 | a.IsTrue(m.GetBool("c") == true)
28 | }
29 |
30 | {
31 | var obj = &typeA{}
32 | err := MapToObject(maps.Map{
33 | "b": 1024,
34 | "c": true,
35 | }, obj)
36 | if err != nil {
37 | t.Fatal(err)
38 | }
39 | a.IsTrue(obj.B == 1024)
40 | a.IsTrue(obj.C == true)
41 | PrintT(obj, t)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/internal/utils/jsonutils/utils.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package jsonutils
4 |
5 | import (
6 | "bytes"
7 | "encoding/json"
8 | "testing"
9 | )
10 |
11 | func PrintT(obj any, t *testing.T) {
12 | data, err := json.MarshalIndent(obj, "", " ")
13 | if err != nil {
14 | t.Log(err)
15 | } else {
16 | t.Log(string(data))
17 | }
18 | }
19 |
20 | func Equal(obj1 any, obj2 any) bool {
21 | data1, err := json.Marshal(obj1)
22 | if err != nil {
23 | return false
24 | }
25 |
26 | data2, err := json.Marshal(obj2)
27 | if err != nil {
28 | return false
29 | }
30 |
31 | return bytes.Equal(data1, data2)
32 | }
33 |
--------------------------------------------------------------------------------
/internal/utils/jsonutils/utils_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package jsonutils_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/jsonutils"
7 | "github.com/iwind/TeaGo/assert"
8 | "github.com/iwind/TeaGo/maps"
9 | "testing"
10 | )
11 |
12 | func TestEqual(t *testing.T) {
13 | var a = assert.NewAssertion(t)
14 |
15 | {
16 | var m1 = maps.Map{"a": 1, "b2": true}
17 | var m2 = maps.Map{"b2": true, "a": 1}
18 | a.IsTrue(jsonutils.Equal(m1, m2))
19 | }
20 |
21 | {
22 | var m1 = maps.Map{"a": 1, "b2": true, "c": nil}
23 | var m2 = maps.Map{"b2": true, "a": 1}
24 | a.IsFalse(jsonutils.Equal(m1, m2))
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/internal/utils/kvstore/errors.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package kvstore
4 |
5 | import (
6 | "errors"
7 | "fmt"
8 | "github.com/cockroachdb/pebble"
9 | )
10 |
11 | var ErrTableNotFound = errors.New("table not found")
12 | var ErrKeyTooLong = errors.New("too long key")
13 | var ErrSkip = errors.New("skip") // skip count in iterator
14 | var ErrTableClosed = errors.New("table closed")
15 |
16 | func IsNotFound(err error) bool {
17 | return err != nil && errors.Is(err, pebble.ErrNotFound)
18 | }
19 |
20 | func IsSkipError(err error) bool {
21 | return err != nil && errors.Is(err, ErrSkip)
22 | }
23 |
24 | func Skip() (bool, error) {
25 | return true, ErrSkip
26 | }
27 |
28 | func NewTableClosedErr(tableName string) error {
29 | return fmt.Errorf("table '"+tableName+"' closed: %w", ErrTableClosed)
30 | }
31 |
--------------------------------------------------------------------------------
/internal/utils/kvstore/item.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package kvstore
4 |
5 | type Item[T any] struct {
6 | Key string
7 | Value T
8 | FieldKey []byte
9 | }
10 |
--------------------------------------------------------------------------------
/internal/utils/kvstore/iterator_options.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package kvstore
4 |
5 | import "github.com/cockroachdb/pebble"
6 |
7 | type IteratorOptions struct {
8 | LowerBound []byte
9 | UpperBound []byte
10 | }
11 |
12 | func (this *IteratorOptions) RawOptions() *pebble.IterOptions {
13 | return &pebble.IterOptions{
14 | LowerBound: this.LowerBound,
15 | UpperBound: this.UpperBound,
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/internal/utils/kvstore/logger.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package kvstore
4 |
5 | import (
6 | "fmt"
7 | "github.com/TeaOSLab/EdgeNode/internal/remotelogs"
8 | )
9 |
10 | type Logger struct {
11 | }
12 |
13 | func NewLogger() *Logger {
14 | return &Logger{}
15 | }
16 |
17 | func (this *Logger) Infof(format string, args ...any) {
18 | // stub
19 | }
20 |
21 | func (this *Logger) Errorf(format string, args ...any) {
22 | remotelogs.Error("KV", fmt.Sprintf(format, args...))
23 | }
24 |
25 | func (this *Logger) Fatalf(format string, args ...any) {
26 | remotelogs.Error("KV", fmt.Sprintf(format, args...))
27 | }
28 |
--------------------------------------------------------------------------------
/internal/utils/kvstore/options.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package kvstore
4 |
5 | import "github.com/cockroachdb/pebble"
6 |
7 | var DefaultWriteOptions = &pebble.WriteOptions{
8 | Sync: false,
9 | }
10 |
11 | var DefaultWriteSyncOptions = &pebble.WriteOptions{
12 | Sync: true,
13 | }
14 |
--------------------------------------------------------------------------------
/internal/utils/kvstore/table_counter.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package kvstore
4 |
5 | type CounterTable[T int64 | uint64] struct {
6 | *Table[T]
7 | }
8 |
9 | func NewCounterTable[T int64 | uint64](name string) (*CounterTable[T], error) {
10 | table, err := NewTable[T](name, NewIntValueEncoder[T]())
11 | if err != nil {
12 | return nil, err
13 | }
14 |
15 | return &CounterTable[T]{
16 | Table: table,
17 | }, nil
18 | }
19 |
20 | func (this *CounterTable[T]) Increase(key string, delta T) (newValue T, err error) {
21 | if this.isClosed {
22 | err = NewTableClosedErr(this.name)
23 | return
24 | }
25 |
26 | err = this.Table.WriteTx(func(tx *Tx[T]) error {
27 | value, getErr := tx.Get(key)
28 | if getErr != nil {
29 | if !IsNotFound(getErr) {
30 | return getErr
31 | }
32 | }
33 |
34 | newValue = value + delta
35 | return tx.Set(key, newValue)
36 | })
37 | return
38 | }
39 |
--------------------------------------------------------------------------------
/internal/utils/kvstore/table_field.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package kvstore
4 |
5 | import (
6 | "errors"
7 | )
8 |
9 | func (this *Table[T]) AddField(fieldName string) error {
10 | if !IsValidName(fieldName) {
11 | return errors.New("invalid field name '" + fieldName + "'")
12 | }
13 |
14 | // check existence
15 | for _, field := range this.fieldNames {
16 | if field == fieldName {
17 | return nil
18 | }
19 | }
20 |
21 | this.fieldNames = append(this.fieldNames, fieldName)
22 | return nil
23 | }
24 |
25 | func (this *Table[T]) AddFields(fieldName ...string) error {
26 | for _, subFieldName := range fieldName {
27 | err := this.AddField(subFieldName)
28 | if err != nil {
29 | return err
30 | }
31 | }
32 | return nil
33 | }
34 |
35 | func (this *Table[T]) DropField(fieldName string) error {
36 | this.mu.Lock()
37 | defer this.mu.Unlock()
38 |
39 | var start = this.FieldKey(fieldName + "$")
40 | return this.db.store.rawDB.DeleteRange(start, append(start, 0xFF), DefaultWriteOptions)
41 | }
42 |
--------------------------------------------------------------------------------
/internal/utils/kvstore/table_interface.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package kvstore
4 |
5 | type TableInterface interface {
6 | Name() string
7 | SetNamespace(namespace []byte)
8 | SetDB(db *DB)
9 | Close() error
10 | }
11 |
--------------------------------------------------------------------------------
/internal/utils/kvstore/utils_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package kvstore_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/kvstore"
7 | "github.com/iwind/TeaGo/Tea"
8 | "github.com/iwind/TeaGo/assert"
9 | _ "github.com/iwind/TeaGo/bootstrap"
10 | "testing"
11 | )
12 |
13 | func TestRemoveDB(t *testing.T) {
14 | err := kvstore.RemoveStore(Tea.Root + "/data/stores/test2.store")
15 | if err != nil {
16 | t.Fatal(err)
17 | }
18 | }
19 |
20 | func TestIsValidName(t *testing.T) {
21 | var a = assert.NewAssertion(t)
22 |
23 | a.IsTrue(kvstore.IsValidName("a"))
24 | a.IsTrue(kvstore.IsValidName("aB"))
25 | a.IsTrue(kvstore.IsValidName("aBC1"))
26 | a.IsTrue(kvstore.IsValidName("aBC1._-"))
27 | a.IsFalse(kvstore.IsValidName(" aBC1._-"))
28 | a.IsFalse(kvstore.IsValidName(""))
29 | }
30 |
--------------------------------------------------------------------------------
/internal/utils/kvstore/value_encoder.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package kvstore
4 |
5 | import "encoding/json"
6 |
7 | type ValueEncoder[T any] interface {
8 | Encode(value T) ([]byte, error)
9 | EncodeField(value T, fieldName string) ([]byte, error)
10 | Decode(valueBytes []byte) (value T, err error)
11 | }
12 |
13 | type BaseObjectEncoder[T any] struct {
14 | }
15 |
16 | func (this *BaseObjectEncoder[T]) Encode(value T) ([]byte, error) {
17 | return json.Marshal(value)
18 | }
19 |
20 | func (this *BaseObjectEncoder[T]) Decode(valueData []byte) (value T, err error) {
21 | err = json.Unmarshal(valueData, &value)
22 | return
23 | }
24 |
--------------------------------------------------------------------------------
/internal/utils/kvstore/value_encoder_bool.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package kvstore
4 |
5 | type BoolValueEncoder[T bool] struct {
6 | }
7 |
8 | func NewBoolValueEncoder[T bool]() *BoolValueEncoder[T] {
9 | return &BoolValueEncoder[T]{}
10 | }
11 |
12 | func (this *BoolValueEncoder[T]) Encode(value T) ([]byte, error) {
13 | if value {
14 | return []byte{1}, nil
15 | }
16 | return []byte{0}, nil
17 | }
18 |
19 | func (this *BoolValueEncoder[T]) EncodeField(value T, fieldName string) ([]byte, error) {
20 | _ = fieldName
21 | return this.Encode(value)
22 | }
23 |
24 | func (this *BoolValueEncoder[T]) Decode(valueData []byte) (value T, err error) {
25 | if len(valueData) == 1 {
26 | value = valueData[0] == 1
27 | }
28 | return
29 | }
30 |
--------------------------------------------------------------------------------
/internal/utils/kvstore/value_encoder_bytes.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package kvstore
4 |
5 | type BytesValueEncoder[T []byte] struct {
6 | }
7 |
8 | func NewBytesValueEncoder[T []byte]() *BytesValueEncoder[T] {
9 | return &BytesValueEncoder[T]{}
10 | }
11 |
12 | func (this *BytesValueEncoder[T]) Encode(value T) ([]byte, error) {
13 | if len(value) == 0 {
14 | return nil, nil
15 | }
16 |
17 | var resultValue = make([]byte, len(value))
18 | copy(resultValue, value)
19 | return resultValue, nil
20 | }
21 |
22 | func (this *BytesValueEncoder[T]) EncodeField(value T, fieldName string) ([]byte, error) {
23 | _ = fieldName
24 | return this.Encode(value)
25 | }
26 |
27 | func (this *BytesValueEncoder[T]) Decode(valueData []byte) (value T, err error) {
28 | if len(valueData) == 0 {
29 | return
30 | }
31 |
32 | value = make([]byte, len(valueData))
33 | copy(value, valueData)
34 | return
35 | }
36 |
--------------------------------------------------------------------------------
/internal/utils/kvstore/value_encoder_nil.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package kvstore
4 |
5 | type NilValueEncoder[T []byte] struct {
6 | }
7 |
8 | func NewNilValueEncoder[T []byte]() *NilValueEncoder[T] {
9 | return &NilValueEncoder[T]{}
10 | }
11 |
12 | func (this *NilValueEncoder[T]) Encode(value T) ([]byte, error) {
13 | return nil, nil
14 | }
15 |
16 | func (this *NilValueEncoder[T]) EncodeField(value T, fieldName string) ([]byte, error) {
17 | return nil, nil
18 | }
19 |
20 | func (this *NilValueEncoder[T]) Decode(valueData []byte) (value T, err error) {
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/internal/utils/kvstore/value_encoder_string.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package kvstore
4 |
5 | type StringValueEncoder[T string] struct {
6 | }
7 |
8 | func NewStringValueEncoder[T string]() *StringValueEncoder[T] {
9 | return &StringValueEncoder[T]{}
10 | }
11 |
12 | func (this *StringValueEncoder[T]) Encode(value T) ([]byte, error) {
13 | return []byte(value), nil
14 | }
15 |
16 | func (this *StringValueEncoder[T]) EncodeField(value T, fieldName string) ([]byte, error) {
17 | _ = fieldName
18 | return this.Encode(value)
19 | }
20 |
21 | func (this *StringValueEncoder[T]) Decode(valueData []byte) (value T, err error) {
22 | value = T(valueData)
23 | return
24 | }
25 |
--------------------------------------------------------------------------------
/internal/utils/linkedlist/item.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package linkedlist
4 |
5 | type Item[T any] struct {
6 | prev *Item[T]
7 | next *Item[T]
8 |
9 | Value T
10 | }
11 |
12 | func NewItem[T any](value T) *Item[T] {
13 | return &Item[T]{Value: value}
14 | }
15 |
--------------------------------------------------------------------------------
/internal/utils/lookup.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeCommon/pkg/configutils"
5 | "github.com/miekg/dns"
6 | )
7 |
8 | // LookupCNAME 获取CNAME
9 | func LookupCNAME(host string) (string, error) {
10 | config, err := dns.ClientConfigFromFile("/etc/resolv.conf")
11 | if err != nil {
12 | return "", err
13 | }
14 |
15 | c := new(dns.Client)
16 | m := new(dns.Msg)
17 |
18 | m.SetQuestion(host+".", dns.TypeCNAME)
19 | m.RecursionDesired = true
20 |
21 | var lastErr error
22 | for _, serverAddr := range config.Servers {
23 | r, _, err := c.Exchange(m, configutils.QuoteIP(serverAddr)+":"+config.Port)
24 | if err != nil {
25 | lastErr = err
26 | continue
27 | }
28 | if len(r.Answer) == 0 {
29 | continue
30 | }
31 |
32 | return r.Answer[0].(*dns.CNAME).Target, nil
33 | }
34 | return "", lastErr
35 | }
36 |
--------------------------------------------------------------------------------
/internal/utils/lookup_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package utils
4 |
5 | import "testing"
6 |
7 | func TestLookupCNAME(t *testing.T) {
8 | t.Log(LookupCNAME("www.yun4s.cn"))
9 | }
10 |
--------------------------------------------------------------------------------
/internal/utils/maps/map_fixed_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package maputils_test
4 |
5 | import (
6 | maputils "github.com/TeaOSLab/EdgeNode/internal/utils/maps"
7 | "testing"
8 | )
9 |
10 | func TestNewFixedMap(t *testing.T) {
11 | var m = maputils.NewFixedMap[string, int](3)
12 | m.Put("a", 1)
13 | t.Log(m.RawMap())
14 | t.Log(m.Get("a"))
15 | t.Log(m.Get("b"))
16 |
17 | m.Put("b", 2)
18 | m.Put("c", 3)
19 | t.Log(m.RawMap(), m.Keys())
20 |
21 | m.Put("d", 4)
22 | t.Log(m.RawMap(), m.Keys())
23 |
24 | m.Put("b", 200)
25 | t.Log(m.RawMap(), m.Keys())
26 | }
27 |
--------------------------------------------------------------------------------
/internal/utils/mem/system_1.19.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 | //go:build go1.19
3 |
4 | package memutils
5 |
6 | import (
7 | "runtime/debug"
8 | )
9 |
10 | // 设置软内存最大值
11 | func setMaxMemory(memoryGB int) {
12 | if memoryGB <= 0 {
13 | memoryGB = 1
14 | }
15 |
16 | var maxMemoryBytes = (int64(memoryGB) << 30) * 80 / 100 // 默认 80%
17 | debug.SetMemoryLimit(maxMemoryBytes)
18 | }
19 |
--------------------------------------------------------------------------------
/internal/utils/mem/system_before_1.19.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 | //go:build !go1.19
3 |
4 | package memutils
5 |
6 | // 设置软内存最大值
7 | func setMaxMemory(memoryGB int) {
8 |
9 | }
--------------------------------------------------------------------------------
/internal/utils/mem/system_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package memutils_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/mem"
7 | "testing"
8 | )
9 |
10 | func TestSystemMemoryGB(t *testing.T) {
11 | t.Log(memutils.SystemMemoryGB())
12 | t.Log(memutils.SystemMemoryGB())
13 | t.Log(memutils.SystemMemoryGB())
14 | t.Log(memutils.SystemMemoryBytes())
15 | t.Log(memutils.SystemMemoryBytes())
16 | t.Log(memutils.SystemMemoryBytes()>>30, "GB")
17 | t.Log("available:", memutils.AvailableMemoryGB())
18 | }
19 |
--------------------------------------------------------------------------------
/internal/utils/minifiers/minify.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 | //go:build !plus
3 |
4 | package minifiers
5 |
6 | import (
7 | "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
8 | "net/http"
9 | )
10 |
11 | // MinifyResponse minify response body
12 | func MinifyResponse(config *serverconfigs.HTTPPageOptimizationConfig, url string, resp *http.Response) error {
13 | // stub
14 | return nil
15 | }
16 |
--------------------------------------------------------------------------------
/internal/utils/net.go:
--------------------------------------------------------------------------------
1 | //go:build !freebsd
2 | // +build !freebsd
3 |
4 | package utils
5 |
6 | import (
7 | "context"
8 | "github.com/iwind/TeaGo/logs"
9 | "net"
10 | "syscall"
11 | )
12 |
13 | // ListenReuseAddr 监听可重用的端口
14 | func ListenReuseAddr(network string, addr string) (net.Listener, error) {
15 | config := &net.ListenConfig{
16 | Control: func(network, address string, c syscall.RawConn) error {
17 | return c.Control(func(fd uintptr) {
18 | err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, SO_REUSEPORT, 1)
19 | if err != nil {
20 | logs.Println("[LISTEN]" + err.Error())
21 | }
22 | })
23 | },
24 | KeepAlive: 0,
25 | }
26 | return config.Listen(context.Background(), network, addr)
27 | }
28 |
--------------------------------------------------------------------------------
/internal/utils/net_darwin.go:
--------------------------------------------------------------------------------
1 | // +build darwin
2 |
3 | package utils
4 |
5 | import (
6 | "syscall"
7 | )
8 |
9 | const SO_REUSEPORT = syscall.SO_REUSEPORT
10 |
--------------------------------------------------------------------------------
/internal/utils/net_linux.go:
--------------------------------------------------------------------------------
1 | // +build linux
2 | // 可以在 /usr/include/asm-generic/socket.h 中找到 SO_REUSEPORT 值
3 |
4 | package utils
5 |
6 | const SO_REUSEPORT = 15
7 |
--------------------------------------------------------------------------------
/internal/utils/net_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package utils_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils"
7 | "testing"
8 | )
9 |
10 | func TestParseAddrHost(t *testing.T) {
11 | for _, addr := range []string{"a", "example.com", "example.com:1234", "::1", "[::1]", "[::1]:8080"} {
12 | t.Log(addr + " => " + utils.ParseAddrHost(addr))
13 | }
14 | }
15 |
16 | func TestMergePorts(t *testing.T) {
17 | for _, ports := range [][]int{
18 | {},
19 | {80},
20 | {80, 83, 85},
21 | {80, 81, 83, 85, 86, 87, 88, 90},
22 | {0, 0, 1, 1, 2, 2, 2, 3, 3, 3},
23 | } {
24 | t.Log(ports, "=>", utils.MergePorts(ports))
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/internal/utils/number.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | func MinInt(min1 int, min2 int) int {
4 | if min1 < min2 {
5 | return min1
6 | }
7 | return min2
8 | }
9 |
10 | func MaxInt(min1 int, min2 int) int {
11 | if min1 < min2 {
12 | return min2
13 | }
14 | return min1
15 | }
16 |
--------------------------------------------------------------------------------
/internal/utils/path.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | // CleanPath 清理Path中的多余的字符
4 | func CleanPath(path string) string {
5 | l := len(path)
6 | if l == 0 {
7 | return "/"
8 | }
9 | result := []byte{'/'}
10 | isSlash := true
11 | for i := 0; i < l; i++ {
12 | if path[i] == '?' {
13 | result = append(result, path[i:]...)
14 | break
15 | }
16 | if path[i] == '\\' || path[i] == '/' {
17 | if !isSlash {
18 | isSlash = true
19 | result = append(result, '/')
20 | }
21 | } else {
22 | isSlash = false
23 | result = append(result, path[i])
24 | }
25 | }
26 | return string(result)
27 | }
28 |
--------------------------------------------------------------------------------
/internal/utils/path_test.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/iwind/TeaGo/assert"
5 | "testing"
6 | )
7 |
8 | func TestCleanPath(t *testing.T) {
9 | a := assert.NewAssertion(t)
10 |
11 | a.IsTrue(CleanPath("") == "/")
12 | a.IsTrue(CleanPath("/hello/world") == "/hello/world")
13 | a.IsTrue(CleanPath("\\hello\\world") == "/hello/world")
14 | a.IsTrue(CleanPath("/\\hello\\//world") == "/hello/world")
15 | a.IsTrue(CleanPath("hello/world") == "/hello/world")
16 | a.IsTrue(CleanPath("/hello////world") == "/hello/world")
17 | }
18 |
19 | func TestCleanPath_Args(t *testing.T) {
20 | a := assert.NewAssertion(t)
21 | a.IsTrue(CleanPath("/hello/world?base=///////") == "/hello/world?base=///////")
22 | }
23 |
24 | func BenchmarkCleanPath(b *testing.B) {
25 | for i := 0; i < b.N; i++ {
26 | _ = CleanPath("/hello///world/very/long/very//long")
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/internal/utils/percpu/chan.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package percpu
4 |
5 | import (
6 | "runtime"
7 | )
8 |
9 | type Chan[T any] struct {
10 | c chan T
11 |
12 | count int
13 | cList []chan T
14 | }
15 |
16 | func NewChan[T any](size int) *Chan[T] {
17 | var count = max(runtime.NumCPU(), runtime.GOMAXPROCS(0))
18 | var cList []chan T
19 | for i := 0; i < count; i++ {
20 | cList = append(cList, make(chan T, size))
21 | }
22 |
23 | return &Chan[T]{
24 | c: make(chan T, size),
25 | count: count,
26 | cList: cList,
27 | }
28 | }
29 |
30 | func (this *Chan[T]) C() chan T {
31 | var procId = GetProcId()
32 | if procId < this.count {
33 | return this.cList[procId]
34 | }
35 | return this.c
36 | }
37 |
--------------------------------------------------------------------------------
/internal/utils/percpu/chan_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package percpu_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/percpu"
7 | "github.com/TeaOSLab/EdgeNode/internal/zero"
8 | "testing"
9 | )
10 |
11 | func TestChan_C(t *testing.T) {
12 | var c = percpu.NewChan[zero.Zero](10)
13 | c.C() <- zero.Zero{}
14 |
15 | t.Log(<-c.C())
16 |
17 | select {
18 | case <-c.C():
19 | t.Fatal("should not return from here")
20 | default:
21 | t.Log("ok")
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/internal/utils/percpu/proc_id.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package percpu
4 |
5 | import (
6 | _ "unsafe"
7 | )
8 |
9 | //go:linkname runtime_procPin runtime.procPin
10 | func runtime_procPin() int
11 |
12 | //go:linkname runtime_procUnpin runtime.procUnpin
13 | func runtime_procUnpin() int
14 |
15 | func GetProcId() int {
16 | var pid = runtime_procPin()
17 | runtime_procUnpin()
18 | return pid
19 | }
20 |
--------------------------------------------------------------------------------
/internal/utils/ratelimit/bandwidth_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package ratelimit_test
4 |
5 | import (
6 | "context"
7 | "github.com/TeaOSLab/EdgeNode/internal/utils/ratelimit"
8 | "github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
9 | "testing"
10 | )
11 |
12 | func TestBandwidth(t *testing.T) {
13 | if !testutils.IsSingleTesting() {
14 | return
15 | }
16 |
17 | var bandwidth = ratelimit.NewBandwidth(32 << 10)
18 | bandwidth.Ack(context.Background(), 123)
19 | bandwidth.Ack(context.Background(), 16 << 10)
20 | bandwidth.Ack(context.Background(), 32 << 10)
21 | }
22 |
23 | func TestBandwidth_0(t *testing.T) {
24 | var bandwidth = ratelimit.NewBandwidth(0)
25 | bandwidth.Ack(context.Background(), 123)
26 | bandwidth.Ack(context.Background(), 123456)
27 | }
28 |
--------------------------------------------------------------------------------
/internal/utils/ratelimit/counter_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package ratelimit_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/ratelimit"
7 | "github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
8 | "testing"
9 | "time"
10 | )
11 |
12 | func TestCounter_ACK(t *testing.T) {
13 | if !testutils.IsSingleTesting() {
14 | return
15 | }
16 |
17 | var counter = ratelimit.NewCounter(10)
18 |
19 | go func() {
20 | for i := 0; i < 10; i++ {
21 | counter.Ack()
22 | }
23 | //counter.Release()
24 | t.Log("waiting", time.Now().Unix())
25 | counter.Ack()
26 | t.Log("done", time.Now().Unix())
27 | }()
28 |
29 | time.Sleep(1 * time.Second)
30 | counter.Close()
31 | time.Sleep(1 * time.Second)
32 | }
33 |
34 | func TestCounter_Release(t *testing.T) {
35 | var counter = ratelimit.NewCounter(10)
36 |
37 | for i := 0; i < 10; i++ {
38 | counter.Ack()
39 | }
40 | for i := 0; i < 10; i++ {
41 | counter.Release()
42 | }
43 | t.Log(counter.Len())
44 | }
45 |
--------------------------------------------------------------------------------
/internal/utils/reader_utils.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package utils
4 |
5 | import "io"
6 |
7 | func CopyWithFilter(writer io.Writer, reader io.Reader, buf []byte, filter func(p []byte) []byte) (written int64, err error) {
8 | for {
9 | n, err := reader.Read(buf)
10 | if n > 0 {
11 | n2, err := writer.Write(filter(buf[:n]))
12 | written += int64(n2)
13 | if err != nil {
14 | return written, err
15 | }
16 | }
17 | if err != nil {
18 | if err == io.EOF {
19 | break
20 | }
21 | return written, err
22 | }
23 | }
24 | return written, nil
25 | }
26 |
--------------------------------------------------------------------------------
/internal/utils/readers/.gitignore:
--------------------------------------------------------------------------------
1 | readers_concurrent_file*
--------------------------------------------------------------------------------
/internal/utils/readers/handlers.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package readers
4 |
--------------------------------------------------------------------------------
/internal/utils/readers/reader_base.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package readers
4 |
5 | type BaseReader struct {
6 | }
7 |
--------------------------------------------------------------------------------
/internal/utils/readers/reader_bytes_counter.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package readers
4 |
5 | import "io"
6 |
7 | type BytesCounterReader struct {
8 | rawReader io.Reader
9 | count int64
10 | }
11 |
12 | func NewBytesCounterReader(rawReader io.Reader) *BytesCounterReader {
13 | return &BytesCounterReader{
14 | rawReader: rawReader,
15 | }
16 | }
17 |
18 | func (this *BytesCounterReader) Read(p []byte) (n int, err error) {
19 | n, err = this.rawReader.Read(p)
20 | this.count += int64(n)
21 | return
22 | }
23 |
24 | func (this *BytesCounterReader) TotalBytes() int64 {
25 | return this.count
26 | }
27 |
--------------------------------------------------------------------------------
/internal/utils/readers/reader_closer_filter.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package readers
4 |
5 | import "io"
6 |
7 | type FilterFunc = func(p []byte, readErr error) error
8 |
9 | type FilterReaderCloser struct {
10 | rawReader io.Reader
11 | filters []FilterFunc
12 | }
13 |
14 | func NewFilterReaderCloser(rawReader io.Reader) *FilterReaderCloser {
15 | return &FilterReaderCloser{
16 | rawReader: rawReader,
17 | }
18 | }
19 |
20 | func (this *FilterReaderCloser) Add(filter FilterFunc) {
21 | this.filters = append(this.filters, filter)
22 | }
23 |
24 | func (this *FilterReaderCloser) Read(p []byte) (n int, err error) {
25 | n, err = this.rawReader.Read(p)
26 | for _, filter := range this.filters {
27 | filterErr := filter(p[:n], err)
28 | if (err == nil || err != io.EOF) && filterErr != nil {
29 | err = filterErr
30 | return
31 | }
32 | }
33 | return
34 | }
35 |
36 | func (this *FilterReaderCloser) Close() error {
37 | closer, ok := this.rawReader.(io.Closer)
38 | if ok {
39 | return closer.Close()
40 | }
41 | return nil
42 | }
43 |
--------------------------------------------------------------------------------
/internal/utils/readers/reader_closer_filter_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package readers_test
4 |
5 | import (
6 | "bytes"
7 | "errors"
8 | "github.com/TeaOSLab/EdgeNode/internal/utils/readers"
9 | "testing"
10 | )
11 |
12 | func TestNewFilterReader(t *testing.T) {
13 | var reader = readers.NewFilterReaderCloser(bytes.NewBufferString("0123456789"))
14 | reader.Add(func(p []byte, err error) error {
15 | t.Log("filter1:", string(p), err)
16 | return nil
17 | })
18 | reader.Add(func(p []byte, err error) error {
19 | t.Log("filter2:", string(p), err)
20 | if string(p) == "345" {
21 | return errors.New("end")
22 | }
23 | return nil
24 | })
25 | reader.Add(func(p []byte, err error) error {
26 | t.Log("filter3:", string(p), err)
27 | return nil
28 | })
29 |
30 | var buf = make([]byte, 3)
31 | for {
32 | n, err := reader.Read(buf)
33 | if n > 0 {
34 | t.Log(string(buf[:n]))
35 | }
36 | if err != nil {
37 | t.Log(err)
38 | break
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/internal/utils/readers/reader_print.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package readers
4 |
5 | import (
6 | "io"
7 | "log"
8 | )
9 |
10 | type PrintReader struct {
11 | rawReader io.Reader
12 | tag string
13 | }
14 |
15 | func NewPrintReader(rawReader io.Reader, tag string) io.Reader {
16 | return &PrintReader{
17 | rawReader: rawReader,
18 | tag: tag,
19 | }
20 | }
21 |
22 | func (this *PrintReader) Read(p []byte) (n int, err error) {
23 | n, err = this.rawReader.Read(p)
24 | if n > 0 {
25 | log.Println("[" + this.tag + "]" + string(p[:n]))
26 | }
27 | return
28 | }
29 |
--------------------------------------------------------------------------------
/internal/utils/readers/reader_tee.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package readers
4 |
5 | import (
6 | "io"
7 | )
8 |
9 | type TeeReader struct {
10 | r io.Reader
11 | w io.Writer
12 |
13 | onFail func(err error)
14 | onEOF func()
15 | }
16 |
17 | func NewTeeReader(reader io.Reader, writer io.Writer) *TeeReader {
18 | return &TeeReader{
19 | r: reader,
20 | w: writer,
21 | }
22 | }
23 |
24 | func (this *TeeReader) Read(p []byte) (n int, err error) {
25 | n, err = this.r.Read(p)
26 | if n > 0 {
27 | _, wErr := this.w.Write(p[:n])
28 | if err == nil && wErr != nil {
29 | err = wErr
30 | }
31 | }
32 | if err != nil {
33 | if err == io.EOF {
34 | if this.onEOF != nil {
35 | this.onEOF()
36 | }
37 | } else {
38 | if this.onFail != nil {
39 | this.onFail(err)
40 | }
41 | }
42 | }
43 | return
44 | }
45 |
46 | func (this *TeeReader) OnFail(onFail func(err error)) {
47 | this.onFail = onFail
48 | }
49 |
50 | func (this *TeeReader) OnEOF(onEOF func()) {
51 | this.onEOF = onEOF
52 | }
53 |
--------------------------------------------------------------------------------
/internal/utils/rlimit_darwin.go:
--------------------------------------------------------------------------------
1 | //go:build darwin
2 | // +build darwin
3 |
4 | package utils
5 |
6 | import (
7 | "syscall"
8 | )
9 |
10 | // SetRLimit set resource limit
11 | func SetRLimit(limit uint64) error {
12 | var rLimit = &syscall.Rlimit{}
13 | err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, rLimit)
14 | if err != nil {
15 | return err
16 | }
17 |
18 | if rLimit.Cur < limit {
19 | rLimit.Cur = limit
20 | }
21 | if rLimit.Max < limit {
22 | rLimit.Max = limit
23 | }
24 | return syscall.Setrlimit(syscall.RLIMIT_NOFILE, rLimit)
25 | }
26 |
27 | // SetSuitableRLimit set best resource limit value
28 | func SetSuitableRLimit() error {
29 | return SetRLimit(4096 * 100) // 1M=100Files
30 | }
31 |
--------------------------------------------------------------------------------
/internal/utils/rlimit_linux.go:
--------------------------------------------------------------------------------
1 | //go:build linux
2 | // +build linux
3 |
4 | package utils
5 |
6 | import (
7 | "syscall"
8 | )
9 |
10 | // SetRLimit set resource limit
11 | func SetRLimit(limit uint64) error {
12 | var rLimit = &syscall.Rlimit{}
13 | err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, rLimit)
14 | if err != nil {
15 | return err
16 | }
17 |
18 | if rLimit.Cur < limit {
19 | rLimit.Cur = limit
20 | }
21 | if rLimit.Max < limit {
22 | rLimit.Max = limit
23 | }
24 | return syscall.Setrlimit(syscall.RLIMIT_NOFILE, rLimit)
25 | }
26 |
27 | // SetSuitableRLimit set best resource limit value
28 | func SetSuitableRLimit() error {
29 | return SetRLimit(4096 * 100) // 1M=100Files
30 | }
31 |
--------------------------------------------------------------------------------
/internal/utils/rlimit_others.go:
--------------------------------------------------------------------------------
1 | //go:build !linux && !darwin
2 | // +build !linux,!darwin
3 |
4 | package utils
5 |
6 | // set resource limit
7 | func SetRLimit(limit uint64) error {
8 | return nil
9 | }
10 |
11 | // set best resource limit value
12 | func SetSuitableRLimit() error {
13 | return nil
14 | }
15 |
--------------------------------------------------------------------------------
/internal/utils/service_others.go:
--------------------------------------------------------------------------------
1 | // +build !linux,!windows
2 |
3 | package utils
4 |
5 | // 安装服务
6 | func (this *ServiceManager) Install(exePath string, args []string) error {
7 | return nil
8 | }
9 |
10 | // 启动服务
11 | func (this *ServiceManager) Start() error {
12 | return nil
13 | }
14 |
15 | // 删除服务
16 | func (this *ServiceManager) Uninstall() error {
17 | return nil
18 | }
19 |
--------------------------------------------------------------------------------
/internal/utils/service_test.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
5 | "testing"
6 | )
7 |
8 | func TestServiceManager_Log(t *testing.T) {
9 | manager := NewServiceManager(teaconst.ProductName, teaconst.ProductName+" Server")
10 | manager.Log("Hello, World")
11 | manager.LogError("Hello, World")
12 | }
13 |
--------------------------------------------------------------------------------
/internal/utils/sync/.gitignore:
--------------------------------------------------------------------------------
1 | # experiments
2 | counter*
3 | values*
--------------------------------------------------------------------------------
/internal/utils/testutils/utils.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package testutils
4 |
5 | import (
6 | "fmt"
7 | "math/rand"
8 | "os"
9 | )
10 |
11 | // IsSingleTesting 判断当前测试环境是否为单个函数测试
12 | func IsSingleTesting() bool {
13 | for _, arg := range os.Args {
14 | if arg == "-test.run" {
15 | return true
16 | }
17 | }
18 | return false
19 | }
20 |
21 | // RandIP 生成一个随机IP用于测试
22 | func RandIP() string {
23 | return fmt.Sprintf("%d.%d.%d.%d", rand.Int()%255, rand.Int()%255, rand.Int()%255, rand.Int()%255)
24 | }
25 |
--------------------------------------------------------------------------------
/internal/utils/ticker.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/zero"
5 | "sync"
6 | "time"
7 | )
8 |
9 | // Ticker 类似于time.Ticker,但能够真正地停止
10 | type Ticker struct {
11 | raw *time.Ticker
12 | done chan zero.Zero
13 | once sync.Once
14 |
15 | C <-chan time.Time
16 | }
17 |
18 | // NewTicker 创建新Ticker
19 | func NewTicker(duration time.Duration) *Ticker {
20 | raw := time.NewTicker(duration)
21 | return &Ticker{
22 | raw: raw,
23 | C: raw.C,
24 | done: make(chan zero.Zero, 1),
25 | }
26 | }
27 |
28 | // Next 查找下一个Tick
29 | func (this *Ticker) Next() bool {
30 | select {
31 | case <-this.raw.C:
32 | return true
33 | case <-this.done:
34 | return false
35 | }
36 | }
37 |
38 | // Stop 停止
39 | func (this *Ticker) Stop() {
40 | this.once.Do(func() {
41 | this.raw.Stop()
42 | this.done <- zero.New()
43 | })
44 | }
45 |
--------------------------------------------------------------------------------
/internal/utils/ticker_utils.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/goman"
5 | "time"
6 | )
7 |
8 | // Every 定时运行某个函数
9 | func Every(duration time.Duration, f func(ticker *Ticker)) *Ticker {
10 | ticker := NewTicker(duration)
11 | goman.New(func() {
12 | for ticker.Next() {
13 | f(ticker)
14 | }
15 | })
16 |
17 | return ticker
18 | }
19 |
--------------------------------------------------------------------------------
/internal/utils/time.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | // GMTUnixTime 计算GMT时间戳
8 | func GMTUnixTime(timestamp int64) int64 {
9 | _, offset := time.Now().Zone()
10 | return timestamp - int64(offset)
11 | }
12 |
13 | // GMTTime 计算GMT时间
14 | func GMTTime(t time.Time) time.Time {
15 | _, offset := time.Now().Zone()
16 | return t.Add(-time.Duration(offset) * time.Second)
17 | }
18 |
--------------------------------------------------------------------------------
/internal/utils/time_test.go:
--------------------------------------------------------------------------------
1 | package utils_test
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/utils"
5 | "testing"
6 | "time"
7 | )
8 |
9 | func TestGMTUnixTime(t *testing.T) {
10 | t.Log(utils.GMTUnixTime(time.Now().Unix()))
11 | }
12 |
13 | func TestGMTTime(t *testing.T) {
14 | t.Log(utils.GMTTime(time.Now()))
15 | }
16 |
--------------------------------------------------------------------------------
/internal/utils/version.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/binary"
5 | "net"
6 | "strings"
7 | )
8 |
9 | // VersionToLong 计算版本代号
10 | func VersionToLong(version string) uint32 {
11 | var countDots = strings.Count(version, ".")
12 | if countDots == 2 {
13 | version += ".0"
14 | } else if countDots == 1 {
15 | version += ".0.0"
16 | } else if countDots == 0 {
17 | version += ".0.0.0"
18 | }
19 | var ip = net.ParseIP(version)
20 | if ip == nil || ip.To4() == nil {
21 | return 0
22 | }
23 | return binary.BigEndian.Uint32(ip.To4())
24 | }
25 |
--------------------------------------------------------------------------------
/internal/utils/version_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package utils_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils"
7 | "testing"
8 | )
9 |
10 | func TestVersionToLong(t *testing.T) {
11 | for _, v := range []string{
12 | "",
13 | "a",
14 | "1",
15 | "1.2",
16 | "1.2.1",
17 | "1.2.1.4",
18 | "1.2.3.4.5",
19 | } {
20 | t.Log(v, "=>", utils.VersionToLong(v))
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/internal/utils/workspace.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package utils
4 |
5 | import "regexp"
6 |
7 | var workspaceReg = regexp.MustCompile(`/Edge[A-Z]\w+/`)
8 |
9 | func RemoveWorkspace(path string) string {
10 | var indexes = workspaceReg.FindAllStringIndex(path, -1)
11 | if len(indexes) > 0 {
12 | return path[indexes[len(indexes)-1][0]:]
13 | }
14 | return path
15 | }
16 |
--------------------------------------------------------------------------------
/internal/utils/writers/writer_bytes_counter.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package writers
4 |
5 | import "io"
6 |
7 | type BytesCounterWriter struct {
8 | writer io.Writer
9 | count int64
10 | }
11 |
12 | func NewBytesCounterWriter(rawWriter io.Writer) *BytesCounterWriter {
13 | return &BytesCounterWriter{writer: rawWriter}
14 | }
15 |
16 | func (this *BytesCounterWriter) RawWriter() io.Writer {
17 | return this.writer
18 | }
19 |
20 | func (this *BytesCounterWriter) Write(p []byte) (n int, err error) {
21 | n, err = this.writer.Write(p)
22 | this.count += int64(n)
23 | return
24 | }
25 |
26 | func (this *BytesCounterWriter) Close() error {
27 | return nil
28 | }
29 |
30 | func (this *BytesCounterWriter) TotalBytes() int64 {
31 | return this.count
32 | }
33 |
--------------------------------------------------------------------------------
/internal/utils/writers/writer_print.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package writers
4 |
5 | import (
6 | "io"
7 | "log"
8 | )
9 |
10 | type PrintWriter struct {
11 | rawWriter io.Writer
12 | tag string
13 | }
14 |
15 | func NewPrintWriter(rawWriter io.Writer, tag string) io.Writer {
16 | return &PrintWriter{
17 | rawWriter: rawWriter,
18 | tag: tag,
19 | }
20 | }
21 |
22 | func (this *PrintWriter) Write(p []byte) (n int, err error) {
23 | n, err = this.rawWriter.Write(p)
24 | if n > 0 {
25 | log.Println("[" + this.tag + "]" + string(p[:n]))
26 | }
27 | return
28 | }
29 |
--------------------------------------------------------------------------------
/internal/utils/writers/writer_rate_limit_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package writers
4 |
5 | import (
6 | "sync"
7 | "testing"
8 | "time"
9 | )
10 |
11 | func TestSleep(t *testing.T) {
12 | var count = 2000
13 | var wg = sync.WaitGroup{}
14 | wg.Add(count)
15 | var before = time.Now()
16 | for i := 0; i < count; i++ {
17 | go func() {
18 | defer wg.Done()
19 | time.Sleep(1 * time.Second)
20 | }()
21 | }
22 | wg.Wait()
23 | t.Log(time.Since(before).Seconds()*1000, "ms")
24 | }
25 |
26 | func TestTimeout(t *testing.T) {
27 | var count = 2000
28 | var wg = sync.WaitGroup{}
29 | wg.Add(count)
30 | var before = time.Now()
31 | for i := 0; i < count; i++ {
32 | go func() {
33 | defer wg.Done()
34 |
35 | var timeout = time.NewTimer(1 * time.Second)
36 | <-timeout.C
37 | }()
38 | }
39 | wg.Wait()
40 | t.Log(time.Since(before).Seconds()*1000, "ms")
41 | }
42 |
--------------------------------------------------------------------------------
/internal/waf/README.md:
--------------------------------------------------------------------------------
1 | # WAF
2 | A basic WAF for TeaWeb.
3 |
4 | ## Config Constructions
5 | ~~~
6 | WAF
7 | Inbound
8 | Rule Groups
9 | Rule Sets
10 | Rules
11 | Checkpoint Param Value
12 | Outbound
13 | Rule Groups
14 | ...
15 | ~~~
16 |
17 | ## Apply WAF
18 | ~~~
19 | Request --> WAF --> Backends
20 | /
21 | Response <-- WAF <----
22 | ~~~
23 |
24 | ## Coding
25 | ~~~go
26 | waf := teawaf.NewWAF()
27 |
28 | // add rule groups here
29 |
30 | err := waf.Init()
31 | if err != nil {
32 | return
33 | }
34 | waf.Start()
35 |
36 | // match http request
37 | // (req *http.Request, responseWriter http.ResponseWriter)
38 | goNext, ruleSet, _ := waf.MatchRequest(req, responseWriter)
39 | if ruleSet != nil {
40 | log.Println("meet rule set:", ruleSet.Name, "action:", ruleSet.Action)
41 | }
42 | if !goNext {
43 | return
44 | }
45 |
46 | // stop the waf
47 | // waf.Stop()
48 | ~~~
--------------------------------------------------------------------------------
/internal/waf/action_allow.go:
--------------------------------------------------------------------------------
1 | package waf
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "net/http"
6 | )
7 |
8 | type AllowScope = string
9 |
10 | const (
11 | AllowScopeGroup AllowScope = "group"
12 | AllowScopeServer AllowScope = "server"
13 | AllowScopeGlobal AllowScope = "global"
14 | )
15 |
16 | type AllowAction struct {
17 | BaseAction
18 |
19 | Scope AllowScope `yaml:"scope" json:"scope"`
20 | }
21 |
22 | func (this *AllowAction) Init(waf *WAF) error {
23 | return nil
24 | }
25 |
26 | func (this *AllowAction) Code() string {
27 | return ActionAllow
28 | }
29 |
30 | func (this *AllowAction) IsAttack() bool {
31 | return false
32 | }
33 |
34 | func (this *AllowAction) WillChange() bool {
35 | return true
36 | }
37 |
38 | func (this *AllowAction) Perform(waf *WAF, group *RuleGroup, set *RuleSet, request requests.Request, writer http.ResponseWriter) PerformResult {
39 | // do nothing
40 | return PerformResult{
41 | ContinueRequest: true,
42 | GoNextGroup: this.Scope == AllowScopeGroup,
43 | IsAllowed: true,
44 | AllowScope: this.Scope,
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/internal/waf/action_base.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package waf
4 |
5 | type BaseAction struct {
6 | currentActionId int64
7 | }
8 |
9 | // ActionId 读取ActionId
10 | func (this *BaseAction) ActionId() int64 {
11 | return this.currentActionId
12 | }
13 |
14 | // SetActionId 设置Id
15 | func (this *BaseAction) SetActionId(actionId int64) {
16 | this.currentActionId = actionId
17 | }
18 |
--------------------------------------------------------------------------------
/internal/waf/action_category.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package waf
4 |
5 | import "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
6 |
7 | type ActionCategory = string
8 |
9 | const (
10 | ActionCategoryAllow ActionCategory = firewallconfigs.HTTPFirewallActionCategoryAllow
11 | ActionCategoryBlock ActionCategory = firewallconfigs.HTTPFirewallActionCategoryBlock
12 | ActionCategoryVerify ActionCategory = firewallconfigs.HTTPFirewallActionCategoryVerify
13 | )
14 |
--------------------------------------------------------------------------------
/internal/waf/action_config.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package waf
4 |
5 | import "github.com/iwind/TeaGo/maps"
6 |
7 | type ActionConfig struct {
8 | Code string `yaml:"code" json:"code"`
9 | Options maps.Map `yaml:"options" json:"options"`
10 | }
11 |
--------------------------------------------------------------------------------
/internal/waf/action_definition.go:
--------------------------------------------------------------------------------
1 | package waf
2 |
3 | import "reflect"
4 |
5 | // ActionDefinition action definition
6 | type ActionDefinition struct {
7 | Name string
8 | Code ActionString
9 | Description string
10 | Category string // category: block, verify, allow
11 | Instance ActionInterface
12 | Type reflect.Type
13 | }
14 |
--------------------------------------------------------------------------------
/internal/waf/action_instance.go:
--------------------------------------------------------------------------------
1 | package waf
2 |
3 | type Action struct {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/internal/waf/action_interface.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package waf
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
7 | "net/http"
8 | )
9 |
10 | type ActionInterface interface {
11 | // Init 初始化
12 | Init(waf *WAF) error
13 |
14 | // ActionId 读取ActionId
15 | ActionId() int64
16 |
17 | // SetActionId 设置ID
18 | SetActionId(id int64)
19 |
20 | // Code 代号
21 | Code() string
22 |
23 | // IsAttack 是否为拦截攻击动作
24 | IsAttack() bool
25 |
26 | // WillChange determine if the action will change the request
27 | WillChange() bool
28 |
29 | // Perform the action
30 | Perform(waf *WAF, group *RuleGroup, set *RuleSet, request requests.Request, writer http.ResponseWriter) PerformResult
31 | }
32 |
--------------------------------------------------------------------------------
/internal/waf/action_log.go:
--------------------------------------------------------------------------------
1 | package waf
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "net/http"
6 | )
7 |
8 | type LogAction struct {
9 | BaseAction
10 | }
11 |
12 | func (this *LogAction) Init(waf *WAF) error {
13 | return nil
14 | }
15 |
16 | func (this *LogAction) Code() string {
17 | return ActionLog
18 | }
19 |
20 | func (this *LogAction) IsAttack() bool {
21 | return false
22 | }
23 |
24 | func (this *LogAction) WillChange() bool {
25 | return false
26 | }
27 |
28 | func (this *LogAction) Perform(waf *WAF, group *RuleGroup, set *RuleSet, request requests.Request, writer http.ResponseWriter) PerformResult {
29 | return PerformResult{
30 | ContinueRequest: true,
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/internal/waf/action_tag.go:
--------------------------------------------------------------------------------
1 | package waf
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "net/http"
6 | )
7 |
8 | type TagAction struct {
9 | BaseAction
10 |
11 | Tags []string `yaml:"tags" json:"tags"`
12 | }
13 |
14 | func (this *TagAction) Init(waf *WAF) error {
15 | return nil
16 | }
17 |
18 | func (this *TagAction) Code() string {
19 | return ActionTag
20 | }
21 |
22 | func (this *TagAction) IsAttack() bool {
23 | return false
24 | }
25 |
26 | func (this *TagAction) WillChange() bool {
27 | return false
28 | }
29 |
30 | func (this *TagAction) Perform(waf *WAF, group *RuleGroup, set *RuleSet, request requests.Request, writer http.ResponseWriter) PerformResult {
31 | return PerformResult{
32 | ContinueRequest: true,
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/internal/waf/allow_cookie_info_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package waf_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
7 | "github.com/TeaOSLab/EdgeNode/internal/waf"
8 | "github.com/iwind/TeaGo/assert"
9 | "github.com/iwind/TeaGo/types"
10 | "testing"
11 | )
12 |
13 | func TestAllowCookieInfo_Encode(t *testing.T) {
14 | var a = assert.NewAssertion(t)
15 |
16 | var info = &waf.AllowCookieInfo{
17 | SetId: 123,
18 | ExpiresAt: fasttime.Now().Unix(),
19 | }
20 | data, err := info.Encode()
21 | if err != nil {
22 | t.Fatal(err)
23 | }
24 | t.Log("encrypted: ["+types.String(len(data))+"]", data)
25 |
26 | var info2 = &waf.AllowCookieInfo{}
27 | err = info2.Decode(data)
28 | if err != nil {
29 | t.Fatal(err)
30 | }
31 |
32 | t.Logf("%+v", info2)
33 | a.IsTrue(info.SetId == info2.SetId)
34 | a.IsTrue(info.ExpiresAt == info2.ExpiresAt)
35 | }
36 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/checkpoint_definition.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | // CheckpointDefinition check point definition
4 | type CheckpointDefinition struct {
5 | Name string
6 | Description string
7 | Prefix string
8 | HasParams bool // has sub params
9 | Instance CheckpointInterface
10 | Priority int
11 | }
12 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/option.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | type OptionInterface interface {
4 | Type() string
5 | }
6 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/option_field.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | // attach option
4 | type FieldOption struct {
5 | Name string
6 | Code string
7 | Value string // default value
8 | IsRequired bool
9 | Size int
10 | Comment string
11 | Placeholder string
12 | RightLabel string
13 | MaxLength int
14 | Validate func(value string) (ok bool, message string)
15 | }
16 |
17 | func NewFieldOption(name string, code string) *FieldOption {
18 | return &FieldOption{
19 | Name: name,
20 | Code: code,
21 | }
22 | }
23 |
24 | func (this *FieldOption) Type() string {
25 | return "field"
26 | }
27 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/option_options.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import "github.com/iwind/TeaGo/maps"
4 |
5 | type OptionsOption struct {
6 | Name string
7 | Code string
8 | Value string // default value
9 | IsRequired bool
10 | Size int
11 | Comment string
12 | RightLabel string
13 | Validate func(value string) (ok bool, message string)
14 | Options []maps.Map
15 | }
16 |
17 | func NewOptionsOption(name string, code string) *OptionsOption {
18 | return &OptionsOption{
19 | Name: name,
20 | Code: code,
21 | }
22 | }
23 |
24 | func (this *OptionsOption) Type() string {
25 | return "options"
26 | }
27 |
28 | func (this *OptionsOption) SetOptions(options []maps.Map) {
29 | this.Options = options
30 | }
31 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/param_option.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | type KeyValue struct {
4 | Name string `json:"name"`
5 | Value string `json:"value"`
6 | }
7 |
8 | type ParamOptions struct {
9 | Options []*KeyValue `json:"options"`
10 | }
11 |
12 | func NewParamOptions() *ParamOptions {
13 | return &ParamOptions{}
14 | }
15 |
16 | func (this *ParamOptions) AddParam(name string, value string) {
17 | this.Options = append(this.Options, &KeyValue{
18 | Name: name,
19 | Value: value,
20 | })
21 | }
22 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_arg.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestArgCheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestArgCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | return req.WAFRaw().URL.Query().Get(param), hasRequestBody, nil, nil
15 | }
16 |
17 | func (this *RequestArgCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
18 | if this.IsRequest() {
19 | return this.RequestValue(req, param, options, ruleId)
20 | }
21 | return
22 | }
23 |
24 | func (this *RequestArgCheckpoint) CacheLife() utils.CacheLife {
25 | return utils.CacheMiddleLife
26 | }
27 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_arg_test.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "net/http"
6 | "testing"
7 | )
8 |
9 | func TestArgParam_RequestValue(t *testing.T) {
10 | rawReq, err := http.NewRequest(http.MethodGet, "http://teaos.cn/?name=lu", nil)
11 | if err != nil {
12 | t.Fatal(err)
13 | }
14 |
15 | req := requests.NewTestRequest(rawReq)
16 |
17 | checkpoint := new(RequestArgCheckpoint)
18 | t.Log(checkpoint.RequestValue(req, "name", nil, 1))
19 | t.Log(checkpoint.ResponseValue(req, nil, "name", nil, 1))
20 | t.Log(checkpoint.RequestValue(req, "name2", nil, 1))
21 | }
22 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_args.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestArgsCheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestArgsCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | value = req.WAFRaw().URL.RawQuery
15 | return
16 | }
17 |
18 | func (this *RequestArgsCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
19 | if this.IsRequest() {
20 | return this.RequestValue(req, param, options, ruleId)
21 | }
22 | return
23 | }
24 |
25 | func (this *RequestArgsCheckpoint) CacheLife() utils.CacheLife {
26 | return utils.CacheMiddleLife
27 | }
28 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_cname.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestCNAMECheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestCNAMECheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | value = req.Format("${cname}")
15 | return
16 | }
17 |
18 | func (this *RequestCNAMECheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
19 | if this.IsRequest() {
20 | return this.RequestValue(req, param, options, ruleId)
21 | }
22 | return
23 | }
24 |
25 | func (this *RequestCNAMECheckpoint) CacheLife() utils.CacheLife {
26 | return utils.CacheLongLife
27 | }
28 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_content_type.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestContentTypeCheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestContentTypeCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | value = req.WAFRaw().Header.Get("Content-Type")
15 | return
16 | }
17 |
18 | func (this *RequestContentTypeCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
19 | if this.IsRequest() {
20 | return this.RequestValue(req, param, options, ruleId)
21 | }
22 | return
23 | }
24 |
25 | func (this *RequestContentTypeCheckpoint) CacheLife() utils.CacheLife {
26 | return utils.CacheLongLife
27 | }
28 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_cookie.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestCookieCheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestCookieCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | cookie, err := req.WAFRaw().Cookie(param)
15 | if err != nil {
16 | value = ""
17 | return
18 | }
19 |
20 | value = cookie.Value
21 | return
22 | }
23 |
24 | func (this *RequestCookieCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
25 | if this.IsRequest() {
26 | return this.RequestValue(req, param, options, ruleId)
27 | }
28 | return
29 | }
30 |
31 | func (this *RequestCookieCheckpoint) CacheLife() utils.CacheLife {
32 | return utils.CacheMiddleLife
33 | }
34 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_form_arg_test.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "bytes"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
6 | "io"
7 | "net/http"
8 | "net/url"
9 | "testing"
10 | )
11 |
12 | func TestRequestFormArgCheckpoint_RequestValue(t *testing.T) {
13 | rawReq, err := http.NewRequest(http.MethodPost, "http://teaos.cn", bytes.NewBuffer([]byte("name=lu&age=20&encoded="+url.QueryEscape("ENCODED STRING"))))
14 | if err != nil {
15 | t.Fatal(err)
16 | }
17 |
18 | req := requests.NewTestRequest(rawReq)
19 | req.WAFRaw().Header.Set("Content-Type", "application/x-www-form-urlencoded")
20 |
21 | checkpoint := new(RequestFormArgCheckpoint)
22 | t.Log(checkpoint.RequestValue(req, "name", nil, 1))
23 | t.Log(checkpoint.RequestValue(req, "age", nil, 1))
24 | t.Log(checkpoint.RequestValue(req, "Hello", nil, 1))
25 | t.Log(checkpoint.RequestValue(req, "encoded", nil, 1))
26 |
27 | body, err := io.ReadAll(req.WAFRaw().Body)
28 | if err != nil {
29 | t.Fatal(err)
30 | }
31 | t.Log(string(body))
32 | }
33 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_header.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | "strings"
8 | )
9 |
10 | type RequestHeaderCheckpoint struct {
11 | Checkpoint
12 | }
13 |
14 | func (this *RequestHeaderCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
15 | v, found := req.WAFRaw().Header[param]
16 | if !found {
17 | value = ""
18 | return
19 | }
20 | value = strings.Join(v, ";")
21 | return
22 | }
23 |
24 | func (this *RequestHeaderCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
25 | if this.IsRequest() {
26 | return this.RequestValue(req, param, options, ruleId)
27 | }
28 | return
29 | }
30 |
31 | func (this *RequestHeaderCheckpoint) CacheLife() utils.CacheLife {
32 | return utils.CacheMiddleLife
33 | }
34 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_header_names_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package checkpoints_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/waf/checkpoints"
7 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
8 | "net/http"
9 | "testing"
10 | )
11 |
12 | func TestRequestHeaderNamesCheckpoint_RequestValue(t *testing.T) {
13 | var checkpoint = &checkpoints.RequestHeaderNamesCheckpoint{}
14 | rawReq, err := http.NewRequest(http.MethodGet, "https://example.com", nil)
15 | if err != nil {
16 | t.Fatal(err)
17 | }
18 | rawReq.Header.Set("Accept", "text/html")
19 | rawReq.Header.Set("User-Agent", "Chrome")
20 | rawReq.Header.Set("Accept-Encoding", "br, gzip")
21 | var req = requests.NewTestRequest(rawReq)
22 | t.Log(checkpoint.RequestValue(req, "", nil, 0))
23 | }
24 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_headers_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package checkpoints
4 |
5 | import (
6 | "net/http"
7 | "runtime"
8 | "sort"
9 | "strings"
10 | "testing"
11 | )
12 |
13 | func BenchmarkRequestHeadersCheckpoint_RequestValue(b *testing.B) {
14 | runtime.GOMAXPROCS(1)
15 |
16 | var header = http.Header{
17 | "Content-Type": []string{"keep-alive"},
18 | "User-Agent": []string{"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"},
19 | "Accept-Encoding": []string{"gzip, deflate, br"},
20 | "Referer": []string{"https://goedge.cn/"},
21 | }
22 |
23 | for i := 0; i < b.N; i++ {
24 | var headers = []string{}
25 | for k, v := range header {
26 | for _, subV := range v {
27 | headers = append(headers, k+": "+subV)
28 | }
29 | }
30 | sort.Strings(headers)
31 | _ = strings.Join(headers, "\n")
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_host.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestHostCheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestHostCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | value = req.WAFRaw().Host
15 | return
16 | }
17 |
18 | func (this *RequestHostCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
19 | if this.IsRequest() {
20 | return this.RequestValue(req, param, options, ruleId)
21 | }
22 | return
23 | }
24 |
25 | func (this *RequestHostCheckpoint) CacheLife() utils.CacheLife {
26 | return utils.CacheLongLife
27 | }
28 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_host_test.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "net/http"
6 | "testing"
7 | )
8 |
9 | func TestRequestHostCheckpoint_RequestValue(t *testing.T) {
10 | rawReq, err := http.NewRequest(http.MethodGet, "https://teaos.cn/?name=lu", nil)
11 | if err != nil {
12 | t.Fatal(err)
13 | }
14 |
15 | req := requests.NewTestRequest(rawReq)
16 | req.WAFRaw().Header.Set("Host", "cloud.teaos.cn")
17 |
18 | checkpoint := new(RequestHostCheckpoint)
19 | t.Log(checkpoint.RequestValue(req, "", nil, 1))
20 | }
21 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_is_cname.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestIsCNAMECheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestIsCNAMECheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | if req.Format("${cname}") == req.Format("${host}") {
15 | value = 1
16 | } else {
17 | value = 0
18 | }
19 | return
20 | }
21 |
22 | func (this *RequestIsCNAMECheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
23 | if this.IsRequest() {
24 | return this.RequestValue(req, param, options, ruleId)
25 | }
26 | return
27 | }
28 |
29 | func (this *RequestIsCNAMECheckpoint) CacheLife() utils.CacheLife {
30 | return utils.CacheLongLife
31 | }
32 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_isp_name.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package checkpoints
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
7 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
8 | "github.com/iwind/TeaGo/maps"
9 | )
10 |
11 | type RequestISPNameCheckpoint struct {
12 | Checkpoint
13 | }
14 |
15 | func (this *RequestISPNameCheckpoint) IsComposed() bool {
16 | return false
17 | }
18 |
19 | func (this *RequestISPNameCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
20 | value = req.Format("${isp.name}")
21 | return
22 | }
23 |
24 | func (this *RequestISPNameCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
25 | return this.RequestValue(req, param, options, ruleId)
26 | }
27 |
28 | func (this *RequestISPNameCheckpoint) CacheLife() utils.CacheLife {
29 | return utils.CacheLongLife
30 | }
31 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_length.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestLengthCheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestLengthCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | value = req.WAFRaw().ContentLength
15 | return
16 | }
17 |
18 | func (this *RequestLengthCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
19 | if this.IsRequest() {
20 | return this.RequestValue(req, param, options, ruleId)
21 | }
22 | return
23 | }
24 |
25 | func (this *RequestLengthCheckpoint) CacheLife() utils.CacheLife {
26 | return utils.CacheShortLife
27 | }
28 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_method.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestMethodCheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestMethodCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | value = req.WAFRaw().Method
15 | return
16 | }
17 |
18 | func (this *RequestMethodCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
19 | if this.IsRequest() {
20 | return this.RequestValue(req, param, options, ruleId)
21 | }
22 | return
23 | }
24 |
25 | func (this *RequestMethodCheckpoint) CacheLife() utils.CacheLife {
26 | return utils.CacheLongLife
27 | }
28 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_path.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestPathCheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestPathCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | return req.WAFRaw().URL.Path, false, nil, nil
15 | }
16 |
17 | func (this *RequestPathCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
18 | if this.IsRequest() {
19 | return this.RequestValue(req, param, options, ruleId)
20 | }
21 | return
22 | }
23 |
24 | func (this *RequestPathCheckpoint) CacheLife() utils.CacheLife {
25 | return utils.CacheMiddleLife
26 | }
27 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_path_test.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "net/http"
6 | "testing"
7 | )
8 |
9 | func TestRequestPathCheckpoint_RequestValue(t *testing.T) {
10 | rawReq, err := http.NewRequest(http.MethodGet, "http://teaos.cn/index?name=lu", nil)
11 | if err != nil {
12 | t.Fatal(err)
13 | }
14 |
15 | req := requests.NewTestRequest(rawReq)
16 | checkpoint := new(RequestPathCheckpoint)
17 | t.Log(checkpoint.RequestValue(req, "", nil, 1))
18 | }
19 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_proto.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestProtoCheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestProtoCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | value = req.WAFRaw().Proto
15 | return
16 | }
17 |
18 | func (this *RequestProtoCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
19 | if this.IsRequest() {
20 | return this.RequestValue(req, param, options, ruleId)
21 | }
22 | return
23 | }
24 |
25 | func (this *RequestProtoCheckpoint) CacheLife() utils.CacheLife {
26 | return utils.CacheLongLife
27 | }
28 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_referer.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestRefererCheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestRefererCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | value = req.WAFRaw().Referer()
15 | return
16 | }
17 |
18 | func (this *RequestRefererCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
19 | if this.IsRequest() {
20 | return this.RequestValue(req, param, options, ruleId)
21 | }
22 | return
23 | }
24 |
25 | func (this *RequestRefererCheckpoint) CacheLife() utils.CacheLife {
26 | return utils.CacheMiddleLife
27 | }
28 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_referer_origin_test.go:
--------------------------------------------------------------------------------
1 | package checkpoints_test
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/checkpoints"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
6 | "net/http"
7 | "testing"
8 | )
9 |
10 | func TestRequestRefererOriginCheckpoint_RequestValue(t *testing.T) {
11 | rawReq, err := http.NewRequest(http.MethodGet, "https://example.com", nil)
12 | if err != nil {
13 | t.Fatal(err)
14 | }
15 | var req = requests.NewTestRequest(rawReq)
16 |
17 | var checkpoint = &checkpoints.RequestRefererOriginCheckpoint{}
18 |
19 | {
20 | t.Log(checkpoint.RequestValue(req, "", nil, 0))
21 | }
22 |
23 | {
24 | rawReq.Header.Set("Referer", "https://example.com/hello.yaml")
25 | t.Log(checkpoint.RequestValue(req, "", nil, 0))
26 | }
27 |
28 | {
29 | rawReq.Header.Set("Origin", "https://example.com/world.yaml")
30 | t.Log(checkpoint.RequestValue(req, "", nil, 0))
31 | }
32 |
33 | {
34 | rawReq.Header.Del("Referer")
35 | rawReq.Header.Set("Origin", "https://example.com/world.yaml")
36 | t.Log(checkpoint.RequestValue(req, "", nil, 0))
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_remote_addr.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestRemoteAddrCheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestRemoteAddrCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | value = req.WAFRemoteIP()
15 | return
16 | }
17 |
18 | func (this *RequestRemoteAddrCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
19 | if this.IsRequest() {
20 | return this.RequestValue(req, param, options, ruleId)
21 | }
22 | return
23 | }
24 |
25 | func (this *RequestRemoteAddrCheckpoint) CacheLife() utils.CacheLife {
26 | return utils.CacheShortLife
27 | }
28 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_remote_user.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestRemoteUserCheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestRemoteUserCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | username, _, ok := req.WAFRaw().BasicAuth()
15 | if !ok {
16 | value = ""
17 | return
18 | }
19 | value = username
20 | return
21 | }
22 |
23 | func (this *RequestRemoteUserCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
24 | if this.IsRequest() {
25 | return this.RequestValue(req, param, options, ruleId)
26 | }
27 | return
28 | }
29 |
30 | func (this *RequestRemoteUserCheckpoint) CacheLife() utils.CacheLife {
31 | return utils.CacheMiddleLife
32 | }
33 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_scheme.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestSchemeCheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestSchemeCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | value = req.Format("${scheme}")
15 | return
16 | }
17 |
18 | func (this *RequestSchemeCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
19 | if this.IsRequest() {
20 | return this.RequestValue(req, param, options, ruleId)
21 | }
22 | return
23 | }
24 |
25 | func (this *RequestSchemeCheckpoint) CacheLife() utils.CacheLife {
26 | return utils.CacheLongLife
27 | }
28 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_scheme_test.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "net/http"
6 | "testing"
7 | )
8 |
9 | func TestRequestSchemeCheckpoint_RequestValue(t *testing.T) {
10 | rawReq, err := http.NewRequest(http.MethodGet, "https://teaos.cn/?name=lu", nil)
11 | if err != nil {
12 | t.Fatal(err)
13 | }
14 |
15 | req := requests.NewTestRequest(rawReq)
16 | checkpoint := new(RequestSchemeCheckpoint)
17 | t.Log(checkpoint.RequestValue(req, "", nil, 1))
18 | }
19 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_uri.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestURICheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestURICheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | if len(req.WAFRaw().RequestURI) > 0 {
15 | value = req.WAFRaw().RequestURI
16 | } else if req.WAFRaw().URL != nil {
17 | value = req.WAFRaw().URL.RequestURI()
18 | }
19 | return
20 | }
21 |
22 | func (this *RequestURICheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
23 | if this.IsRequest() {
24 | return this.RequestValue(req, param, options, ruleId)
25 | }
26 | return
27 | }
28 |
29 | func (this *RequestURICheckpoint) CacheLife() utils.CacheLife {
30 | return utils.CacheShortLife
31 | }
32 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_url.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestURLCheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestURLCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | return req.Format("${requestURL}"), hasRequestBody, nil, nil
15 | }
16 |
17 | func (this *RequestURLCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
18 | if this.IsRequest() {
19 | return this.RequestValue(req, param, options, ruleId)
20 | }
21 | return
22 | }
23 |
24 | func (this *RequestURLCheckpoint) CacheLife() utils.CacheLife {
25 | return utils.CacheShortLife
26 | }
27 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/request_user_agent.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | type RequestUserAgentCheckpoint struct {
10 | Checkpoint
11 | }
12 |
13 | func (this *RequestUserAgentCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
14 | value = req.WAFRaw().UserAgent()
15 | return
16 | }
17 |
18 | func (this *RequestUserAgentCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
19 | if this.IsRequest() {
20 | return this.RequestValue(req, param, options, ruleId)
21 | }
22 | return
23 | }
24 |
25 | func (this *RequestUserAgentCheckpoint) CacheLife() utils.CacheLife {
26 | return utils.CacheShortLife
27 | }
28 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/response_body_test.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "bytes"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
6 | "io"
7 | "net/http"
8 | "testing"
9 | )
10 |
11 | func TestResponseBodyCheckpoint_ResponseValue(t *testing.T) {
12 | resp := requests.NewResponse(new(http.Response))
13 | resp.StatusCode = 200
14 | resp.Header = http.Header{}
15 | resp.Header.Set("Hello", "World")
16 | resp.Body = io.NopCloser(bytes.NewBuffer([]byte("Hello, World")))
17 |
18 | checkpoint := new(ResponseBodyCheckpoint)
19 | t.Log(checkpoint.ResponseValue(nil, resp, "", nil, 1))
20 | t.Log(checkpoint.ResponseValue(nil, resp, "", nil, 1))
21 | t.Log(checkpoint.ResponseValue(nil, resp, "", nil, 1))
22 | t.Log(checkpoint.ResponseValue(nil, resp, "", nil, 1))
23 |
24 | data, err := io.ReadAll(resp.Body)
25 | if err != nil {
26 | t.Fatal(err)
27 | }
28 | t.Log("after read:", string(data))
29 | }
30 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/response_bytes_sent.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | // ResponseBytesSentCheckpoint ${bytesSent}
10 | type ResponseBytesSentCheckpoint struct {
11 | Checkpoint
12 | }
13 |
14 | func (this *ResponseBytesSentCheckpoint) IsRequest() bool {
15 | return false
16 | }
17 |
18 | func (this *ResponseBytesSentCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
19 | value = 0
20 | return
21 | }
22 |
23 | func (this *ResponseBytesSentCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
24 | value = 0
25 | if resp != nil {
26 | value = resp.ContentLength
27 | }
28 | return
29 | }
30 |
31 | func (this *ResponseBytesSentCheckpoint) CacheLife() utils.CacheLife {
32 | return utils.CacheShortLife
33 | }
34 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/response_header_test.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "net/http"
6 | "testing"
7 | )
8 |
9 | func TestResponseHeaderCheckpoint_ResponseValue(t *testing.T) {
10 | resp := requests.NewResponse(new(http.Response))
11 | resp.StatusCode = 200
12 | resp.Header = http.Header{}
13 | resp.Header.Set("Hello", "World")
14 |
15 | checkpoint := new(ResponseHeaderCheckpoint)
16 | t.Log(checkpoint.ResponseValue(nil, resp, "Hello", nil, 1))
17 | }
18 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/response_status.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | // ResponseStatusCheckpoint ${bytesSent}
10 | type ResponseStatusCheckpoint struct {
11 | Checkpoint
12 | }
13 |
14 | func (this *ResponseStatusCheckpoint) IsRequest() bool {
15 | return false
16 | }
17 |
18 | func (this *ResponseStatusCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
19 | value = 0
20 | return
21 | }
22 |
23 | func (this *ResponseStatusCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
24 | if resp != nil {
25 | value = resp.StatusCode
26 | }
27 | return
28 | }
29 |
30 | func (this *ResponseStatusCheckpoint) CacheLife() utils.CacheLife {
31 | return utils.CacheLongLife
32 | }
33 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/response_status_test.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "net/http"
6 | "testing"
7 | )
8 |
9 | func TestResponseStatusCheckpoint_ResponseValue(t *testing.T) {
10 | resp := requests.NewResponse(new(http.Response))
11 | resp.StatusCode = 200
12 |
13 | checkpoint := new(ResponseStatusCheckpoint)
14 | t.Log(checkpoint.ResponseValue(nil, resp, "", nil, 1))
15 | }
16 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/sample_request.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | // SampleRequestCheckpoint just a sample checkpoint, copy and change it for your new checkpoint
10 | type SampleRequestCheckpoint struct {
11 | Checkpoint
12 | }
13 |
14 | func (this *SampleRequestCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
15 | return
16 | }
17 |
18 | func (this *SampleRequestCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
19 | if this.IsRequest() {
20 | return this.RequestValue(req, param, options, ruleId)
21 | }
22 | return
23 | }
24 |
25 | func (this *SampleRequestCheckpoint) CacheLife() utils.CacheLife {
26 | return utils.CacheMiddleLife
27 | }
28 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/sample_response.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | "github.com/iwind/TeaGo/maps"
7 | )
8 |
9 | // SampleResponseCheckpoint just a sample checkpoint, copy and change it for your new checkpoint
10 | type SampleResponseCheckpoint struct {
11 | Checkpoint
12 | }
13 |
14 | func (this *SampleResponseCheckpoint) IsRequest() bool {
15 | return false
16 | }
17 |
18 | func (this *SampleResponseCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value any, sysErr error, userErr error) {
19 | return
20 | }
21 |
22 | func (this *SampleResponseCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value any, hasRequestBody bool, sysErr error, userErr error) {
23 | return
24 | }
25 |
26 | func (this *SampleResponseCheckpoint) CacheLife() utils.CacheLife {
27 | return utils.CacheMiddleLife
28 | }
29 |
--------------------------------------------------------------------------------
/internal/waf/checkpoints/utils_test.go:
--------------------------------------------------------------------------------
1 | package checkpoints
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | "testing"
7 | )
8 |
9 | func TestFindCheckpointDefinition_Markdown(t *testing.T) {
10 | result := []string{}
11 | for _, def := range AllCheckpoints {
12 | row := "## " + def.Name + "\n* 前缀:`${" + def.Prefix + "}`\n* 描述:" + def.Description
13 | if def.HasParams {
14 | row += "\n* 是否有子参数:YES"
15 |
16 | paramOptions := def.Instance.ParamOptions()
17 | if paramOptions != nil && len(paramOptions.Options) > 0 {
18 | row += "\n* 可选子参数"
19 | for _, option := range paramOptions.Options {
20 | row += "\n * `" + option.Name + "`:值为 `" + option.Value + "`"
21 | }
22 | }
23 | } else {
24 | row += "\n* 是否有子参数:NO"
25 | }
26 | row += "\n"
27 | result = append(result, row)
28 | }
29 |
30 | fmt.Print(strings.Join(result, "\n") + "\n")
31 | }
32 |
--------------------------------------------------------------------------------
/internal/waf/injectionutils/libinjection/README.md:
--------------------------------------------------------------------------------
1 | copy from https://github.com/libinjection/libinjection
--------------------------------------------------------------------------------
/internal/waf/injectionutils/libinjection/src/libinjection_xss.h:
--------------------------------------------------------------------------------
1 | #ifndef LIBINJECTION_XSS
2 | #define LIBINJECTION_XSS
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | /**
9 | * HEY THIS ISN'T DONE
10 | */
11 |
12 | /* pull in size_t */
13 |
14 | #include
15 |
16 | int libinjection_is_xss(const char* s, size_t len, int flags, int strictMode);
17 |
18 | #ifdef __cplusplus
19 | }
20 | #endif
21 | #endif
22 |
--------------------------------------------------------------------------------
/internal/waf/injectionutils/libinjection_sqli.c:
--------------------------------------------------------------------------------
1 | #define LIBINJECTION_VERSION "3.9.1"
2 |
3 | #include "libinjection/src/libinjection_sqli.c"
--------------------------------------------------------------------------------
/internal/waf/injectionutils/libinjection_xss.c:
--------------------------------------------------------------------------------
1 | #define LIBINJECTION_VERSION "3.9.1"
2 |
3 | #include "libinjection/src/libinjection_xss.c"
4 | #include "libinjection/src/libinjection_html5.c"
5 |
6 | #define GOEDGE_VERSION "25" // last version is for GoEdge change
--------------------------------------------------------------------------------
/internal/waf/ip_lists_deleted.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package waf
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/zero"
7 | "sync"
8 | )
9 |
10 | var deletedIPListIdMap = map[int64]zero.Zero{} // listId => Zero
11 | var deletedIPListLocker = sync.RWMutex{}
12 |
13 | // AddDeletedIPList add deleted ip list
14 | func AddDeletedIPList(ipListId int64) {
15 | if ipListId <= 0 {
16 | return
17 | }
18 |
19 | deletedIPListLocker.Lock()
20 | deletedIPListIdMap[ipListId] = zero.Zero{}
21 | deletedIPListLocker.Unlock()
22 | }
23 |
24 | // ExistDeletedIPList check if ip list has been deleted
25 | func ExistDeletedIPList(ipListId int64) bool {
26 | deletedIPListLocker.RLock()
27 | _, ok := deletedIPListIdMap[ipListId]
28 | deletedIPListLocker.RUnlock()
29 | return ok
30 | }
31 |
--------------------------------------------------------------------------------
/internal/waf/param_filter.go:
--------------------------------------------------------------------------------
1 | package waf
2 |
3 | import "github.com/iwind/TeaGo/maps"
4 |
5 | type ParamFilter struct {
6 | Code string `yaml:"code" json:"code"`
7 | Options maps.Map `yaml:"options" json:"options"`
8 | }
9 |
--------------------------------------------------------------------------------
/internal/waf/requests/response.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | import "net/http"
4 |
5 | type Response struct {
6 | *http.Response
7 |
8 | BodyData []byte
9 | }
10 |
11 | func NewResponse(resp *http.Response) *Response {
12 | return &Response{
13 | Response: resp,
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/internal/waf/results.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package waf
4 |
5 | // PerformResult action performing result
6 | type PerformResult struct {
7 | ContinueRequest bool
8 | GoNextGroup bool
9 | GoNextSet bool
10 | IsAllowed bool
11 | AllowScope AllowScope
12 | }
13 |
14 | // MatchResult request match result
15 | type MatchResult struct {
16 | GoNext bool
17 | HasRequestBody bool
18 | Group *RuleGroup
19 | Set *RuleSet
20 | IsAllowed bool
21 | AllowScope AllowScope
22 | }
23 |
--------------------------------------------------------------------------------
/internal/waf/template.go:
--------------------------------------------------------------------------------
1 | package waf
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
6 | )
7 |
8 | func Template() (*WAF, error) {
9 | var config = firewallconfigs.HTTPFirewallTemplate()
10 | if config.Inbound != nil {
11 | config.Inbound.IsOn = true
12 | }
13 |
14 | for _, group := range config.AllRuleGroups() {
15 | if group.Code == "cc" || group.Code == "cc2" {
16 | continue
17 | }
18 | group.IsOn = true
19 |
20 | for _, set := range group.Sets {
21 | set.IsOn = true
22 | }
23 | }
24 |
25 | instance, err := SharedWAFManager.ConvertWAF(config)
26 | if err != nil {
27 | return nil, err
28 | }
29 |
30 | for _, group := range instance.Inbound {
31 | for _, set := range group.RuleSets {
32 | for _, rule := range set.Rules {
33 | rule.cacheLife = utils.CacheDisabled // for performance test
34 | _ = rule
35 | }
36 | }
37 | }
38 |
39 | return instance, nil
40 | }
41 |
--------------------------------------------------------------------------------
/internal/waf/values/number_list_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package values_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/waf/values"
7 | "github.com/iwind/TeaGo/assert"
8 | "testing"
9 | )
10 |
11 | func TestParseNumberList(t *testing.T) {
12 | var a = assert.NewAssertion(t)
13 |
14 | {
15 | var list = values.ParseNumberList("")
16 | a.IsFalse(list.Contains(123))
17 | }
18 |
19 | {
20 | var list = values.ParseNumberList(`123
21 | 456
22 |
23 | 789.1234`)
24 | a.IsTrue(list.Contains(123))
25 | a.IsFalse(list.Contains(0))
26 | a.IsFalse(list.Contains(789.123))
27 | a.IsTrue(list.Contains(789.1234))
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/internal/waf/values/string_list_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
2 |
3 | package values_test
4 |
5 | import (
6 | "github.com/TeaOSLab/EdgeNode/internal/waf/values"
7 | "github.com/iwind/TeaGo/assert"
8 | "testing"
9 | )
10 |
11 | func TestParseStringList(t *testing.T) {
12 | var a = assert.NewAssertion(t)
13 |
14 | {
15 | var list = values.ParseStringList("", false)
16 | a.IsFalse(list.Contains("hello"))
17 | }
18 |
19 | {
20 | var list = values.ParseStringList(`hello
21 |
22 | world
23 | hi
24 |
25 | people`, false)
26 | a.IsTrue(list.Contains("hello"))
27 | a.IsFalse(list.Contains("hello1"))
28 | a.IsFalse(list.Contains("Hello"))
29 | a.IsTrue(list.Contains("hi"))
30 | }
31 | {
32 | var list = values.ParseStringList(`Hello
33 |
34 | world
35 | hi
36 |
37 | people`, true)
38 | a.IsTrue(list.Contains("hello"))
39 | a.IsTrue(list.Contains("Hello"))
40 | a.IsTrue(list.Contains("HELLO"))
41 | a.IsFalse(list.Contains("How"))
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/internal/waf/waf_manager_test.go:
--------------------------------------------------------------------------------
1 | package waf_test
2 |
3 | import (
4 | "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
5 | "github.com/TeaOSLab/EdgeNode/internal/waf"
6 | "github.com/iwind/TeaGo/logs"
7 | "testing"
8 | )
9 |
10 | func TestWAFManager_convert(t *testing.T) {
11 | p := &firewallconfigs.HTTPFirewallPolicy{
12 | Id: 1,
13 | IsOn: true,
14 | Inbound: &firewallconfigs.HTTPFirewallInboundConfig{
15 | IsOn: true,
16 | Groups: []*firewallconfigs.HTTPFirewallRuleGroup{
17 | {
18 | Id: 1,
19 | Sets: []*firewallconfigs.HTTPFirewallRuleSet{
20 | {
21 | Id: 1,
22 | },
23 | {
24 | Id: 2,
25 | Rules: []*firewallconfigs.HTTPFirewallRule{
26 | {
27 | Id: 1,
28 | },
29 | {
30 | Id: 2,
31 | },
32 | },
33 | },
34 | },
35 | },
36 | },
37 | },
38 | }
39 | w, err := waf.SharedWAFManager.ConvertWAF(p)
40 | if err != nil {
41 | t.Fatal(err)
42 | }
43 |
44 | logs.PrintAsJSON(w, t)
45 | }
46 |
--------------------------------------------------------------------------------
/internal/zero/zero.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package zero
4 |
5 | type Zero = struct{}
6 |
7 | func New() Zero {
8 | return Zero{}
9 | }
10 |
--------------------------------------------------------------------------------
/internal/zero/zero_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
2 |
3 | package zero
4 |
5 | import (
6 | "runtime"
7 | "testing"
8 | )
9 |
10 | func TestZero_Chan(t *testing.T) {
11 | var stat1 = &runtime.MemStats{}
12 | runtime.ReadMemStats(stat1)
13 |
14 | var m = make(chan Zero, 2_000_000)
15 | for i := 0; i < 1_000_000; i++ {
16 | m <- New()
17 | }
18 |
19 | var stat2 = &runtime.MemStats{}
20 | runtime.ReadMemStats(stat2)
21 | t.Log(stat2.HeapInuse, stat1.HeapInuse, stat2.HeapInuse-stat1.HeapInuse, "B")
22 | t.Log(len(m))
23 | }
24 |
25 | func TestZero_Map(t *testing.T) {
26 | var stat1 = &runtime.MemStats{}
27 | runtime.ReadMemStats(stat1)
28 |
29 | var m = map[int]Zero{}
30 | for i := 0; i < 1_000_000; i++ {
31 | m[i] = New()
32 | }
33 |
34 | var stat2 = &runtime.MemStats{}
35 | runtime.ReadMemStats(stat2)
36 | t.Log((stat2.HeapInuse-stat1.HeapInuse)/1024/1024, "MB")
37 | t.Log(len(m))
38 |
39 | _, ok := m[1024]
40 | t.Log(ok)
41 | }
42 |
--------------------------------------------------------------------------------