From a9f9014edd32dc22844054628db24cb12107ed19 Mon Sep 17 00:00:00 2001 From: Konstantin Demin Date: Fri, 19 Jul 2024 16:52:06 +0300 Subject: [PATCH] initial commit --- .ci/build-all.sh | 21 +++++ .ci/build.sh | 20 +++++ .ci/envsh.build | 44 ++++++++++ .ci/envsh.common | 72 +++++++++++++++++ .ci/envsh.registry | 14 ++++ .ci/image-all.sh | 50 ++++++++++++ .ci/image.sh | 27 +++++++ .ci/registry-login.sh | 21 +++++ .ci/sync-all.sh | 56 +++++++++++++ .ci/sync-latest.sh | 47 +++++++++++ .dockerignore | 3 + .gitignore | 4 + .woodpecker/.build.yml | 75 +++++++++++++++++ .woodpecker/.latest.yml | 43 ++++++++++ Dockerfile | 35 ++++++++ Dockerfile.ci | 12 +++ LICENSE | 175 ++++++++++++++++++++++++++++++++++++++++ Makefile | 69 ++++++++++++++++ fiber.go | 50 ++++++++++++ fiber_debug.go | 20 +++++ fiber_nodebug.go | 11 +++ go.mod | 19 +++++ go.sum | 28 +++++++ main.go | 50 ++++++++++++ return-code.go | 48 +++++++++++ 25 files changed, 1014 insertions(+) create mode 100755 .ci/build-all.sh create mode 100755 .ci/build.sh create mode 100644 .ci/envsh.build create mode 100644 .ci/envsh.common create mode 100644 .ci/envsh.registry create mode 100755 .ci/image-all.sh create mode 100755 .ci/image.sh create mode 100755 .ci/registry-login.sh create mode 100755 .ci/sync-all.sh create mode 100755 .ci/sync-latest.sh create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 .woodpecker/.build.yml create mode 100644 .woodpecker/.latest.yml create mode 100644 Dockerfile create mode 100644 Dockerfile.ci create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 fiber.go create mode 100644 fiber_debug.go create mode 100644 fiber_nodebug.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 return-code.go diff --git a/.ci/build-all.sh b/.ci/build-all.sh new file mode 100755 index 0000000..c3bfe3d --- /dev/null +++ b/.ci/build-all.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +[ -z "${CI_DEBUG}" ] || set -xv + +r=0 + +TARGET_PLATFORMS=$(printf '%s' "${TARGET_PLATFORMS:?}" | tr ',' ' ') +for TARGET_PLATFORM in ${TARGET_PLATFORMS} ; do + export TARGET_PLATFORM + + . .ci/envsh.build + .ci/build.sh || r=$? + [ "$r" = 0 ] || break +done + +make ci-clean + +exit "$r" diff --git a/.ci/build.sh b/.ci/build.sh new file mode 100755 index 0000000..3145a44 --- /dev/null +++ b/.ci/build.sh @@ -0,0 +1,20 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +[ -z "${CI_DEBUG}" ] || set -xv + +mkdir -p dist +OUTDIR=dist +OUTSFX='-'$(printf '%s' "${TARGET_PLATFORM:?}" | tr '/' '-') + +export OUTDIR OUTSFX + +idle() { + nice -n +40 \ + chrt -i 0 \ + ionice -c 3 \ + "$@" +} +idle make clean build || make clean build diff --git a/.ci/envsh.build b/.ci/envsh.build new file mode 100644 index 0000000..b3ab1cc --- /dev/null +++ b/.ci/envsh.build @@ -0,0 +1,44 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +unset GOAMD64 GOARM GOPPC64 GOMIPS GOMIPS64 + +## produce GOOS and GOARCH from TARGET_PLATFORM +unset GOOS GOARCH _variant +IFS=/ read -r GOOS GOARCH _variant <<-EOF +${TARGET_PLATFORM:?} +EOF +## verify that GOOS and GOARCH are not empty +: "${GOOS:?}" "${GOARCH:?}" +export GOOS GOARCH +## fill env with Go-related variables +if [ -n "${_variant}" ] ; then + case "${GOARCH}" in + amd64 ) + export GOAMD64="${_variant}" ;; + arm ) + export GOARM="${_variant#v}" ;; + ppc64 | ppc64le ) + export GOPPC64="${_variant}" ;; + mips | mipsle ) + export GOMIPS="${_variant}" ;; + mips64 | mips64le ) + export GOMIPS64="${_variant}" ;; + esac +fi +unset _variant + +unset RELMODE +while : ; do + [ -n "${CI_COMMIT_BRANCH}" ] || break + [ -n "${CI_REPO_DEFAULT_BRANCH}" ] || break + + ## RELMODE is for default branch only + [ "${CI_COMMIT_BRANCH}" = "${CI_REPO_DEFAULT_BRANCH}" ] || break + export RELMODE=1 + + break +done +[ -z "${CI_COMMIT_TAG}" ] || export RELMODE=1 diff --git a/.ci/envsh.common b/.ci/envsh.common new file mode 100644 index 0000000..b66907c --- /dev/null +++ b/.ci/envsh.common @@ -0,0 +1,72 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +## shifty-nifty shell goodies :) + +## do same thing as GitLab does for CI_COMMIT_REF_SLUG: +## 1. lowercase string +## 2. replace not allowed chars with '-' (squeezing repeats) +## allowed chars are: `0-9`, `a-z` and '-' +## 3. remove leading and trailing '-' (if any) +## 4. truncate string up to 63 chars +## 5. remove trailing '-' (if any) +ref_slug() { + printf '%s' "${1:?}" \ + | sed -Ez 's/^(.+)$/\L\1/;s/[^0-9a-z]+/-/g;s/^-//;s/-$//;s/^(.{1,63}).*$/\1/;s/-$//' \ + | tr -d '\0' +} + +## normalize image tag +## performs like ref_slug() except: +## - symbols '_' and '.' are allowed too +## - truncate string up to 96 chars +## - squeeze symbol sequences: +## - '-' has higher priority than surrounding (leading and trailing) symbols +## - first symbol in sequence has higher priority than following symbols +## NB: implementation is rather demonstrative than effective +image_tag_norm() { + printf '%s' "${1:?}" \ + | sed -Ez 's/^(.+)$/\L\1/;s/[^0-9a-z_.]+/-/g' \ + | sed -Ez 's/\.+/./g;s/_+/_/g;s/[_.]+-/-/g;s/-[_.]+/-/g;s/([_.])[_.]+/\1/g' \ + | sed -Ez 's/^[_.-]//;s/[_.-]$//;s/^(.{1,95}).*$/\1/;s/[_.-]$//' \ + | tr -d '\0' +} + +## misc CI things +# CI_COMMIT_SHORT_SHA="${CI_COMMIT_SHA:0:8}" +CI_COMMIT_SHORT_SHA=$(printf '%s' "${CI_COMMIT_SHA}" | cut -c 1-8) +if [ -n "${CI_COMMIT_BRANCH}" ] ; then + CI_COMMIT_REF_SLUG="${CI_COMMIT_BRANCH}" +fi +if [ -n "${CI_COMMIT_SOURCE_BRANCH}" ] ; then + CI_COMMIT_REF_SLUG="${CI_COMMIT_SOURCE_BRANCH}" +fi +if [ -n "${CI_COMMIT_TAG}" ] ; then + CI_COMMIT_REF_SLUG="${CI_COMMIT_TAG}" +fi +CI_COMMIT_REF_SLUG="$(image_tag_norm "${CI_COMMIT_REF_SLUG}")" + +## image tag(s) +IMAGE_TAG="${CI_COMMIT_SHORT_SHA}-b${CI_PIPELINE_NUMBER}-${CI_COMMIT_REF_SLUG}" +EXTRA_TAGS=$(image_tag_norm "branch-${CI_COMMIT_BRANCH}") +if [ -n "${CI_COMMIT_TAG}" ] ; then + IMAGE_TAG="${CI_COMMIT_SHORT_SHA}" + EXTRA_TAGS="${CI_COMMIT_REF_SLUG}" + ## TODO: think about "latest" tag: it should be error-prone for "backward tag push" + # EXTRA_TAGS="${CI_COMMIT_REF_SLUG} latest" +else + if [ -n "${CI_COMMIT_SOURCE_BRANCH}" ] ; then + echo "Running on branch '${CI_COMMIT_SOURCE_BRANCH}'" + else + if [ "${CI_COMMIT_BRANCH}" != "${CI_REPO_DEFAULT_BRANCH}" ] ; then + echo "Running on branch '${CI_COMMIT_BRANCH}'" + else + IMAGE_TAG="${CI_COMMIT_SHORT_SHA}" + fi + fi +fi +IMAGE_TAG=$(image_tag_norm "${IMAGE_TAG}") + +export CI_COMMIT_SHORT_SHA CI_COMMIT_REF_SLUG IMAGE_TAG EXTRA_TAGS diff --git a/.ci/envsh.registry b/.ci/envsh.registry new file mode 100644 index 0000000..4033b69 --- /dev/null +++ b/.ci/envsh.registry @@ -0,0 +1,14 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +## setup image registry authentication +export REGISTRY_AUTH_FILE="${PWD}/.ci/.auth.json" +if ! [ -s "${REGISTRY_AUTH_FILE}" ] ; then + if [ -z "${REGISTRY_AUTH}" ] ; then + echo 'REGISTRY_AUTH is missing' + exit 1 + fi + printf '%s' "${REGISTRY_AUTH}" > "${REGISTRY_AUTH_FILE}" +fi diff --git a/.ci/image-all.sh b/.ci/image-all.sh new file mode 100755 index 0000000..4813b17 --- /dev/null +++ b/.ci/image-all.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +[ -z "${CI_DEBUG}" ] || set -xv + +: "${TARGET_PLATFORMS:?}" + +. .ci/envsh.common +. .ci/envsh.registry + +: "${IMAGE_NAME:?}" "${IMAGE_TAG:?}" +IMAGE="${IMAGE_NAME}:${IMAGE_TAG}" + +## used by .ci/image.sh +export IMAGE_MANIFEST="${IMAGE}" + +if buildah manifest exists "${IMAGE}" ; then + buildah manifest rm "${IMAGE}" +fi +buildah manifest create "${IMAGE}" + +r=0 + +TARGET_PLATFORMS=$(printf '%s' "${TARGET_PLATFORMS}" | tr ',' ' ') +for TARGET_PLATFORM in ${TARGET_PLATFORMS} ; do + export TARGET_PLATFORM + + . .ci/envsh.build + + PLATFORM_SUFFIX='-'$(printf '%s' "${TARGET_PLATFORM}" | tr '/' '-') + export PLATFORM_SUFFIX + + .ci/image.sh || r=$? + [ "$r" = 0 ] || break + + buildah manifest add "${IMAGE}" "${IMAGE}${PLATFORM_SUFFIX}" +done + +[ "$r" = 0 ] || exit "$r" + +## list built image(s) +echo +echo 'IMAGES:' +echo +buildah images --all --noheading --format 'table {{.ID}} {{.Name}}:{{.Tag}} {{.Size}} {{.CreatedAtRaw}}' --filter "reference=${IMAGE_NAME}" +echo + +buildah manifest push --all "${IMAGE}" "docker://${IMAGE}" diff --git a/.ci/image.sh b/.ci/image.sh new file mode 100755 index 0000000..f847f84 --- /dev/null +++ b/.ci/image.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +. .ci/envsh.registry + +[ -z "${CI_DEBUG}" ] || set -xv + +## produce _real_ BASE_IMAGE because "static-debian12:debug-nonroot" is not multiarch image (yet) +export BASE_IMAGE="${BASE_IMAGE:?}-${GOARCH:?}" + +buildah pull \ + --platform "${TARGET_PLATFORM}" \ + --retry 3 --retry-delay 30s \ +"${BASE_IMAGE}" + +## build image +buildah bud \ + -t "${IMAGE_NAME}:${IMAGE_TAG}${PLATFORM_SUFFIX}" \ + -f ./Dockerfile.ci \ + ${IMAGE_MANIFEST:+ --manifest "${IMAGE_MANIFEST}" } \ + --platform "${TARGET_PLATFORM}" \ + --build-arg "TARGET_PLATFORM=${TARGET_PLATFORM}" \ + --build-arg "PLATFORM_SUFFIX=${PLATFORM_SUFFIX}" \ + --build-arg "BASE_IMAGE=${BASE_IMAGE}" \ + --network=host diff --git a/.ci/registry-login.sh b/.ci/registry-login.sh new file mode 100755 index 0000000..e6d77e2 --- /dev/null +++ b/.ci/registry-login.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +[ -z "${CI_DEBUG}" ] || set -xv + +unset _bin +for i in podman buildah skopeo ; do + if command -V "$i" >/dev/null ; then + _bin=$i + break + fi +done +: "${_bin:?}" + +. .ci/envsh.registry + +for i ; do + "${_bin}" login "$i" ${image_dst}:${IMAGE_TAG}" + img_copy "${image_interim}" "${image_dst}:${IMAGE_TAG}" || r=$? + [ "$r" = 0 ] || break + + for tag in ${EXTRA_TAGS} ; do + [ -n "${tag}" ] || continue + + echo " -> ${image_src}:${tag}" + img_copy "${image_interim}" "${image_src}:${tag}" || r=$? + [ "$r" = 0 ] || break + + echo " -> ${image_dst}:${tag}" + img_copy "${image_interim}" "${image_dst}:${tag}" || r=$? + [ "$r" = 0 ] || break + done + + break +done + +rm -rf "${oci_dir}" +exit "$r" diff --git a/.ci/sync-latest.sh b/.ci/sync-latest.sh new file mode 100755 index 0000000..a9a1179 --- /dev/null +++ b/.ci/sync-latest.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +[ -z "${CI_DEBUG}" ] || set -xv + +: "${IMAGE_NAME:?}" "${EXT_IMAGE_NAME:?}" "${LATEST_TAG:?}" + +. .ci/envsh.registry + +image_src="docker://${IMAGE_NAME}" +image_dst="docker://${EXT_IMAGE_NAME}" + +oci_dir="${PWD}/oci-layers" +image_interim="oci:${oci_dir}:$(basename "${IMAGE_NAME}"):${LATEST_TAG}" + +rm -rf "${oci_dir}" ; mkdir "${oci_dir}" + +r=0 + +img_copy() { + for i in $(seq 1 3) ; do + if skopeo copy --all "$@" ; then + return 0 + fi + done + return 1 +} + +while : ; do + img_copy "${image_src}:${LATEST_TAG}" "${image_interim}" || r=$? + [ "$r" = 0 ] || break + + echo " -> ${image_src}:latest" + img_copy "${image_interim}" "${image_src}:latest" || r=$? + [ "$r" = 0 ] || break + + echo " -> ${image_dst}:latest" + img_copy "${image_interim}" "${image_dst}:latest" || r=$? + [ "$r" = 0 ] || break + + break +done + +rm -rf "${oci_dir}" +exit "$r" diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e70b330 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +.ci +.vscode +rngpotd* diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8bb16ec --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/.ci/.auth.json +/.vscode/ +/dist/ +/rngpotd diff --git a/.woodpecker/.build.yml b/.woodpecker/.build.yml new file mode 100644 index 0000000..50e38c3 --- /dev/null +++ b/.woodpecker/.build.yml @@ -0,0 +1,75 @@ +when: + - event: [ push, tag, cron, manual ] + evaluate: 'LATEST_TAG == ""' + +variables: + - &image_name 'quay.krd.sh/krd/rngpotd' + - &ext_image_name 'docker.io/rockdrilla/rngpotd' + - &buildah_image 'quay.krd.sh/quay_io/containers/buildah:v1.36.0' + - &skopeo_image 'quay.krd.sh/quay_io/containers/skopeo:v1.15.2' + - &go_image 'quay.krd.sh/golang:1.22.5-bookworm' + - &base_image 'quay.krd.sh/gcr_io/distroless/static-debian12:nonroot' + ## value list depends on base image + ## ref: https://github.com/GoogleContainerTools/distroless#debian-12 + - &target_platforms 'linux/amd64,linux/arm,linux/arm64,linux/ppc64le,linux/s390x' + +## kind of fixup (remove in near future) +## ref: https://github.com/woodpecker-ci/plugin-git/releases +clone: + git: + image: quay.krd.sh/docker_io/woodpeckerci/plugin-git:2.5.1 + +## NB: ${variable} expressions are subject to pre-processing. +## ref: https://woodpecker-ci.org/docs/usage/environment + +steps: + + - name: verify-registry-credentials + image: *skopeo_image + environment: + GOMAXPROCS: "4" + MALLOC_ARENA_MAX: "4" + secrets: [ REGISTRY_AUTH ] + commands: + - .ci/registry-login.sh quay.krd.sh docker.io + + - name: build-all + image: *go_image + environment: + GOMAXPROCS: "4" + MALLOC_ARENA_MAX: "4" + ## + TARGET_PLATFORMS: *target_platforms + ## these secrets are server-wide + secrets: [ GOPRIVATE, GOPROXY, GOSUMDB ] + commands: + - .ci/build-all.sh + + - name: image-all + image: *buildah_image + privileged: true + environment: + GOMAXPROCS: "4" + MALLOC_ARENA_MAX: "4" + ## + BUILDAH_FORMAT: "docker" + TARGET_PLATFORMS: *target_platforms + BASE_IMAGE: *base_image + IMAGE_NAME: *image_name + commands: + - .ci/image-all.sh + + - name: image-sync + image: *skopeo_image + environment: + GOMAXPROCS: "4" + MALLOC_ARENA_MAX: "4" + ## + IMAGE_NAME: *image_name + EXT_IMAGE_NAME: *ext_image_name + commands: + - .ci/sync-all.sh + +## personal tweaks :) +labels: + network: dmz diff --git a/.woodpecker/.latest.yml b/.woodpecker/.latest.yml new file mode 100644 index 0000000..38d6ef6 --- /dev/null +++ b/.woodpecker/.latest.yml @@ -0,0 +1,43 @@ +when: + - event: [ manual ] + evaluate: 'LATEST_TAG != ""' + +variables: + - &image_name 'quay.krd.sh/krd/rngpotd' + - &ext_image_name 'docker.io/rockdrilla/rngpotd' + - &skopeo_image 'quay.krd.sh/quay_io/containers/skopeo:v1.15.2' + +## kind of fixup (remove in near future) +## ref: https://github.com/woodpecker-ci/plugin-git/releases +clone: + git: + image: quay.krd.sh/docker_io/woodpeckerci/plugin-git:2.5.1 + +## NB: ${variable} expressions are subject to pre-processing. +## ref: https://woodpecker-ci.org/docs/usage/environment + +steps: + + - name: verify-registry-credentials + image: *skopeo_image + environment: + GOMAXPROCS: "4" + MALLOC_ARENA_MAX: "4" + secrets: [ REGISTRY_AUTH ] + commands: + - .ci/registry-login.sh quay.krd.sh docker.io + + - name: image-sync-latest + image: *skopeo_image + environment: + GOMAXPROCS: "4" + MALLOC_ARENA_MAX: "4" + ## + IMAGE_NAME: *image_name + EXT_IMAGE_NAME: *ext_image_name + commands: + - .ci/sync-latest.sh + +## personal tweaks :) +labels: + network: dmz diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4de36ec --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +ARG GO_IMAGE=docker.io/library/golang:1.22.5-bookworm +ARG BASE_IMAGE=gcr.io/distroless/static-debian12:nonroot + +## --- + +FROM ${GO_IMAGE} as build +SHELL [ "/bin/sh", "-ec" ] + +ARG GOPROXY +ARG GOSUMDB +ARG GOPRIVATE + +ARG RELMODE + +WORKDIR /go/src + +COPY . . + +ENV GOMAXPROCS=4 \ + MALLOC_ARENA_MAX=4 + +RUN go env | grep -F -e GOPROXY -e GOSUMDB ; \ + make OUTDIR=/go/bin ; \ + make ci-clean + +## --- + +FROM ${BASE_IMAGE} + +COPY --from=build /go/bin/rngpotd /bin/ + +ENTRYPOINT [ "/bin/rngpotd" ] +CMD [ ] + +USER nonroot:nonroot diff --git a/Dockerfile.ci b/Dockerfile.ci new file mode 100644 index 0000000..f75cf58 --- /dev/null +++ b/Dockerfile.ci @@ -0,0 +1,12 @@ +ARG TARGET_PLATFORM +ARG BASE_IMAGE + +FROM --platform=${TARGET_PLATFORM} ${BASE_IMAGE} + +ARG PLATFORM_SUFFIX +COPY /dist/rngpotd${PLATFORM_SUFFIX} /bin/rngpotd + +ENTRYPOINT [ ] +CMD [ "/bin/rngpotd" ] + +USER nonroot:nonroot diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..67db858 --- /dev/null +++ b/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fa9df2e --- /dev/null +++ b/Makefile @@ -0,0 +1,69 @@ +#!/usr/bin/make -f +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin + +SHELL :=/bin/sh +.SHELLFLAGS :=-ec + +.NOTPARALLEL: + +BIN := rngpotd + +OUTDIR ?= . +OUTSFX ?= +OUTBIN ?= $(OUTDIR)/$(BIN)$(OUTSFX) + +export GO ?= go +export CGO_ENABLED ?= 0 +TAGS ?= +LDFLAGS ?= +GO_BUILDFLAGS ?= +GO_LDFLAGS := -w $(LDFLAGS) + +comma :=, +ifeq ($(RELMODE),1) + TAGS := rngpot_nodebug$(if $(strip $(TAGS)),$(comma)$(strip $(TAGS))) + + GO_LDFLAGS += -s +endif + +.PHONY: all +all: build + +.PHONY: clean build dev-build ci-clean + +clean: + $(if $(wildcard $(OUTBIN)),rm -fv $(OUTBIN),:) + +build: $(OUTBIN) + +test_git = git -c log.showsignature=false show -s --format=%H:%ct + +$(OUTBIN): + @:; \ + GO_BUILDFLAGS='$(strip $(GO_BUILDFLAGS))' ; \ + if ! $(test_git) >/dev/null 2>&1 ; then \ + echo "!!! git information is asbent !!!" >&2 ; \ + GO_BUILDFLAGS="-buildvcs=false $${GO_BUILDFLAGS}" ; \ + fi ; \ + for i in $$(seq 1 3) ; do \ + if $(GO) get ; then break ; fi ; \ + done ; \ + $(GO) build -o $@ \ + $${GO_BUILDFLAGS} \ + $(if $(strip $(TAGS)),-tags '$(strip $(TAGS))') \ + $(if $(strip $(GO_LDFLAGS)),-ldflags '$(strip $(GO_LDFLAGS))') \ + $(if $(VERBOSE),-v) ; \ + $(GO) version -m $@ + +dev-build: GO_BUILDFLAGS := -race $(GO_BUILDFLAGS) +dev-build: CGO_ENABLED := 1 +dev-build: RELMODE := 0 +dev-build: build + +ci-clean: + for d in '$(shell $(GO) env GOCACHE)' '$(shell $(GO) env GOMODCACHE)' ; do \ + [ -n "$$d" ] || continue ; \ + [ -d "$$d" ] || continue ; \ + rm -rf "$$d" ; \ + done diff --git a/fiber.go b/fiber.go new file mode 100644 index 0000000..9535fe9 --- /dev/null +++ b/fiber.go @@ -0,0 +1,50 @@ +package main + +import ( + "math/rand" + "time" + + "github.com/gofiber/fiber/v2" +) + +var ( + fiberConfig = fiber.Config{ + AppName: userAgent, + ServerHeader: serverAgent, + + StrictRouting: true, + CaseSensitive: true, + Immutable: true, + + BodyLimit: -1, + ReadTimeout: 10 * time.Millisecond, + + DisableKeepalive: true, + DisableHeaderNormalizing: true, + DisablePreParseMultipartForm: true, + DisableDefaultDate: true, + DisableDefaultContentType: true, + + ErrorHandler: fiberErrorHandler, + } +) + +func initFiberApp() *fiber.App { + preinitFiberConfig() + + app := fiber.New(fiberConfig) + preinitFiberApp(app) + app.Use(fiberRngPot) + + return app +} + +func fiberRngPot(c *fiber.Ctx) error { + return c.SendStatus( + rngRetCodes[rand.Intn(n_rngRetCodes)], + ) +} + +func fiberErrorHandler(c *fiber.Ctx, err error) error { + return fiberRngPot(c) +} diff --git a/fiber_debug.go b/fiber_debug.go new file mode 100644 index 0000000..e466fa7 --- /dev/null +++ b/fiber_debug.go @@ -0,0 +1,20 @@ +//go:build !rngpot_nodebug + +package main + +import ( + "time" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/logger" +) + +func preinitFiberConfig() { + fiberConfig.EnablePrintRoutes = true +} + +func preinitFiberApp(app *fiber.App) { + app.Use(logger.New(logger.Config{ + TimeInterval: 100 * time.Millisecond, + })) +} diff --git a/fiber_nodebug.go b/fiber_nodebug.go new file mode 100644 index 0000000..7e7d91b --- /dev/null +++ b/fiber_nodebug.go @@ -0,0 +1,11 @@ +//go:build rngpot_nodebug + +package main + +import "github.com/gofiber/fiber/v2" + +func preinitFiberConfig() { +} + +func preinitFiberApp(app *fiber.App) { +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..11261ca --- /dev/null +++ b/go.mod @@ -0,0 +1,19 @@ +module git.krd.sh/krd/rngpotd + +go 1.22 + +require github.com/gofiber/fiber/v2 v2.52.5 + +require ( + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.55.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/sys v0.22.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..09f1559 --- /dev/null +++ b/go.sum @@ -0,0 +1,28 @@ +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo= +github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8= +github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..7a7a57e --- /dev/null +++ b/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "flag" + "log" + "runtime" +) + +const ( + appName = "rngpotd" + appVersion = "0.0.1" + userAgent = appName + "/" + appVersion + serverAgent = "nginx/1.27.0" + + minimumGoMaxProcs = 2 + maximumGoMaxProcs = 4 +) + +var ( + listenEndpoint string +) + +func init() { + flag.StringVar(&listenEndpoint, "listen", ":8888", "listen (\":port\", \"address:port\")") +} + +func main() { + gmp := runtime.GOMAXPROCS(0) + if gmp < minimumGoMaxProcs { + runtime.GOMAXPROCS(minimumGoMaxProcs) + } + if gmp > maximumGoMaxProcs { + runtime.GOMAXPROCS(maximumGoMaxProcs) + } + + n_rngRetCodes = len(rngRetCodes) + + log.SetFlags(log.Flags() | log.Lmicroseconds) + flag.Parse() + + log.Printf("%s: starting\n", userAgent) + + app := initFiberApp() + + log.Printf("%s: ready\n", userAgent) + log.Printf("%s: going to listen %q\n", userAgent, listenEndpoint) + if err := app.Listen(listenEndpoint); err != nil { + log.Fatal(err) + } +} diff --git a/return-code.go b/return-code.go new file mode 100644 index 0000000..d3c0d80 --- /dev/null +++ b/return-code.go @@ -0,0 +1,48 @@ +package main + +import ( + "github.com/gofiber/fiber/v2" +) + +var ( + rngRetCodes = [...]int{ + fiber.StatusTeapot, + + fiber.StatusEarlyHints, + + fiber.StatusNonAuthoritativeInformation, + fiber.StatusNoContent, + fiber.StatusResetContent, + fiber.StatusMultiStatus, + fiber.StatusAlreadyReported, + fiber.StatusIMUsed, + 267, // https://github.com/maximal/http-267 + + fiber.StatusSwitchProxy, + + fiber.StatusPaymentRequired, + fiber.StatusNotAcceptable, + fiber.StatusProxyAuthRequired, + fiber.StatusConflict, + fiber.StatusGone, + fiber.StatusPreconditionFailed, + fiber.StatusRequestEntityTooLarge, + fiber.StatusRequestURITooLong, + fiber.StatusUnsupportedMediaType, + fiber.StatusRequestedRangeNotSatisfiable, + fiber.StatusExpectationFailed, + fiber.StatusMisdirectedRequest, + fiber.StatusUnprocessableEntity, + fiber.StatusLocked, + fiber.StatusFailedDependency, + fiber.StatusTooEarly, + fiber.StatusUpgradeRequired, + fiber.StatusPreconditionRequired, + fiber.StatusTooManyRequests, + fiber.StatusRequestHeaderFieldsTooLarge, + 444, // nginx gone wild + fiber.StatusUnavailableForLegalReasons, + 499, // try harder comrade! + } + n_rngRetCodes = 1 +)