Private Server: SSL certificate management can be implemented through frontend load balancers like Nginx, which is more efficient in terms of performance.
Cloud Service Providers: Most cloud service providers' load balancing services have built-in SSL certificate management.
Example Nginx configuration:
server {
listen 443 ssl;
server_name your-domain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Implement through frontend load balancers like Nginx. This approach is definitely more efficient than built-in httpsrv implementation, also conforms to operational management convenience. More importantly, it maintains httpsrv's positioning as purely concise.
Example of Nginx enabling gzip compression:
http {
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript
application/x-javascript application/xml+rss
application/json application/javascript;
}When using reverse proxies like Nginx, you need to get the real IP from HTTP headers:
func (c BaseController) GetClientIP() string {
// Check X-Real-IP header
if ip := c.Request.Header.Get("X-Real-IP"); ip != "" {
return ip
}
// Check X-Forwarded-For header
if ip := c.Request.Header.Get("X-Forwarded-For"); ip != "" {
return ip
}
// Default to RemoteAddr
return c.Request.RemoteAddr
}httpsrv doesn't have built-in CORS support. You can implement it through Filter:
package main
import (
"github.com/hooto/httpsrv"
)
func CorsFilter(c *httpsrv.Controller, fc []httpsrv.Filter) {
// Set CORS headers
c.Response.Out.Header().Set("Access-Control-Allow-Origin", "*")
c.Response.Out.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Response.Out.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
c.Response.Out.Header().Set("Access-Control-Max-Age", "86400")
// Handle OPTIONS preflight request
if c.Request.Method == "OPTIONS" {
c.Response.Out.WriteHeader(200)
return
}
// Continue executing subsequent Filters
fc[0](c, fc[1:])
}
func main() {
// Register CORS Filter
httpsrv.GlobalService.Filters = []httpsrv.Filter{
CorsFilter,
}
// Register module
httpsrv.GlobalService.HandleModule("/", NewModule())
httpsrv.GlobalService.Start()
}httpsrv supports standard multipart form data upload:
type FileUpload struct {
*httpsrv.Controller
}
func (c FileUpload) UploadAction() {
// Parse form
if err := c.Request.ParseMultipartForm(32 << 20); err != nil {
c.RenderError(400, "Form parsing failed")
return
}
file, header, err := c.Request.FormFile("file")
if err != nil {
c.RenderError(400, "Failed to get file")
return
}
defer file.Close()
// Save file
// Here uses example logic, actual project should save to specified directory
// File information can be obtained through header
filename := header.Filename
filesize := header.Size
contentType := header.Header.Get("Content-Type")
c.RenderJson(map[string]interface{}{
"status": "success",
"filename": filename,
"size": filesize,
"type": contentType,
})
}Frontend HTML form:
<form action="/file-upload/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="file" />
<button type="submit">Upload</button>
</form>httpsrv has built-in Session support:
type Auth struct {
*httpsrv.Controller
}
func (c Auth) LoginAction() {
username := c.Params.Value("username")
password := c.Params.Value("password")
// Verify username and password
if username == "admin" && password == "password" {
// Set Session
c.Session.Set("user_id", "12345")
c.Session.Set("username", username)
c.RenderJson(map[string]string{
"status": "success",
"message": "Login successful",
})
} else {
c.RenderError(401, "Invalid username or password")
}
}
func (c Auth) ProfileAction() {
// Get Session
userId := c.Session.Get("user_id")
username := c.Session.Get("username")
if userId == "" {
c.RenderError(401, "Not logged in")
return
}
c.RenderJson(map[string]interface{}{
"user_id": userId,
"username": username,
})
}
func (c Auth) LogoutAction() {
// Clear Session
c.Session.Clear()
c.RenderJson(map[string]string{
"status": "success",
"message": "Logout successful",
})
}httpsrv supports multiple languages, can be implemented by setting Locale:
type I18nDemo struct {
*httpsrv.Controller
}
func (c I18nDemo) IndexAction() {
// Get language setting from request
// Can get from URL parameters, Cookie or HTTP headers
lang := c.Params.Value("lang")
if lang == "" {
lang = c.Request.Header.Get("Accept-Language")
}
messages := map[string]map[string]string{
"zh": {
"title": "欢迎",
"hello": "你好,世界",
},
"en": {
"title": "Welcome",
"hello": "Hello, World",
},
}
if msg, ok := messages[lang]; ok {
c.RenderJson(msg)
} else {
c.RenderJson(messages["zh"])
}
}It's recommended to implement versioning through Module paths:
func main() {
// v1 API
httpsrv.GlobalService.HandleModule("/api/v1", NewApiV1Module())
// v2 API
httpsrv.GlobalService.HandleModule("/api/v2", NewApiV2Module())
httpsrv.GlobalService.Start()
}
// v1 module
func NewApiV1Module() *httpsrv.Module {
mod := httpsrv.NewModule()
mod.RegisterController(new(ApiV1Controller))
return mod
}
// v2 module
func NewApiV2Module() *httpsrv.Module {
mod := httpsrv.NewModule()
mod.RegisterController(new(ApiV2Controller))
return mod
}Access paths:
- v1 API:
/api/v1/controller/action - v2 API:
/api/v2/controller/action
Use signal handling to implement graceful shutdown:
package main
import (
"os"
"os/signal"
"syscall"
"github.com/hooto/httpsrv"
)
func main() {
// Start service
go func() {
httpsrv.GlobalService.Start()
}()
// Wait for interrupt signal
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
// Execute cleanup operations
// Close database connections
// Save cache, etc.
// httpsrv will handle shutdown automatically
}httpsrv supports multiple configuration file formats, JSON or YAML is recommended:
package main
import (
"encoding/json"
"io/ioutil"
"os"
"github.com/hooto/httpsrv"
)
type Config struct {
HttpPort int `json:"http_port"`
DatabaseURL string `json:"database_url"`
RedisURL string `json:"redis_url"`
LogLevel string `json:"log_level"`
}
func loadConfig() (*Config, error) {
configFile := "config.json"
if os.Getenv("APP_ENV") == "production" {
configFile = "config.prod.json"
}
data, err := ioutil.ReadFile(configFile)
if err != nil {
return nil, err
}
var config Config
if err := json.Unmarshal(data, &config); err != nil {
return nil, err
}
return &config, nil
}
func main() {
config, err := loadConfig()
if err != nil {
panic(err)
}
httpsrv.GlobalService.Config.HttpPort = uint16(config.HttpPort)
// Use other configuration items to connect to database, Redis, etc.
httpsrv.GlobalService.Start()
}Create a dedicated health check module:
type HealthCheck struct {
*httpsrv.Controller
}
func (c HealthCheck) IndexAction() {
// Check database connection
dbOK := checkDatabase()
// Check Redis connection
redisOK := checkRedis()
status := "ok"
if !dbOK || !redisOK {
status = "error"
c.Response.Out.WriteHeader(503)
}
c.RenderJson(map[string]interface{}{
"status": status,
"database": dbOK,
"redis": redisOK,
})
}
func NewHealthCheckModule() *httpsrv.Module {
mod := httpsrv.NewModule()
mod.RegisterController(new(HealthCheck))
return mod
}
func main() {
// Register health check module
httpsrv.GlobalService.HandleModule("/health", NewHealthCheckModule())
// Register other modules
httpsrv.GlobalService.HandleModule("/", NewMainModule())
httpsrv.GlobalService.Start()
}It's recommended to use dedicated static file servers:
func main() {
// Use CDN or dedicated static file service
// httpsrv only handles dynamic content
httpsrv.GlobalService.HandleModule("/api", NewApiModule())
httpsrv.GlobalService.Start()
}Or use Nginx to serve static files:
server {
location /static/ {
root /path/to/static/files;
expires 30d;
add_header Cache-Control "public, immutable";
}
location / {
proxy_pass http://localhost:8080;
}
}httpsrv doesn't have built-in caching. Redis or in-memory cache is recommended:
import "github.com/lynkdb/redisgo"
var redisClient *redisgo.RedisClient
func main() {
// Initialize Redis client
redisClient = redisgo.NewClient(&redisgo.Config{
Addr: "localhost:6379",
})
httpsrv.GlobalService.Start()
}
type CacheDemo struct {
*httpsrv.Controller
}
func (c CacheDemo) GetDataAction() {
key := "data_key"
// Try to get from cache
data, err := redisClient.Get(key).Result()
if err == nil && data != "" {
c.Response.Out.Header().Set("X-Cache", "HIT")
c.RenderString(data)
return
}
// Cache miss, get from database
dbData := fetchDataFromDB()
// Set cache
redisClient.Set(key, dbData, 5*time.Minute)
c.Response.Out.Header().Set("X-Cache", "MISS")
c.RenderString(dbData)
}Make sure Action method ends with Action():
// ❌ Wrong
func (c User) Login() {
// ...
}
// ✅ Correct
func (c User) LoginAction() {
// ...
}Check if static file path configuration is correct:
func NewModule() *httpsrv.Module {
mod := httpsrv.NewModule()
// Make sure local file path exists
mod.RegisterFileServer("/assets", "./static", nil)
return mod
}
// Access URL: /assets/css/style.css
// Corresponds to local file: ./static/css/style.cssCheck if template path is set correctly:
func NewModule() *httpsrv.Module {
mod := httpsrv.NewModule("ui")
// Make sure template path is set correctly
mod.SetTemplatePath("./views/ui")
return mod
}
// If Controller is User and Action is Index
// System will look for: ./views/ui/user/index.tplhttpsrv as a personal project has been applied to certain industry software since 2013:
- SysInner.com uses httpsrv to implement all API and Web modules
- Provides API implementation in a certain hundred-billion-level advertising system, stable and efficient in handling data collection and index queries
- Provides API implementation in a certain PB-level object storage service, with stable and reliable stability and memory usage in high-traffic IO read/write scenarios
- Provides API implementations including user systems, message systems, interaction systems, etc., in multiple satellite TV gala live interaction events, running efficiently without exceptions in billions of PV and millions of UV scenarios, with comprehensive response time within 50 milliseconds (Environment note: 60 interface server 2-core cloud hosts, 6 database 4-core high-speed SSD cloud hosts, cluster load peak less than 5%, traffic implemented through cloud service provider load balancer export)
If the above information doesn't help, you can send the problem to evorui at gmail dot com.
Pull Requests or Bug reports are welcome, please visit: https://github.com/hooto/httpsrv
go get -u github.com/hooto/httpsrv