Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3689685680 | ||
|
|
2525d929b1 |
No files matched your search
@@ -725,14 +725,11 @@ Key settings in more detail:
|
|||||||
- `signing_key` — HMAC secret for URL signatures
|
- `signing_key` — HMAC secret for URL signatures
|
||||||
- `db_url` — the SQLite database to open; omitted, it is
|
- `db_url` — the SQLite database to open; omitted, it is
|
||||||
`file:<state_dir>/state.sqlite3?_pragma=journal_mode(WAL)`, which keeps the
|
`file:<state_dir>/state.sqlite3?_pragma=journal_mode(WAL)`, which keeps the
|
||||||
database in WAL mode. `state_dir` is percent-encoded in it, so a `%`, `?` or
|
database in WAL mode. pixa opens one connection to it, so its own reads and
|
||||||
`#` in the name is written as `%25`, `%3F` or `%23`. For a `state_dir` of `/`
|
writes run one at a time. pixa adds `_pragma=busy_timeout(5000)` to any
|
||||||
or one starting with `//` it starts `file:////`, so SQLite does not read the
|
`db_url`, so a write that finds another program writing to the file waits up
|
||||||
first name in the path as a host. pixa opens one connection to it, so its own
|
to five seconds for it instead of failing. WAL mode comes only from the URL:
|
||||||
reads and writes run one at a time. pixa adds `_pragma=busy_timeout(5000)` to
|
keep `_pragma=journal_mode(WAL)` in one you set
|
||||||
any `db_url`, so a write that finds another program writing to the file waits
|
|
||||||
up to five seconds for it instead of failing. WAL mode comes only from the
|
|
||||||
URL: keep `_pragma=journal_mode(WAL)` in one you set
|
|
||||||
- `cache_max_bytes` — disk cache size limit in bytes; `0` disables the disk
|
- `cache_max_bytes` — disk cache size limit in bytes; `0` disables the disk
|
||||||
cache entirely; omitted defaults to 75% of the sum of the free space on the
|
cache entirely; omitted defaults to 75% of the sum of the free space on the
|
||||||
filesystem containing `<state_dir>/cache/` and the bytes of source and
|
filesystem containing `<state_dir>/cache/` and the bytes of source and
|
||||||
|
|||||||
@@ -36,14 +36,6 @@ P2: security: per-origin rate limiting
|
|||||||
URL kept the brackets when no port followed them, could not look the host up,
|
URL kept the brackets when no port followed them, could not look the host up,
|
||||||
and the request answered 500. It now checks the address without brackets, so a
|
and the request answered 500. It now checks the address without brackets, so a
|
||||||
private one is refused with 403, as with a port.
|
private one is refused with 403, as with a port.
|
||||||
- 2026-10-16 a `state_dir` whose name holds `%`, `?` or `#` keeps its database
|
|
||||||
at `state.sqlite3` in that directory (closes #290): the `db_url` derived from
|
|
||||||
it put the name into a `file:` URL as it was, so SQLite read `%41` as `A`, a
|
|
||||||
`?` or `#` ended the path early, and the journal mode parameter was lost. The
|
|
||||||
derived `db_url` is now built with Go's `net/url`, which writes those
|
|
||||||
characters as `%25`, `%3F` and `%23`. For a `state_dir` of `/` or one starting
|
|
||||||
with `//` it starts `file:////`, so SQLite no longer reads the first name in
|
|
||||||
the path as a host.
|
|
||||||
- 2026-10-16 a size with one side `0` keeps the side computed from the source's
|
- 2026-10-16 a size with one side `0` keeps the side computed from the source's
|
||||||
proportions at 1 or more (closes #283): it was rounded down, so for a thin
|
proportions at 1 or more (closes #283): it was rounded down, so for a thin
|
||||||
source it came out at 0, and libvips then made the size square. An 8192x1
|
source it came out at 0, and libvips then made the size square. An 8192x1
|
||||||
|
|||||||
@@ -338,7 +338,8 @@ func newFromSmartConfig(sc *smartconfig.Config) (*Config, error) {
|
|||||||
settingName(keyDBURL), errValueEmpty)
|
settingName(keyDBURL), errValueEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.DBURL = defaultDBURL(c.StateDir)
|
// The driver sets the journal mode only through a _pragma parameter.
|
||||||
|
c.DBURL = fmt.Sprintf("file:%s/state.sqlite3?_pragma=journal_mode(WAL)", c.StateDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
if loader.err != nil {
|
if loader.err != nil {
|
||||||
@@ -353,30 +354,6 @@ func newFromSmartConfig(sc *smartconfig.Config) (*Config, error) {
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultDBURL returns the db_url used when db_url is omitted: the file
|
|
||||||
// state.sqlite3 in stateDir, in WAL mode, which the driver sets only
|
|
||||||
// through a _pragma parameter. String() percent-encodes any %, ? or # in
|
|
||||||
// the directory name, which SQLite decodes back, so the URL names exactly
|
|
||||||
// this file. OmitHost writes file:/var/lib/pixa/... instead of
|
|
||||||
// file:///var/lib/pixa/..., and keeps a relative stateDir from being read
|
|
||||||
// as a host name. The path written into the URL is stateDir followed by
|
|
||||||
// /state.sqlite3; when it starts with //, as it does for a stateDir of /
|
|
||||||
// or one starting with //, the empty host is kept, so the URL starts
|
|
||||||
// file://// and SQLite does not read the first name in the path as a
|
|
||||||
// host.
|
|
||||||
func defaultDBURL(stateDir string) string {
|
|
||||||
dbPath := stateDir + "/state.sqlite3"
|
|
||||||
|
|
||||||
dbURL := url.URL{
|
|
||||||
Scheme: "file",
|
|
||||||
OmitHost: !strings.HasPrefix(dbPath, "//"),
|
|
||||||
Path: dbPath,
|
|
||||||
RawQuery: "_pragma=journal_mode(WAL)",
|
|
||||||
}
|
|
||||||
|
|
||||||
return dbURL.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateKnownKeys rejects configuration files containing keys the
|
// validateKnownKeys rejects configuration files containing keys the
|
||||||
// application does not understand, so typos fail at startup instead of
|
// application does not understand, so typos fail at startup instead of
|
||||||
// being silently ignored, and rejects keys that are explicitly set to
|
// being silently ignored, and rejects keys that are explicitly set to
|
||||||
|
|||||||
@@ -135,104 +135,6 @@ func TestDefaultDBURLOpensTheDatabaseInWALMode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestDerivedDBURLOpensStateDirHoldingURLCharacters opens the db_url
|
|
||||||
// derived from a state_dir whose name holds %41, ? and #, and checks that
|
|
||||||
// the database is state.sqlite3 in that directory, in WAL mode. Put into
|
|
||||||
// a file: URL as it is, the name would lose its meaning: %41 is read as
|
|
||||||
// A, and the path ends at the ?, which also loses the journal mode.
|
|
||||||
func TestDerivedDBURLOpensStateDirHoldingURLCharacters(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
stateDir := filepath.Join(t.TempDir(), "a%41b?c#d")
|
|
||||||
|
|
||||||
err := os.Mkdir(stateDir, 0o750)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create %q: %v", stateDir, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quoted so YAML takes the whole name as it is.
|
|
||||||
c, err := configFromYAML(t, signingKeyLine+"state_dir: '"+stateDir+"'\n")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("config with state_dir %q should be valid, got: %v", stateDir, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("state_dir %q gives db_url %q", stateDir, c.DBURL)
|
|
||||||
|
|
||||||
checkDBURLOpensFileInWALMode(t, c.DBURL, filepath.Join(stateDir, "state.sqlite3"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestDerivedDBURLOpensStateDirStartingWithTwoSlashes opens the db_url
|
|
||||||
// derived from a state_dir that starts with //, and checks that the
|
|
||||||
// database is state.sqlite3 in that directory, in WAL mode. Written right
|
|
||||||
// after file:, such a name gives a URL starting file://, and SQLite reads
|
|
||||||
// the first directory name as a host and refuses to open it.
|
|
||||||
func TestDerivedDBURLOpensStateDirStartingWithTwoSlashes(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
|
||||||
stateDir := "/" + dir
|
|
||||||
|
|
||||||
c, err := configFromYAML(t, signingKeyLine+"state_dir: "+stateDir+"\n")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("config with state_dir %q should be valid, got: %v", stateDir, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("state_dir %q gives db_url %q", stateDir, c.DBURL)
|
|
||||||
|
|
||||||
checkDBURLOpensFileInWALMode(t, c.DBURL, filepath.Join(dir, "state.sqlite3"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestDerivedDBURLForRootStateDir checks the db_url derived from a
|
|
||||||
// state_dir of /. The path written into the URL is then
|
|
||||||
// //state.sqlite3, and written right after file: it gives a URL starting
|
|
||||||
// file://, where SQLite reads state.sqlite3 as a host and refuses to open
|
|
||||||
// it. The test only checks the URL, since it cannot write to /.
|
|
||||||
func TestDerivedDBURLForRootStateDir(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
c, err := configFromYAML(t, signingKeyLine+"state_dir: /\n")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("config with state_dir / should be valid, got: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("state_dir / gives db_url %q", c.DBURL)
|
|
||||||
|
|
||||||
wantDBURL := "file:////state.sqlite3?_pragma=journal_mode(WAL)"
|
|
||||||
if c.DBURL != wantDBURL {
|
|
||||||
t.Errorf("DBURL = %q, want %q", c.DBURL, wantDBURL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkDBURLOpensFileInWALMode opens dbURL with the SQLite driver pixad
|
|
||||||
// uses, and checks that the database is in WAL mode and is the file
|
|
||||||
// dbFile.
|
|
||||||
func checkDBURLOpensFileInWALMode(t *testing.T, dbURL, dbFile string) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
db, err := sql.Open("sqlite", dbURL)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to open %q: %v", dbURL, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Cleanup(func() { _ = db.Close() })
|
|
||||||
|
|
||||||
var journalMode string
|
|
||||||
|
|
||||||
err = db.QueryRowContext(t.Context(), "PRAGMA journal_mode").Scan(&journalMode)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to read the journal mode of %q: %v", dbURL, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if journalMode != "wal" {
|
|
||||||
t.Errorf("journal mode of %q = %q, want wal", dbURL, journalMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = os.Stat(dbFile)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("opening %q did not create %q: %v", dbURL, dbFile, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExplicitValidValuesAreUsed(t *testing.T) {
|
func TestExplicitValidValuesAreUsed(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user