diff --git a/_chapter14/section55/main.go b/_chapter14/section55/main.go index 830f360..5ff6eac 100644 --- a/_chapter14/section55/main.go +++ b/_chapter14/section55/main.go @@ -1,4 +1,4 @@ -package main +package section55 import ( "context" diff --git a/_chapter14/section55/main_test.go b/_chapter14/section55/main_test.go index aa6a56b..1ff19ff 100644 --- a/_chapter14/section55/main_test.go +++ b/_chapter14/section55/main_test.go @@ -1,4 +1,4 @@ -package main +package section55 import ( "context" diff --git a/_chapter14/section56/main.go b/_chapter14/section56/main.go new file mode 100644 index 0000000..dd0c05f --- /dev/null +++ b/_chapter14/section56/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "context" + "fmt" + "golang.org/x/sync/errgroup" + "log" + "net" + "net/http" + "os" +) + +func run(ctx context.Context, l net.Listener) error { + s := &http.Server{ + // 인수로 받은 net.Listener를 이용하므로 Addr 필드는 지정하지 않습니다. + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) + }), + } + eg, ctx := errgroup.WithContext(ctx) + eg.Go(func() error { + // Serve 메서드로 변경합니다. + if err := s.Serve(l); err != nil && + err != http.ErrServerClosed { + log.Printf("failed to close: %+v", err) + return err + } + return nil + }) + + <-ctx.Done() + if err := s.Shutdown(context.Background()); err != nil { + log.Printf("failed to shutdown: %+v", err) + } + + return eg.Wait() +} + +func main() { + // go run . 18080 + if len(os.Args) != 2 { + log.Printf("need port number\n") + os.Exit(1) + } + + p := os.Args[1] + l, err := net.Listen("tcp", ":"+p) + if err != nil { + log.Fatalf("failed to listen port %s: %v", p, err) + } + + if err := run(context.Background(), l); err != nil { + log.Printf("failed to terminate server: %v", err) + os.Exit(1) + } +} diff --git a/_chapter14/section56/main_test.go b/_chapter14/section56/main_test.go new file mode 100644 index 0000000..cdd6cb4 --- /dev/null +++ b/_chapter14/section56/main_test.go @@ -0,0 +1,53 @@ +package main + +import ( + "context" + "fmt" + "golang.org/x/sync/errgroup" + "io" + "net" + "net/http" + "testing" +) + +func TestRun(t *testing.T) { + // net/http 에서는 포트 번호에 0을 지정하면 사용 가능한 포트 번호를 동적으로 선택합니다. + l, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("failed to listen port %v", err) + } + ctx, cancel := context.WithCancel(context.Background()) + eg, ctx := errgroup.WithContext(ctx) + eg.Go(func() error { + return run(ctx, l) + }) + + in := "message" + url := fmt.Sprintf("http://%s/%s", l.Addr().String(), in) + // 어떤 포트 번호로 리슨중인지 확인합니다. + t.Logf("try request to %q", url) + rsp, err := http.Get(url) + + // 이후 코드 동일 + if err != nil { + t.Errorf("failed to get: %+v", err) + } + defer rsp.Body.Close() + + got, err := io.ReadAll(rsp.Body) + if err != nil { + t.Fatalf("failed to read body: %v", err) + } + + // HTTP 서버의 반환값을 검증합니다. + want := fmt.Sprintf("Hello, %s!", in) + if string(got) != want { + t.Errorf("want %q, but got %q", want, got) + } + + // run 함수를 종료합니다. + cancel() + if err := eg.Wait(); err != nil { + t.Fatal(err) + } +}