0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Golangでhttpサーバーを作り直して、「Tiny Shiba.」と名付けた

Last updated at Posted at 2024-04-22

shiba_barner.png

golangのnet/httpを使った簡易WEBサーバー「Shiba.」をgithubにて公開していたのですが。HandleFuncが勝手にスレッドを作って動いているマルチスレッドなサーバーだと知って作り直し初めまして

どうせなら、構造体を使ったURLの指定を思い浮かんだので使いたい。そして、実行スレッド数を指定できる方法を海外のウェブで見つけたので、それも取り込んで作り直したら。ワンソースなプログラムでまとまりました

shiba.go
package main

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"net"
	"net/http"

	"golang.org/x/time/rate"
	"log"

	"context"
	"os"
	"os/signal"

	"runtime"
	"runtime/debug"
)

// Server System Settings.

const (
	DEBUG bool = true
)

var (
	filePath string = "public"

	port string = "8000"

	urlLength int = 1000

	threadCount int = 10
) // サーバーのパラメーターの設定

var (
	urlSetting []*urlRotator = []*urlRotator{
		{"/", world},
	}
) // URLルーティングの設定

var (
	limiter = rate.NewLimiter(1, 4) // 秒単位で4アクセス
) // 実行スレッド数の設定

// Server.

type urlRotator struct {
	path     string
	function func(w http.ResponseWriter, r *http.Request)
}

var (
	rexUrl = regexp.MustCompile(`\A[A-Za-z0-9\%\#\$\-\_\.\+\!\*\'\(\)\,\;\/\?\:\@\=\&\~\\\|]+\z`)

	hostAddress string
)

func main() {
	//
	mux := http.NewServeMux()
	for _, f := range urlSetting {
		mux.HandleFunc("/exec"+strings.TrimSpace(f.path), f.function)
	}
	mux.HandleFunc("/", publicFile)

	urlSetting = nil // メモリから削除

	//
	filePath = strings.TrimSpace(filePath)
	port = strings.TrimSpace(port)

	if port == "" {
		port = "80"
	}

	h := ""
	if DEBUG == true {
		h = "127.0.0.1"
	}

	a := net.JoinHostPort(h, port)

	server := &http.Server{
		Addr:    a,
		Handler: start(mux),

		ReadHeaderTimeout: 10 * time.Second,
		ReadTimeout:       10 * time.Second,
	}

	runtime.GC()
	debug.SetGCPercent(-1)

	fmt.Println("start Shiba server.")

	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
	defer stop()

	go func() {
		log.Fatal(server.ListenAndServe())
	}()

	<-ctx.Done()
	ctx, cancel := context.WithTimeout(context.Background(), 14*time.Second)
	defer cancel()

	fmt.Println("shutdown.")
	server.Shutdown(ctx)

	debug.SetGCPercent(100)
}

var threadCounter int = 0

func start(next http.Handler) http.Handler {
	//
	threadCounter++
	if threadCounter == threadCount {
		threadCounter = 0

		rtsGarbageCollection()
	}

	//
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if limiter.Allow() == false {
			http.Error(w, http.StatusText(429), http.StatusTooManyRequests)
			return
		}

		waf(w, r)

		next.ServeHTTP(w, r)
	})
}

func waf(w http.ResponseWriter, r *http.Request) {
	if len(r.URL.Path) > urlLength {
		http.Error(w, http.StatusText(400), http.StatusBadRequest)
		return
	}

	if rexUrl.FindString(r.URL.Path) == "" {
		http.Error(w, http.StatusText(400), http.StatusBadRequest)
		return
	}
}

func rtsGarbageCollection() {
	var mem runtime.MemStats

	runtime.ReadMemStats(&mem)

	if mem.HeapAlloc > 500<<20 { // 500MB
		runtime.GC()
	}
}

func publicFile(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, filePath+r.URL.Path)
}

// Action(s).

func world(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "New World.")
}

とりあえず、これをベースとして新型のShibaを作り直すつもりですが。取り急ぎ、動いちゃった試作品をMITライセンスで、qiitaへ置いときます。Your own risks...

今回、スマートなサーバーを作れたと思うけど。検索のみでプログラムを作ると、脳みそをまとめる手間が凄いのだなと思いました。書籍は大切

以上です

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?