Skip to content

Commit 3a0c7e3

Browse files
authored
Merge pull request #19 from shekshuev/iter25
Iter25
2 parents e184a64 + 04f3d1b commit 3a0c7e3

23 files changed

+2630
-152
lines changed

cmd/shortener/main.go

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"context"
55
"fmt"
6+
"net"
67
"net/http"
78
"os"
89
"os/signal"
@@ -14,11 +15,15 @@ import (
1415
"github.com/shekshuev/shortener/internal/app/logger"
1516
"github.com/shekshuev/shortener/internal/app/service"
1617
"github.com/shekshuev/shortener/internal/app/store"
18+
"google.golang.org/grpc"
1719

1820
_ "github.com/joho/godotenv/autoload"
1921
"go.uber.org/zap"
2022

2123
_ "net/http/pprof"
24+
25+
"github.com/shekshuev/shortener/internal/app/grpcserver"
26+
"github.com/shekshuev/shortener/internal/app/proto"
2227
)
2328

2429
var (
@@ -48,53 +53,87 @@ func main() {
4853

4954
l := logger.NewLogger()
5055
cfg := config.GetConfig()
51-
var urlStore store.URLStore = nil
56+
57+
var trustedSubnet *net.IPNet
58+
if cfg.TrustedSubnet != "" {
59+
_, subnet, err := net.ParseCIDR(cfg.TrustedSubnet)
60+
if err != nil {
61+
l.Log.Fatal("Invalid trusted subnet", zap.Error(err))
62+
}
63+
trustedSubnet = subnet
64+
}
65+
66+
var urlStore store.URLStore
5267
if cfg.DatabaseDSN == cfg.DefaultDatabaseDSN {
5368
urlStore = store.NewMemoryURLStore(&cfg)
5469
} else {
5570
urlStore = store.NewPostgresURLStore(&cfg)
5671
}
5772
urlService := service.NewURLService(urlStore, &cfg)
58-
urlHandler := handler.NewURLHandler(urlService)
59-
server := &http.Server{
73+
74+
urlHandler := handler.NewURLHandler(urlService, trustedSubnet)
75+
httpServer := &http.Server{
6076
Addr: cfg.ServerAddress,
6177
Handler: urlHandler.Router,
6278
}
6379

80+
grpcSrv := grpc.NewServer()
81+
grpcHandler := grpcserver.NewServer(urlService)
82+
proto.RegisterURLShortenerServer(grpcSrv, grpcHandler)
83+
84+
grpcListener, err := net.Listen("tcp", cfg.GRPCServerAddress)
85+
if err != nil {
86+
l.Log.Fatal("Failed to listen for gRPC", zap.Error(err))
87+
}
88+
6489
done := make(chan os.Signal, 1)
6590
signal.Notify(done, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
6691

6792
go func() {
6893
var err error
6994
if cfg.EnableHTTPS {
7095
l.Log.Info("Starting HTTPS server", zap.String("addr", cfg.ServerAddress))
71-
err = server.ListenAndServeTLS(cfg.CertFile, cfg.KeyFile)
96+
err = httpServer.ListenAndServeTLS(cfg.CertFile, cfg.KeyFile)
7297
} else {
7398
l.Log.Info("Starting HTTP server", zap.String("addr", cfg.ServerAddress))
74-
err = server.ListenAndServe()
99+
err = httpServer.ListenAndServe()
75100
}
76101
if err != nil && err != http.ErrServerClosed {
77-
l.Log.Error("Error starting server", zap.Error(err))
102+
l.Log.Error("Error starting HTTP server", zap.Error(err))
103+
}
104+
}()
105+
106+
go func() {
107+
l.Log.Info("Starting gRPC server", zap.String("addr", cfg.GRPCServerAddress))
108+
if err := grpcSrv.Serve(grpcListener); err != nil {
109+
l.Log.Error("Error starting gRPC server", zap.Error(err))
78110
}
79111
}()
112+
80113
go func() {
81-
http.ListenAndServe("localhost:6060", nil)
114+
_ = http.ListenAndServe("localhost:6060", nil)
82115
}()
83116

84-
l.Log.Info("Server started. Waiting for shutdown signal...")
117+
l.Log.Info("Servers started. Waiting for shutdown signal...")
85118

86119
<-done
87120
l.Log.Info("Shutdown signal received")
121+
88122
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
89123
defer cancel()
124+
125+
grpcSrv.GracefulStop()
126+
l.Log.Info("gRPC server shutdown gracefully")
127+
128+
if err := httpServer.Shutdown(ctx); err != nil {
129+
l.Log.Error("HTTP server forced to shutdown", zap.Error(err))
130+
} else {
131+
l.Log.Info("HTTP server shutdown gracefully")
132+
}
133+
90134
if err := urlStore.Close(); err != nil {
91135
l.Log.Error("Error closing store", zap.Error(err))
92136
} else {
93137
l.Log.Info("Store closed")
94138
}
95-
if err := server.Shutdown(ctx); err != nil {
96-
l.Log.Error("Server forced to shutdown", zap.Error(err))
97-
} else {
98-
l.Log.Info("Server shutdown gracefully")
99-
}
100139
}

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ require (
3030
github.com/go-toolsmith/strparse v1.1.0 // indirect
3131
github.com/go-toolsmith/typep v1.1.0 // indirect
3232
github.com/gofrs/uuid v4.4.0+incompatible // indirect
33+
github.com/golang/protobuf v1.5.3 // indirect
3334
github.com/google/go-cmp v0.7.0 // indirect
3435
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect
3536
github.com/pkg/errors v0.9.1 // indirect
@@ -46,6 +47,10 @@ require (
4647
golang.org/x/mod v0.23.0 // indirect
4748
golang.org/x/net v0.35.0 // indirect
4849
golang.org/x/sync v0.11.0 // indirect
50+
golang.org/x/sys v0.30.0 // indirect
4951
golang.org/x/text v0.22.0 // indirect
52+
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
53+
google.golang.org/grpc v1.59.0 // indirect
54+
google.golang.org/protobuf v1.32.0 // indirect
5055
gopkg.in/yaml.v3 v3.0.1 // indirect
5156
)

go.sum

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1
3737
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
3838
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
3939
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
40+
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
41+
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
42+
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
43+
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
4044
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
4145
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
4246
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
@@ -87,12 +91,26 @@ golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
8791
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
8892
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
8993
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
94+
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
95+
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
9096
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
9197
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
9298
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
9399
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
94100
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
95101
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
102+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
103+
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY=
104+
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
105+
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
106+
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
107+
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
108+
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
109+
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
110+
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
111+
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
112+
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
113+
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
96114
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
97115
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
98116
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

internal/app/config/config.go

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,40 +12,48 @@ import (
1212

1313
// Config содержит настройки приложения, включая параметры сервера, базы данных и файлового хранилища.
1414
type Config struct {
15-
ServerAddress string // Адрес и порт, на котором запускается сервер.
16-
BaseURL string // Базовый URL для сокращённых ссылок.
17-
FileStoragePath string // Путь к файлу для хранения сокращённых URL.
18-
DatabaseDSN string // Строка подключения к базе данных.
19-
EnableHTTPS bool // Включить HTTPS.
20-
CertFile string // Путь к файлу с сертификатом.
21-
KeyFile string // путь к файлу с ключом.
22-
DefaultServerAddress string // Значение по умолчанию для ServerAddress.
23-
DefaultBaseURL string // Значение по умолчанию для BaseURL.
24-
DefaultFileStoragePath string // Значение по умолчанию для FileStoragePath.
25-
DefaultDatabaseDSN string // Значение по умолчанию для DatabaseDSN.
26-
DefaultEnableHTTPS bool // Значение по умолчанию для EnableHTTPS.
27-
DefaultCertFile string // Значение по умолчанию для CertFile.
28-
DefaultKeyFile string // Значение по умолчанию для KeyFile.
15+
ServerAddress string // Адрес и порт, на котором запускается сервер.
16+
BaseURL string // Базовый URL для сокращённых ссылок.
17+
FileStoragePath string // Путь к файлу для хранения сокращённых URL.
18+
DatabaseDSN string // Строка подключения к базе данных.
19+
EnableHTTPS bool // Включить HTTPS.
20+
CertFile string // Путь к файлу с сертификатом.
21+
KeyFile string // путь к файлу с ключом.
22+
TrustedSubnet string // Доверенная подсеть в CIDR-формате.
23+
GRPCServerAddress string // Адрес GRPC
24+
DefaultServerAddress string // Значение по умолчанию для ServerAddress.
25+
DefaultBaseURL string // Значение по умолчанию для BaseURL.
26+
DefaultFileStoragePath string // Значение по умолчанию для FileStoragePath.
27+
DefaultDatabaseDSN string // Значение по умолчанию для DatabaseDSN.
28+
DefaultEnableHTTPS bool // Значение по умолчанию для EnableHTTPS.
29+
DefaultCertFile string // Значение по умолчанию для CertFile.
30+
DefaultKeyFile string // Значение по умолчанию для KeyFile.
31+
DefaultTrustedSubnet string // Значение по умолчанию для TrustedSubnet.
32+
DefaultGRPCServerAddress string // Значение по умолчанию для GRPCServerAddress.
2933
}
3034

3135
type envConfig struct {
32-
ServerAddress string `env:"SERVER_ADDRESS"`
33-
BaseURL string `env:"BASE_URL"`
34-
FileStoragePath string `env:"FILE_STORAGE_PATH"`
35-
DatabaseDSN string `env:"DATABASE_DSN"`
36-
EnableHTTPS string `env:"ENABLE_HTTPS"`
37-
CertFile string `env:"TLS_CERT"`
38-
KeyFile string `env:"TLS_KEY"`
36+
ServerAddress string `env:"SERVER_ADDRESS"`
37+
BaseURL string `env:"BASE_URL"`
38+
FileStoragePath string `env:"FILE_STORAGE_PATH"`
39+
DatabaseDSN string `env:"DATABASE_DSN"`
40+
EnableHTTPS string `env:"ENABLE_HTTPS"`
41+
CertFile string `env:"TLS_CERT"`
42+
KeyFile string `env:"TLS_KEY"`
43+
TrustedSubnet string `env:"TRUSTED_SUBNET"`
44+
GRPCServerAddress string `env:"GRPC_SERVER_ADDRESS"`
3945
}
4046

4147
type jsonConfig struct {
42-
ServerAddress string `json:"server_address"`
43-
BaseURL string `json:"base_url"`
44-
FileStoragePath string `json:"file_storage_path"`
45-
DatabaseDSN string `json:"database_dsn"`
46-
EnableHTTPS bool `json:"enable_https"`
47-
CertFile string `json:"cert_file"`
48-
KeyFile string `json:"key_file"`
48+
ServerAddress string `json:"server_address"`
49+
BaseURL string `json:"base_url"`
50+
FileStoragePath string `json:"file_storage_path"`
51+
DatabaseDSN string `json:"database_dsn"`
52+
EnableHTTPS bool `json:"enable_https"`
53+
CertFile string `json:"cert_file"`
54+
KeyFile string `json:"key_file"`
55+
TrustedSubnet string `json:"trusted_subnet"`
56+
GRPCServerAddress string `json:"grpc_server_address"`
4957
}
5058

5159
// GetConfig возвращает экземпляр конфига
@@ -58,6 +66,8 @@ func GetConfig() Config {
5866
cfg.DefaultEnableHTTPS = false
5967
cfg.DefaultCertFile = ""
6068
cfg.DefaultKeyFile = ""
69+
cfg.DefaultTrustedSubnet = ""
70+
cfg.DefaultGRPCServerAddress = "localhost:50051"
6171
parseFlags(&cfg)
6272
parsEnv(&cfg)
6373
return cfg
@@ -106,7 +116,16 @@ func parseFlags(cfg *Config) {
106116
} else {
107117
cfg.KeyFile = cfg.DefaultKeyFile
108118
}
109-
119+
if f := flag.Lookup("t"); f == nil {
120+
flag.StringVar(&cfg.TrustedSubnet, "t", cfg.DefaultTrustedSubnet, "trusted subnet CIDR")
121+
} else {
122+
cfg.TrustedSubnet = cfg.DefaultTrustedSubnet
123+
}
124+
if f := flag.Lookup("grpc"); f == nil {
125+
flag.StringVar(&cfg.GRPCServerAddress, "grpc", cfg.DefaultGRPCServerAddress, "address and port for gRPC server")
126+
} else {
127+
cfg.GRPCServerAddress = cfg.DefaultGRPCServerAddress
128+
}
110129
flag.Parse()
111130
parseJSON(configPath, cfg)
112131
parsEnv(cfg)
@@ -140,6 +159,12 @@ func parsEnv(cfg *Config) {
140159
if len(envCfg.KeyFile) > 0 {
141160
cfg.KeyFile = envCfg.KeyFile
142161
}
162+
if len(envCfg.TrustedSubnet) > 0 {
163+
cfg.TrustedSubnet = envCfg.TrustedSubnet
164+
}
165+
if len(envCfg.GRPCServerAddress) > 0 {
166+
cfg.GRPCServerAddress = envCfg.GRPCServerAddress
167+
}
143168
}
144169

145170
func parseJSON(path string, cfg *Config) {
@@ -184,4 +209,10 @@ func parseJSON(path string, cfg *Config) {
184209
if cfg.KeyFile == cfg.DefaultKeyFile && jCfg.KeyFile != "" {
185210
cfg.KeyFile = jCfg.KeyFile
186211
}
212+
if cfg.TrustedSubnet == cfg.DefaultTrustedSubnet && jCfg.TrustedSubnet != "" {
213+
cfg.TrustedSubnet = jCfg.TrustedSubnet
214+
}
215+
if cfg.GRPCServerAddress == cfg.DefaultGRPCServerAddress && jCfg.GRPCServerAddress != "" {
216+
cfg.GRPCServerAddress = jCfg.GRPCServerAddress
217+
}
187218
}

0 commit comments

Comments
 (0)