Skip to content

Commit 811bd0b

Browse files
add env and config file support
1 parent 869a25c commit 811bd0b

6 files changed

Lines changed: 142 additions & 101 deletions

File tree

cmd/sqlite-http-proxy/main.go

Lines changed: 72 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,53 @@ import (
44
"crypto/tls"
55
"crypto/x509"
66
"database/sql"
7-
"flag"
87
"fmt"
98
"log"
109
"net"
1110
"net/http"
1211
"os"
12+
"path/filepath"
1313
"strings"
1414

1515
"github.com/elazarl/goproxy"
1616
_ "github.com/mattn/go-sqlite3"
17+
"github.com/peterbourgon/ff/v4"
18+
"github.com/peterbourgon/ff/v4/ffhelp"
1719

1820
"github.com/walterwanderley/sqlite-http-cache/db"
1921
)
2022

21-
var (
22-
port uint
23-
dbParams string
24-
allowHTTP2 bool
25-
verbose bool
26-
27-
ttl uint
28-
29-
caCert string
30-
caCertKey string
23+
func main() {
24+
fs := ff.NewFlagSet("sqlite-http-proxy")
25+
port := fs.Uint('p', "port", 8080, "Server port")
26+
dbParams := fs.StringLong("db-params", "_journal=WAL&_sync=NORMAL&_timeout=5000&_txlock=immediate", "Database connection params")
27+
verbose := fs.Bool('v', "verbose", "Enable verbose mode")
28+
allowHTTP2 := fs.BoolLong("h2", "Allow HTTP2")
29+
ttl := fs.UintLong("ttl", 0, "Time to Live in seconds (0 is infinite time)")
30+
responseTables := fs.StringListLong("response-table", "List of database tables used to store response data")
31+
forceCreateTables := fs.BoolLong("force-create-tables", "Force create response tables if not exists")
32+
caCert := fs.StringLong("ca-cert", "", "Path to CA Certificate file (required to HTTPS proxy)")
33+
caCertKey := fs.StringLong("ca-cert-key", "", "Path to CA Certificate Key file (required to HTTPS proxy)")
34+
readOnly := fs.BoolLong("ro", "Read Only mode. Do not store new HTTP responses")
35+
_ = fs.String('c', "config", "", "config file (optional)")
36+
37+
if err := ff.Parse(fs, os.Args[1:],
38+
ff.WithEnvVarPrefix("SQLITE_HTTP_PROXY"),
39+
ff.WithConfigFileFlag("config"),
40+
ff.WithConfigFileParser(ff.PlainParser),
41+
); err != nil {
42+
fmt.Printf("%s\n", ffhelp.Flags(fs))
43+
fmt.Printf("err=%v\n", err)
44+
return
45+
}
3146

32-
responseTables string
33-
forceCreateTables bool
34-
readOnly bool
35-
)
47+
if len(fs.GetArgs()) == 0 {
48+
log.Fatalf("Usage: %s <FLAGS> [DatabasePath1] [DatabasePathN\n\nExample:\n\t%s example.db example2.db example3.db\n", os.Args[0], os.Args[0])
49+
}
3650

37-
func main() {
38-
flag.UintVar(&port, "p", 8080, "Server port")
39-
flag.StringVar(&dbParams, "db-params", "_journal=WAL&_sync=NORMAL&_timeout=5000&_txlock=immediate", "Database connection params")
40-
flag.BoolVar(&verbose, "v", false, "Enable verbose mode")
41-
flag.BoolVar(&allowHTTP2, "h2", false, "Allow HTTP2")
42-
flag.UintVar(&ttl, "ttl", 0, "Time to Live in seconds (0 is infinite time)")
43-
flag.StringVar(&responseTables, "response-tables", "", "Comma separated list of database tables used to store response data")
44-
flag.BoolVar(&forceCreateTables, "force-create-tables", false, "Force create response tables if not exists")
45-
flag.StringVar(&caCert, "ca-cert", "", "Path to CA Certificate file (required to HTTPS proxy)")
46-
flag.StringVar(&caCertKey, "ca-cert-key", "", "Path to CA Certificate Key file (required to HTTPS proxy)")
47-
flag.BoolVar(&readOnly, "ro", false, "Read Only mode. Do not store new HTTP responses")
48-
flag.Parse()
49-
50-
if len(flag.Args()) == 0 {
51-
log.Fatalf("Usage: %s <flags> [DatabasePath1] [DatabasePathN\n\nExample:\n\t%s example.db example2.db example3.db\n", os.Args[0], os.Args[0])
51+
if *verbose {
52+
fmt.Printf("Using options: port=%d db-params=%s, h2=%v, ttl=%d, response-tables=%v, force-create-tables=%v, ca-cert=%s, ca-cert-key=%s, read-only=%v\n",
53+
*port, *dbParams, *allowHTTP2, *ttl, *responseTables, *forceCreateTables, *caCert, *caCertKey, *readOnly)
5254
}
5355

5456
dbs := make([]*sql.DB, 0)
@@ -57,14 +59,31 @@ func main() {
5759
tableList []string
5860
err error
5961
)
60-
for _, file := range flag.Args() {
61-
var dsn string
62-
if file == ":memory:" {
63-
dsn = file + "?cache=shared"
64-
} else {
65-
dsn = fmt.Sprintf("file:%s?%s", file, dbParams)
62+
63+
dsnList := make([]string, 0)
64+
for _, pattern := range fs.GetArgs() {
65+
if pattern == ":memory:" {
66+
dsn := pattern + "?cache=shared"
67+
dsnList = append(dsnList, dsn)
68+
continue
6669
}
70+
matches, err := filepath.Glob(pattern)
71+
if err != nil {
72+
log.Fatal(err)
73+
}
74+
75+
for _, file := range matches {
76+
dsn := fmt.Sprintf("file:%s?%s", file, *dbParams)
77+
dsnList = append(dsnList, dsn)
78+
}
79+
if len(matches) == 0 && !strings.Contains(pattern, "*") {
80+
dsn := fmt.Sprintf("file:%s?%s", pattern, *dbParams)
81+
dsnList = append(dsnList, dsn)
82+
}
83+
84+
}
6785

86+
for _, dsn := range dsnList {
6887
sqlDB, err := sql.Open("sqlite3", dsn)
6988
if err != nil {
7089
log.Fatalf("open db error: %v", err)
@@ -73,17 +92,17 @@ func main() {
7392

7493
dbs = append(dbs, sqlDB)
7594

76-
if responseTables == "" {
95+
if responseTables == nil || len(*responseTables) == 0 {
7796
tableList, err = db.ResponseTables(sqlDB)
7897
if err != nil {
7998
log.Fatalf("discovery response tables: %v", err)
8099
}
81100
} else {
82-
tableList = strings.Split(responseTables, ",")
83-
if forceCreateTables {
101+
tableList = *responseTables
102+
if *forceCreateTables {
84103
err := db.CreateResponseTables(sqlDB, tableList...)
85104
if err != nil {
86-
log.Fatalf("force create tables: %v", err)
105+
log.Fatalf("force create tables on DB %q: %v", dsn, err)
87106
}
88107
}
89108
}
@@ -102,12 +121,12 @@ func main() {
102121
defer repository.Close()
103122

104123
proxy := goproxy.NewProxyHttpServer()
105-
proxy.Verbose = verbose
106-
proxy.AllowHTTP2 = allowHTTP2
124+
proxy.Verbose = *verbose
125+
proxy.AllowHTTP2 = *allowHTTP2
107126

108-
if caCert != "" && caCertKey != "" {
127+
if *caCert != "" && *caCertKey != "" {
109128
proxy.Logger.Printf("INFO: Starting HTTP/HTTPS Proxy...")
110-
cert, err := parseCA([]byte(caCert), []byte(caCertKey))
129+
cert, err := parseCA([]byte(*caCert), []byte(*caCertKey))
111130
if err != nil {
112131
log.Fatal(err)
113132
}
@@ -122,20 +141,24 @@ func main() {
122141
}
123142

124143
proxy.OnRequest().Do(&requestHandler{
125-
querier: repository,
144+
querier: repository,
145+
verbose: *verbose,
146+
ttl: *ttl,
147+
readOnly: *readOnly,
126148
})
127-
if !readOnly {
149+
if !*readOnly {
128150
proxy.OnResponse().Do(&responseHandler{
129-
writer: repository,
151+
writer: repository,
152+
verbose: *verbose,
130153
})
131154
}
132155

133-
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
156+
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
134157
if err != nil {
135158
log.Fatalf("cannot open port %d: %v", port, err)
136159
}
137160

138-
proxy.Logger.Printf("SQLite-HTTP-Proxy listening port=%d", port)
161+
proxy.Logger.Printf("SQLite-HTTP-Proxy listening port=%d", *port)
139162
log.Fatal(http.Serve(lis, proxy))
140163
}
141164

cmd/sqlite-http-proxy/request.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ type requestQuerier interface {
1919
}
2020

2121
type requestHandler struct {
22-
querier requestQuerier
22+
verbose bool
23+
ttl uint
24+
readOnly bool
25+
querier requestQuerier
2326
}
2427

2528
func (h *requestHandler) Handle(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
@@ -38,12 +41,12 @@ func (h *requestHandler) Handle(r *http.Request, ctx *goproxy.ProxyCtx) (*http.R
3841
return r, nil
3942
}
4043

41-
if !readOnly && ttl > 0 && uint(time.Since(resp.Timestamp).Seconds()) > ttl {
44+
if !h.readOnly && h.ttl > 0 && uint(time.Since(resp.Timestamp).Seconds()) > h.ttl {
4245
// data is too old, tell the responseHandler to save the new data
4346
ctx.UserData = fmt.Sprintf("%s:%d", resp.TableName, resp.DatabaseID)
4447
return r, nil
4548
}
46-
if verbose {
49+
if h.verbose {
4750
slog.Info("serving from database", "url", url, "status", resp.Status, "timestamp", resp.Timestamp.Format(time.RFC3339))
4851
}
4952

cmd/sqlite-http-proxy/response.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ type responseWriter interface {
1616
}
1717

1818
type responseHandler struct {
19-
writer responseWriter
19+
writer responseWriter
20+
verbose bool
2021
}
2122

2223
func (h *responseHandler) Handle(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
2324
if ctx.UserData != nil {
24-
if verbose {
25+
if h.verbose {
2526
slog.Info("recording response", "url", ctx.Req.URL.String(), "status", resp.StatusCode)
2627
}
2728
responseDB, err := db.HttpToResponse(resp)

0 commit comments

Comments
 (0)