Skip to content

Commit 6a98210

Browse files
committed
feat: add httpsrv libary
1 parent 429eb7c commit 6a98210

13 files changed

+2080
-0
lines changed

pkg/httpsrv/README.md

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
## English | [中文](readme-cn.md)
2+
3+
## HTTP Server
4+
5+
`httpsrv` is a convenient wrapper library for Go's `net/http`, designed to simplify and standardize the process of starting HTTP and HTTPS servers. It supports multiple TLS certificate management modes, including self-signed certificates, Let's Encrypt, external files, and remote APIs, allowing you to quickly launch a robust web server with simple configuration.
6+
7+
<br>
8+
9+
### Features
10+
11+
- **Multiple Operating Modes**:
12+
- **HTTP**: Quickly start a standard HTTP service.
13+
- **Self-Signed**: Automatically generate and manage self-signed TLS certificates for local development environments.
14+
- **Let's Encrypt**: Integrates with `autocert` to automatically obtain and renew Let's Encrypt certificates.
15+
- **External**: Use your existing certificate and private key files.
16+
- **Remote API**: Dynamically fetch certificates from a specified API endpoint.
17+
- **Graceful Shutdown**: Built-in `Shutdown` method for easy implementation of a graceful server shutdown.
18+
- **Simple Configuration**: Provides a clear and flexible configuration method through chain calls and the option pattern.
19+
- **High Extensibility**: The `TLSer` interface allows you to easily implement custom certificate management strategies, such as fetching certificates from Etcd, Consul, etc.
20+
21+
<br>
22+
23+
### Examples of Usage
24+
25+
Below are usage examples for different modes.
26+
27+
#### 1. Start a standard HTTP server
28+
29+
This is the simplest mode, requiring no TLS configuration.
30+
31+
```go
32+
package main
33+
34+
import (
35+
"fmt"
36+
"net/http"
37+
38+
"github.com/go-dev-frame/sponge/pkg/httpsrv"
39+
)
40+
41+
func main() {
42+
// Create an HTTP Mux
43+
mux := http.NewServeMux()
44+
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
45+
fmt.Fprintln(w, "Hello, HTTP World!")
46+
})
47+
48+
// Configure http.Server
49+
httpServer := &http.Server{
50+
Addr: ":8080",
51+
Handler: mux,
52+
}
53+
54+
// Create and run the service
55+
fmt.Println("HTTP server listening on :8080")
56+
server := httpsrv.New(httpServer)
57+
if err := server.Run(); err != nil {
58+
fmt.Printf("Server error: %v\n", err)
59+
}
60+
}
61+
```
62+
63+
<br>
64+
65+
#### 2. HTTPS - Self-Signed Certificate (for development)
66+
67+
This mode automatically generates `cert.pem` and `key.pem` files, making it ideal for local development and testing.
68+
69+
```go
70+
package main
71+
72+
import (
73+
"fmt"
74+
"net/http"
75+
76+
"github.com/go-dev-frame/sponge/pkg/httpsrv"
77+
)
78+
79+
func main() {
80+
// Create an HTTP Mux
81+
mux := http.NewServeMux()
82+
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
83+
fmt.Fprintln(w, "Hello, HTTP World!")
84+
})
85+
86+
// Configure http.Server
87+
httpServer := &http.Server{
88+
Addr: ":8443",
89+
Handler: mux,
90+
}
91+
92+
// Configure self-signed mode
93+
tlsConfig := httpsrv.NewTLSSelfSignedConfig(
94+
// Optional: Custom certificate storage directory
95+
//httpsrv.WithTLSSelfSignedCacheDir("certs/self-signed"),
96+
// Optional: Custom certificate validity period (in days)
97+
//httpsrv.WithTLSSelfSignedExpirationDays(365),
98+
// Optional: Add other IPs to the certificate
99+
//httpsrv.WithTLSSelfSignedWanIPs("192.168.1.100"),
100+
)
101+
102+
// Create and run the service
103+
fmt.Println("HTTP server listening on :8443")
104+
server := httpsrv.New(httpServer, tlsConfig)
105+
if err := server.Run(); err != nil {
106+
fmt.Printf("Server error: %v\n", err)
107+
}
108+
}
109+
```
110+
111+
<br>
112+
113+
#### 3. HTTPS - Let's Encrypt (for production)
114+
115+
This mode automatically obtains certificates from Let's Encrypt and handles renewal.
116+
117+
```go
118+
package main
119+
120+
import (
121+
"fmt"
122+
"net/http"
123+
124+
"github.com/go-dev-frame/sponge/pkg/httpsrv"
125+
)
126+
127+
func main() {
128+
// Create an HTTP Mux
129+
mux := http.NewServeMux()
130+
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
131+
fmt.Fprintln(w, "Hello, HTTP World!")
132+
})
133+
134+
httpServer := &http.Server{
135+
Addr: ":443",
136+
Handler: mux,
137+
}
138+
139+
// Configure Let's Encrypt mode
140+
tlsConfig := httpsrv.NewTLSEAutoEncryptConfig(
141+
"your-domain.com", // Your domain
142+
"your-email@example.com", // Your email for Let's Encrypt notifications
143+
// Optional: Enable HTTP -> HTTPS automatic redirection (listens on :80 by default)
144+
//httpsrv.WithTLSEncryptEnableRedirect(),
145+
// Optional: Custom certificate cache directory
146+
//httpsrv.WithTLSEncryptCacheDir("certs/encrypt"),
147+
)
148+
149+
fmt.Println("HTTP server listening on :443")
150+
server := httpsrv.New(httpServer, tlsConfig)
151+
if err := server.Run(); err != nil {
152+
fmt.Printf("Server error: %v\n", err)
153+
}
154+
}
155+
```
156+
157+
<br>
158+
159+
#### 4. HTTPS - Using External Certificate Files
160+
161+
If you already have your own certificate and private key files, you can use this mode.
162+
163+
```go
164+
package main
165+
166+
import (
167+
"fmt"
168+
"net/http"
169+
170+
"github.com/go-dev-frame/sponge/pkg/httpsrv"
171+
)
172+
173+
func main() {
174+
// Create an HTTP Mux
175+
mux := http.NewServeMux()
176+
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
177+
fmt.Fprintln(w, "Hello, HTTP World!")
178+
})
179+
180+
httpServer := &http.Server{
181+
Addr: ":8443",
182+
Handler: mux,
183+
}
184+
185+
// Configure external file mode
186+
tlsConfig := httpsrv.NewTLSExternalConfig(
187+
"/path/to/your/cert.pem",
188+
"/path/to/your/key.pem",
189+
)
190+
191+
fmt.Println("HTTP server listening on :8443")
192+
server := httpsrv.New(httpServer, tlsConfig)
193+
if err := server.Run(); err != nil {
194+
fmt.Printf("Server error: %v\n", err)
195+
}
196+
}
197+
```
198+
199+
<br>
200+
201+
#### 5. HTTPS - Fetching Certificates from a Remote API
202+
203+
This mode allows you to dynamically pull the certificate and private key from a URL. The API should return a JSON object containing `cert_file` and `key_file` fields, with their values being Base64-encoded PEM data, as shown below:
204+
205+
```json
206+
{
207+
"cert_file": "-----BEGIN CERTIFICATE-----\nCERTIFICATE_DATA\n-----END CERTIFICATE-----",
208+
"key_file": "-----BEGIN PRIVATE KEY-----\nPRIVATE_KEY_DATA\n-----END PRIVATE KEY-----"
209+
}
210+
```
211+
212+
```go
213+
package main
214+
215+
import (
216+
"fmt"
217+
"github.com/go-dev-frame/sponge/pkg/httpsrv"
218+
"net/http"
219+
)
220+
221+
func main() {
222+
// Create an HTTP Mux
223+
mux := http.NewServeMux()
224+
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
225+
fmt.Fprintln(w, "Hello, HTTP World!")
226+
})
227+
228+
httpServer := &http.Server{
229+
Addr: ":8443",
230+
Handler: mux,
231+
}
232+
233+
// Configure remote API mode
234+
tlsConfig := httpsrv.NewTLSRemoteAPIConfig(
235+
"https://your-api-endpoint.com/certs",
236+
// Optional: Set request headers for authentication, etc.
237+
//httpsrv.WithTLSRemoteAPIHeaders(map[string]string{"Authorization": "Bearer your-token"}),
238+
// Optional: Set http.Client request timeout
239+
//httpsrv.WithTLSRemoteAPITimeout(10*time.Second),
240+
)
241+
242+
fmt.Println("HTTP server listening on :8443")
243+
server := httpsrv.New(httpServer, tlsConfig)
244+
if err := server.Run(); err != nil {
245+
fmt.Printf("Server error: %v\n", err)
246+
}
247+
}
248+
```

