-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathhooks.go
More file actions
139 lines (114 loc) · 6.22 KB
/
hooks.go
File metadata and controls
139 lines (114 loc) · 6.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package blossy
import (
"io"
"log/slog"
"net/url"
"github.com/pippellia-btc/blossom"
)
// Hooks of the blossom server, that the user of this framework can configure.
type Hooks struct {
Reject RejectHooks
On OnHooks
}
func DefaultHooks() Hooks {
return Hooks{
Reject: RejectHooks{},
On: NewOnHooks(),
}
}
// RejectHooks defines optional functions that can preemptively reject
// certain actions before they are processed by the server.
//
// Each function in a hook slice is evaluated in order. If any function
// returns a non-nil error, the corresponding input is immediately rejected.
//
// These hooks are useful for enforcing access policies, validating input,
// or applying rate limits before the server performs further processing.
type RejectHooks struct {
// Download is invoked before processing a GET /<hash>.<ext> request.
Download slice[func(r Request, hash blossom.Hash, ext string) *blossom.Error]
// Check is invoked before processing a HEAD /<hash>.<ext> request.
Check slice[func(r Request, hash blossom.Hash, ext string) *blossom.Error]
// Delete is invoked before processing a DELETE /<hash> request.
Delete slice[func(r Request, hash blossom.Hash) *blossom.Error]
// Upload is invoked when processing the HEAD /upload and before processing every PUT /upload request.
Upload slice[func(r Request, hints UploadHints) *blossom.Error]
// Mirror is invoked before processing a PUT /mirror request.
// The url has been previously validated to be a non-nil HTTPS URL with a valid blossom hash in its path.
Mirror slice[func(r Request, url *url.URL) *blossom.Error]
// Media is invoked when processing the HEAD /media and before processing every PUT /media request.
Media slice[func(r Request, hints UploadHints) *blossom.Error]
// Report is invoked before processing a PUT /report request.
Report slice[func(r Request, report Report) *blossom.Error]
}
// OnHooks defines functions invoked after specific blossom events occur.
// These hooks customize how the server reacts to requests such as upload or delete.
// Each function is called only after the corresponding input has passed all RejectHooks (if any).
//
// OnHooks are typically used to implement custom processing, persistence,
// logging, authorization, or other side effects.
type OnHooks struct {
// Download handles the core logic for GET /<sha256>.<ext> as per BUD-01.
// Use [Serve] to serve a [blossom.Blob] directly to the client or [Redirect] to redirect the client to another URL.
// Learn more here: https://github.com/hzrd149/blossom/blob/master/buds/01.md
Download func(r Request, hash blossom.Hash, ext string) (BlobDelivery, *blossom.Error)
// Check handles the core logic for HEAD /<sha256>.<ext> as per BUD-01.
// Use [Found] to return blob metadata directly, or [Redirect] to redirect the client to another URL.
// Learn more here: https://github.com/hzrd149/blossom/blob/master/buds/01.md
Check func(r Request, hash blossom.Hash, ext string) (MetaDelivery, *blossom.Error)
// Delete handles the core logic for DELETE /<sha256> as per BUD-02.
// This hook is optional. If not specified, the endpoint will return the http status code 501 (Not Implemented).
// Learn more here: https://github.com/hzrd149/blossom/blob/master/buds/02.md
Delete func(r Request, hash blossom.Hash) *blossom.Error
// Upload handles the core logic for PUT /upload as per BUD-02.
// If the returned blob descriptor has an empty URL, the server will automatically derive it from the
// hostname, the hash and the type of the blob.
// This hook is optional. If not specified, the endpoint will return the http status code 501 (Not Implemented).
// Learn more here: https://github.com/hzrd149/blossom/blob/master/buds/02.md
Upload func(r Request, hints UploadHints, data io.Reader) (blossom.BlobDescriptor, *blossom.Error)
// Mirror handles the core logic for PUT /mirror as per BUD-04.
// The url has been previously validated to be a non-nil HTTPS URL with a valid blossom hash in its path.
// If the returned blob descriptor has an empty URL, the server will automatically derive it from the
// hostname, the hash and the type of the blob.
// This hook is optional. If not specified, the endpoint will return the http status code 501 (Not Implemented).
// Learn more here: https://github.com/hzrd149/blossom/blob/master/buds/04.md
Mirror func(r Request, url *url.URL) (blossom.BlobDescriptor, *blossom.Error)
// Media handles the core logic for PUT /media as per BUD-05.
// If the returned blob descriptor has an empty URL, the server will automatically derive it from the
// hostname, the hash and the type of the blob.
// This hook is optional. If not specified, the endpoint will return the http status code 501 (Not Implemented).
// Learn more here: https://github.com/hzrd149/blossom/blob/master/buds/05.md
Media func(r Request, hints UploadHints, data io.Reader) (blossom.BlobDescriptor, *blossom.Error)
// Report handles the core logic for PUT /report as per BUD-09.
// This hook is optional. If not specified, the endpoint will return the http status code 501 (Not Implemented).
// Learn more here: https://github.com/hzrd149/blossom/blob/master/buds/09.md
Report func(r Request, report Report) *blossom.Error
}
func NewOnHooks() OnHooks {
return OnHooks{
Download: defaultDownload,
Check: defaultCheck,
}
}
func defaultDownload(r Request, hash blossom.Hash, ext string) (BlobDelivery, *blossom.Error) {
slog.Info("received GET request", "hash", hash.Hex(), "ext", ext, "ip", r.IP().Group())
return nil, blossom.ErrNotFound("The Download hook is not configured")
}
func defaultCheck(r Request, hash blossom.Hash, ext string) (MetaDelivery, *blossom.Error) {
slog.Info("received HEAD request", "hash", hash.Hex(), "ext", ext, "ip", r.IP().Group())
return nil, blossom.ErrNotFound("The Check hook is not configured")
}
// Slice is an internal type used to simplify registration of hooks.
type slice[T any] []T
// Append adds hooks to the end of the slice, in the provided order.
func (s *slice[T]) Append(hooks ...T) {
*s = append(*s, hooks...)
}
// Prepend adds hooks to the start of the slice, in the provided order.
func (s *slice[T]) Prepend(hooks ...T) {
*s = append(hooks, *s...)
}
// Clear resets the slice, removing all registered hooks.
func (s *slice[T]) Clear() {
*s = nil
}