diff --git a/README.md b/README.md index bd26875..7eae304 100644 --- a/README.md +++ b/README.md @@ -375,6 +375,7 @@ This way, the NATS request header `X-NatsBridge-UrlQuery` can be used to set URL ```nginx nats_request [matcher] [serverAlias] subject { [timeout 42ms] + [headers true|false] } ``` @@ -383,8 +384,8 @@ sends the NATS reply back as HTTP response. It is a terminal handler, meaning it does not make sense to place Caddy handlers after this one because they will never be called. -HTTP request headers are converted to NATS headers. NATS reply headers are converted -to HTTP response headers. +HTTP request headers are converted to NATS headers if the headers subdirective is set to true. +NATS reply headers are converted to HTTP response headers. For `matcher`, all registered [Caddy request matchers](https://caddyserver.com/docs/json/apps/http/servers/routes/match/) can be used - and the `nats_request` handler is only triggered if the request matches the matcher. @@ -437,20 +438,20 @@ Additionally, the following placeholders are available: (same as for `nats_publish`) -All HTTP headers will become NATS Message headers. On top if this, the following headers are automatically set: +If the subdirective 'headers' is set to true (default), then all HTTP headers will become NATS Message headers. On top if this, the following headers are automatically set: - `X-NatsBridge-Method` header: contains the HTTP header `GET,POST,HEAD,...` - `X-NatsBridge-UrlPath` header: URI path without query string - `X-NatsBridge-UrlQuery` header: encoded query values, without `?` -> We might want to support setting arbitrary headers later :) (from Caddy expressions). Create an issue if you need this :) - --- ## HTTP -> NATS via `nats_publish` (fire-and-forget) ```nginx -nats_publish [matcher] [serverAlias] subject +nats_publish [matcher] [serverAlias] subject { + [headers true|false] +} ``` `nats_publish` publishes the HTTP request to the specified NATS subject. This @@ -511,14 +512,12 @@ Additionally, the following placeholders are available: (same as for `nats_request`) -All HTTP headers will become NATS Message headers. On top if this, the following headers are automatically set: +If the subdirective 'headers' is set to true (default), then all HTTP headers will become NATS Message headers. On top if this, the following headers are automatically set: - `X-NatsBridge-Method` header: contains the HTTP header `GET,POST,HEAD,...` - `X-NatsBridge-UrlPath` header: URI path without query string - `X-NatsBridge-UrlQuery` header: encoded query values, without `?` -> We might want to support setting arbitrary headers later :) (from Caddy expressions). Create an issue if you need this :) - --- ## large HTTP payloads with store_body_to_jetstream diff --git a/common/http_msg_to_nats_msg.go b/common/http_msg_to_nats_msg.go index 9f809b9..54c13d5 100644 --- a/common/http_msg_to_nats_msg.go +++ b/common/http_msg_to_nats_msg.go @@ -1,31 +1,42 @@ package common import ( - "github.com/nats-io/nats.go" "io" "net/http" + + "github.com/nats-io/nats.go" ) // NatsMsgForHttpRequest creates a nats.Msg from an existing http.Request: the HTTP Request Body is transferred -// to the NATS message Data, and the headers are transferred as well. +// to the NATS message Data, and the headers are transferred as well if 'headers' is true. // // Three special headers are added for the request method, URL path, and raw query. -func NatsMsgForHttpRequest(r *http.Request, subject string) (*nats.Msg, error) { +func NatsMsgForHttpRequest(r *http.Request, subject string, headers bool) (*nats.Msg, error) { var msg *nats.Msg b, _ := io.ReadAll(r.Body) - headers := nats.Header(r.Header) - for k, v := range ExtraNatsMsgHeadersFromContext(r.Context()) { - headers.Add(k, v) - } - msg = &nats.Msg{ - Subject: subject, - Header: headers, - Data: b, + if headers { + headers := nats.Header(r.Header) + for k, v := range ExtraNatsMsgHeadersFromContext(r.Context()) { + headers.Add(k, v) + } + + msg = &nats.Msg{ + Subject: subject, + Header: headers, + Data: b, + } + + msg.Header.Add("X-NatsBridge-Method", r.Method) + msg.Header.Add("X-NatsBridge-UrlPath", r.URL.Path) + msg.Header.Add("X-NatsBridge-UrlQuery", r.URL.RawQuery) + + } else { + msg = &nats.Msg{ + Subject: subject, + Data: b, + } } - msg.Header.Add("X-NatsBridge-Method", r.Method) - msg.Header.Add("X-NatsBridge-UrlPath", r.URL.Path) - msg.Header.Add("X-NatsBridge-UrlQuery", r.URL.RawQuery) return msg, nil } diff --git a/publish/caddyfile.go b/publish/caddyfile.go index 76d7a64..5c93575 100644 --- a/publish/caddyfile.go +++ b/publish/caddyfile.go @@ -1,6 +1,8 @@ package publish import ( + "strconv" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" "github.com/caddyserver/caddy/v2/modules/caddyhttp" @@ -31,6 +33,17 @@ func (p *Publish) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { for d.NextBlock(0) { switch d.Val() { + case "headers": + if !d.NextArg() { + return d.ArgErr() + } + h, err := strconv.ParseBool(d.Val()) + if err != nil { + return d.Err("headers is not a boolean") + } + + p.Headers = h + default: return d.Errf("unrecognized subdirective: %s", d.Val()) } diff --git a/publish/publish.go b/publish/publish.go index 66a4b4f..30755f1 100644 --- a/publish/publish.go +++ b/publish/publish.go @@ -2,18 +2,20 @@ package publish import ( "fmt" + "net/http" + "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/modules/caddyhttp" "github.com/sandstorm/caddy-nats-bridge/common" "github.com/sandstorm/caddy-nats-bridge/natsbridge" "go.uber.org/zap" - "net/http" ) type Publish struct { Subject string `json:"subject,omitempty"` ServerAlias string `json:"serverAlias,omitempty"` + Headers bool `json:"headers,omitempty"` logger *zap.Logger app *natsbridge.NatsBridgeApp @@ -26,6 +28,7 @@ func (Publish) CaddyModule() caddy.ModuleInfo { // Default values return &Publish{ ServerAlias: "default", + Headers: true, } }, } @@ -58,7 +61,7 @@ func (p Publish) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhtt return fmt.Errorf("NATS server alias %s not found", p.ServerAlias) } - msg, err := common.NatsMsgForHttpRequest(r, subj) + msg, err := common.NatsMsgForHttpRequest(r, subj, p.Headers) if err != nil { return err } diff --git a/request/caddyfile.go b/request/caddyfile.go index dcc5c3d..c8f094e 100644 --- a/request/caddyfile.go +++ b/request/caddyfile.go @@ -1,16 +1,19 @@ package request import ( + "strconv" + "time" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" "github.com/caddyserver/caddy/v2/modules/caddyhttp" - "time" ) // ParseRequestHandler parses the nats_request directive. Syntax: // // nats_request [serverAlias] subject { // [timeout 1s] +// [headers true|false] // } func ParseRequestHandler(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { var p = Request{} @@ -42,6 +45,18 @@ func (p *Request) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { } p.Timeout = t + + case "headers": + if !d.NextArg() { + return d.ArgErr() + } + h, err := strconv.ParseBool(d.Val()) + if err != nil { + return d.Err("headers is not a boolean") + } + + p.Headers = h + default: return d.Errf("unrecognized subdirective: %s", d.Val()) } diff --git a/request/request.go b/request/request.go index 9838b1e..22ddd6c 100644 --- a/request/request.go +++ b/request/request.go @@ -2,6 +2,9 @@ package request import ( "fmt" + "net/http" + "time" + "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/modules/caddyhttp" @@ -9,13 +12,12 @@ import ( "github.com/sandstorm/caddy-nats-bridge/common" "github.com/sandstorm/caddy-nats-bridge/natsbridge" "go.uber.org/zap" - "net/http" - "time" ) type Request struct { Subject string `json:"subject,omitempty"` Timeout time.Duration `json:"timeout,omitempty"` + Headers bool `json:"Headers,omitempty"` ServerAlias string `json:"serverAlias,omitempty"` logger *zap.Logger @@ -29,6 +31,7 @@ func (Request) CaddyModule() caddy.ModuleInfo { // Default values return &Request{ Timeout: 1 * time.Second, + Headers: true, ServerAlias: "default", } }, @@ -63,7 +66,7 @@ func (p Request) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhtt return fmt.Errorf("NATS server alias %s not found", p.ServerAlias) } - msg, err := common.NatsMsgForHttpRequest(r, subj) + msg, err := common.NatsMsgForHttpRequest(r, subj, p.Headers) if err != nil { return err }