commit 78a90d5d629d3aa8ca85d6c5aa4ee6dd5f76642e
parent ebb1f9a89b279fd6fdf0297c9c52cdf3a08ba1cf
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date: Tue, 22 Apr 2025 14:05:37 +0200
make body size configurable
Diffstat:
| M | pkg/rest/mailbox.go | | | 111 | ++++++++++++++++++++++++++++++++++--------------------------------------------- |
1 file changed, 48 insertions(+), 63 deletions(-)
diff --git a/pkg/rest/mailbox.go b/pkg/rest/mailbox.go
@@ -28,7 +28,7 @@ import (
"log"
"net/http"
"os"
- "strings"
+ "strings"
"time"
gnunetutil "git.gnunet.org/gnunet-go.git/pkg/util"
@@ -42,6 +42,8 @@ import (
talerutil "taler.net/taler-go.git/pkg/util"
)
+type TalerMailboxBoxySize int
+
// Mailbox is the primary object of the Mailbox service
type Mailbox struct {
@@ -54,27 +56,13 @@ type Mailbox struct {
// Our configuration from the config.json
Cfg *ini.File
+ // Fixed size of message bodies
+ MessageBodyBytes int64 `json:"message_body_bytes"`
+
// Merchant object
Merchant merchant.Merchant
}
-type IdentityMessage struct {
- // Internal ID primary key (GORM thingy).
- // Not returned in JSON
- ID int64 `json:"-"`
-
- // Public DH key used to encrypt the body. Must be fresh
- // and only used once (ephemeral).
- // FIXME: Do we want to use HPKE DHKEM X25519 + ChaChaPoly?
- EphemeralKey string `json:"ephemeral_key"`
-
- // Encrypted message. Must be exactly 256-32 bytes long.
- Body string
-
- // Order ID, if the client recently paid for this message.
- //order_id?: string;
-}
-
// VersionResponse is the JSON response of the /config enpoint
type VersionResponse struct {
// libtool-style representation of the Merchant protocol version, see
@@ -88,9 +76,12 @@ type VersionResponse struct {
// fee for one month of registration
MessageFee string `json:"message_fee"`
+ // Fixed size of message bodies
+ MessageBodyBytes int64 `json:"message_body_bytes"`
+
// How long will the service store a message
// before giving up
- DeliveryPeriod uint64 `josn:"delivery_period"`
+ DeliveryPeriod uint64 `json:"delivery_period"`
}
// MessageDeletionRequest is used to request the deletion of already received
@@ -123,7 +114,7 @@ type MailboxRateLimitedResponse struct {
Hint string `json:"hint"`
}
-type inboxEntry struct {
+type InboxEntry struct {
// ORM
gorm.Model `json:"-"`
@@ -131,7 +122,7 @@ type inboxEntry struct {
EphemeralKey string `json:"ephemeral_key"`
// Encrypted message. Must be exactly 256-32 bytes long.
- Body string
+ Body []byte
// Hash of the inbox for this entry
HMailbox string
@@ -144,17 +135,18 @@ type inboxEntry struct {
}
func (m *Mailbox) configResponse(w http.ResponseWriter, r *http.Request) {
- dpStr := m.Cfg.Section("mailbox").Key("delivery_period").MustString("1w")
+ dpStr := m.Cfg.Section("mailbox").Key("delivery_period").MustString("72h")
dp, err := time.ParseDuration(dpStr)
if err != nil {
log.Fatal(err)
}
cfg := VersionResponse{
- Version: "0:0:0",
- Name: "taler-mailbox",
- MessageFee: m.Cfg.Section("mailbox").Key("message_fee").MustString("KUDOS:1"),
- DeliveryPeriod: uint64(dp.Microseconds()),
+ Version: "0:0:0",
+ Name: "taler-mailbox",
+ MessageBodyBytes: m.MessageBodyBytes,
+ MessageFee: m.Cfg.Section("mailbox").Key("message_fee").MustString("KUDOS:1"),
+ DeliveryPeriod: uint64(dp.Microseconds()),
}
w.Header().Set("Content-Type", "application/json")
response, _ := json.Marshal(cfg)
@@ -164,7 +156,7 @@ func (m *Mailbox) configResponse(w http.ResponseWriter, r *http.Request) {
func (m *Mailbox) getMessagesResponse(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
//to, toSet := vars["timeout_ms"]
- var entries []inboxEntry
+ var entries []InboxEntry
// FIXME rate limit
// FIXME timeout
// FIXME possibly limit results here
@@ -177,39 +169,38 @@ func (m *Mailbox) getMessagesResponse(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
return
}
- // Add ETag of first message ID
- etag := entries[0].ID
- w.Header().Add("ETag", fmt.Sprintf("%d", etag))
+ // Add ETag of first message ID
+ etag := entries[0].ID
+ w.Header().Add("ETag", fmt.Sprintf("%d", etag))
for _, entry := range entries {
eph, err := gnunetutil.DecodeStringToBinary(entry.EphemeralKey, 32)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
- body, err := gnunetutil.DecodeStringToBinary(entry.Body, 256-32)
- if err != nil {
- w.WriteHeader(http.StatusInternalServerError)
- return
- }
w.Write(eph)
- w.Write(body)
+ w.Write(entry.Body)
}
w.WriteHeader(http.StatusOK)
}
func (m *Mailbox) sendMessageResponse(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
- var msg IdentityMessage
- var entry inboxEntry
+ var entry InboxEntry
+ var body = make ([]byte, m.MessageBodyBytes)
if r.Body == nil {
http.Error(w, "No request body", 400)
return
}
- err := json.NewDecoder(r.Body).Decode(&msg)
- if err != nil {
- w.WriteHeader(http.StatusInternalServerError)
+ if r.ContentLength != m.MessageBodyBytes {
+ w.WriteHeader(http.StatusBadRequest)
return
- }
+ }
+ _, err := r.Body.Read(body)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
tx := m.Db.Where("h_mailbox = ?", vars["h_mailbox"])
// FIXME max messages from config
// FIXME unclear if this is how the API is defined
@@ -217,13 +208,12 @@ func (m *Mailbox) sendMessageResponse(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusTooManyRequests)
return
}
- err = m.Db.First(&entry, "h_mailbox = ? AND ephemeral_key = ? AND body = ?", vars["h_mailbox"], msg.EphemeralKey, msg.Body).Error
+ err = m.Db.First(&entry, "h_mailbox = ? AND body = ?", vars["h_mailbox"], body).Error
// FIXME actual cost
cost, _ := talerutil.ParseAmount("KUDOS:1")
if err != nil {
entry.HMailbox = vars["h_mailbox"]
- entry.EphemeralKey = msg.EphemeralKey
- entry.Body = msg.Body
+ entry.Body = body
entry.Read = false
}
if len(entry.OrderID) == 0 {
@@ -258,7 +248,7 @@ func (m *Mailbox) sendMessageResponse(w http.ResponseWriter, r *http.Request) {
func (m *Mailbox) deleteMessagesResponse(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
var msg MessageDeletionRequest
- var entries []inboxEntry
+ var entries []InboxEntry
if r.Body == nil {
http.Error(w, "No request body", 400)
return
@@ -268,15 +258,15 @@ func (m *Mailbox) deleteMessagesResponse(w http.ResponseWriter, r *http.Request)
w.WriteHeader(http.StatusInternalServerError)
return
}
- etag_expected := r.Header.Get("If-Match")
- if etag_expected == "" {
- http.Error(w, "If-Match header missing", 400)
- return
- }
- if strings.Contains(etag_expected, ",") {
- http.Error(w, "If-Match contains multiple values", 400)
- return
- }
+ etag_expected := r.Header.Get("If-Match")
+ if etag_expected == "" {
+ http.Error(w, "If-Match header missing", 400)
+ return
+ }
+ if strings.Contains(etag_expected, ",") {
+ http.Error(w, "If-Match contains multiple values", 400)
+ return
+ }
pkey, err := gnunetutil.DecodeStringToBinary(vars["mailbox"], 32)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
@@ -324,13 +314,8 @@ func (m *Mailbox) deleteMessagesResponse(w http.ResponseWriter, r *http.Request)
w.WriteHeader(http.StatusInternalServerError)
return
}
- body, err := gnunetutil.DecodeStringToBinary(entry.Body, 256-32)
- if err != nil {
- w.WriteHeader(http.StatusInternalServerError)
- return
- }
h_all.Write(eph)
- h_all.Write(body)
+ h_all.Write(entry.Body)
}
if !bytes.Equal(h_all.Sum(nil), checksum) {
w.WriteHeader(http.StatusNotFound)
@@ -375,7 +360,7 @@ func (m *Mailbox) Initialize(cfgfile string) {
if m.Cfg.Section("mailbox").Key("production").MustBool(false) {
fmt.Println("Production mode enabled")
}
-
+ m.MessageBodyBytes = m.Cfg.Section("mailbox").Key("message_body_bytes").MustInt64(256)
psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
m.Cfg.Section("mailbox-pq").Key("host").MustString("localhost"),
m.Cfg.Section("mailbox-pq").Key("port").MustInt64(5432),
@@ -389,7 +374,7 @@ func (m *Mailbox) Initialize(cfgfile string) {
panic(err)
}
m.Db = _db
- if err := m.Db.AutoMigrate(&inboxEntry{}); err != nil {
+ if err := m.Db.AutoMigrate(&InboxEntry{}); err != nil {
panic(err)
}