pkg/httpsrv/http.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package httpsrv
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"net/http"
8+
)
9+
10+
// TLSer abstract different TLS operation schemes
11+
type TLSer interface {
12+
Validate() error
13+
Run(server *http.Server) error
14+
}
15+
16+
// Server TLS server.
17+
type Server struct {
18+
scheme string // e.g. http or https.
19+
server *http.Server // server is the HTTP server instance.
20+
21+
tlser TLSer
22+
}
23+
24+
// New returns a new Server with TLSer injected.
25+
func New(server *http.Server, tlser ...TLSer) *Server {
26+
var tlsMode TLSer
27+
if len(tlser) > 0 {
28+
tlsMode = tlser[0]
29+
}
30+
31+
var scheme = "https"
32+
if tlsMode == nil {
33+
scheme = "http"
34+
}
35+
36+
return &Server{
37+
scheme: scheme,
38+
server: server,
39+
tlser: tlsMode,
40+
}
41+
}
42+
43+
func (s *Server) validate() error {
44+
if s.server == nil {
45+
return errors.New("server must be specified")
46+
}
47+
if s.tlser != nil {
48+
return s.tlser.Validate()
49+
}
50+
return nil
51+
}
52+
53+
// Run starts the server according to the provided configuration.
54+
func (s *Server) Run() error {
55+
if err := s.validate(); err != nil {
56+
return err
57+
}
58+
59+
// no TLS mode specified, run in http mode.
60+
if s.tlser == nil {
61+
return s.runHTTP()
62+
}
63+
64+
return s.tlser.Run(s.server)
65+
}
66+
67+
// Shutdown gracefully shuts down the server and releases resources.
68+
func (s *Server) Shutdown(ctx context.Context) error {
69+
if s.server == nil {
70+
return nil
71+
}
72+
return s.server.Shutdown(ctx)
73+
}
74+
75+
// runHTTP starts the server in http mode, without TLS.
76+
func (s *Server) runHTTP() error {
77+
if err := s.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
78+
return fmt.Errorf("[http server] listen and serve error: %v", err)
79+
}
80+
return nil
81+
}
82+
83+
// Scheme returns the scheme of the server, e.g. http or https.
84+
func (s *Server) Scheme() string {
85+
return s.scheme
86+
}

0 commit comments

Comments
 (0)