Add http body

Also pass http body to commands.
This commit is contained in:
Felix Niederwanger 2024-03-31 19:06:44 +02:00
parent 46c414e2cc
commit 288e23169e
Signed by: phoenix
GPG key ID: 6E77A590E3F6D71C
4 changed files with 104 additions and 2 deletions

View file

@ -21,7 +21,8 @@ type ConfigSettings struct {
GID int `yaml:"gid"` // Custom group ID or 0, if not being used
ReadTimeout int `yaml:"readtimeout"` // Timeout for reading the whole request
WriteTimeout int `yaml:"writetimeout"` // Timeout for writing the whole response
MaxHeaderBytes int `yaml:"maxheadersize"` // Maximum size of the receive body
MaxHeaderBytes int `yaml:"maxheadersize"` // Maximum size of the receive header
MaxBodySize int64 `yaml:"maxbodysize"` // Maximum size of the receive body
TLS TLSSettings `yaml:"tls"`
}

View file

@ -306,6 +306,19 @@ func createHandler(hook Hook) Handler {
buffer.WriteString(fmt.Sprintf("%s:%s\n", k, strings.Join(v, ",")))
}
buffer.WriteString("\n")
// Receive body, if desired
if cf.Settings.MaxBodySize > 0 {
body := make([]byte, cf.Settings.MaxBodySize)
n, err := r.Body.Read(body)
if err != nil {
log.Printf("receive body failed: %s", err)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(500)
fmt.Fprintf(w, "{\"status\":\"fail\",\"reason\":\"receive failure\"}")
return
}
buffer.Write(body[:n])
}
if hook.Background { // Execute command in background
w.Header().Add("Content-Type", "application/json")

View file

@ -1,6 +1,7 @@
package main
import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
@ -253,7 +254,7 @@ func TestTLSWebserver(t *testing.T) {
// Tests the run hook commands
func TestRunHook(t *testing.T) {
testText := "hello Test"
hook := Hook{Name: "hook", Command: "/usr/bin/cat"}
hook := Hook{Name: "hook", Command: "cat"}
buffer, err := hook.Run([]byte(testText))
if err != nil {
@ -265,6 +266,92 @@ func TestRunHook(t *testing.T) {
}
}
// Tests passing the request header and body
func TestHeaderAndBody(t *testing.T) {
// Create temp file
tempFile, err := os.CreateTemp("", "test_header_body_*")
if err != nil {
panic(err)
}
defer func() {
os.Remove(tempFile.Name())
}()
// Create test webserver with receive hook, that passes all headers and the body to the temp file
var cf Config
cf.Settings.BindAddress = "127.0.0.1:2088"
cf.Settings.MaxBodySize = 4096
cf.Hooks = make([]Hook, 0)
cf.Hooks = append(cf.Hooks, Hook{Name: "hook", Command: fmt.Sprintf("tee %s", tempFile.Name()), Route: "/header_and_body"})
listener, err := CreateListener(cf)
if err != nil {
t.Fatalf("error creating listener: %s", err)
return
}
server := CreateWebserver(cf)
mux := http.NewServeMux()
server.Handler = mux
if err := RegisterHandlers(cf, mux); err != nil {
t.Fatalf("error registering handlers: %s", err)
return
}
go server.Serve(listener)
defer func() {
if err := server.Shutdown(context.Background()); err != nil {
t.Fatalf("error while server shutdown: %s", err)
return
}
}()
// Create http request with custom headers and a message body
client := &http.Client{}
req, err := http.NewRequest("GET", fmt.Sprintf("http://%s/header_and_body", cf.Settings.BindAddress), nil)
if err != nil {
panic(err)
}
headers := make(map[string]string, 0)
headers["Header1"] = "value1"
headers["Header2"] = "value2"
headers["Header3"] = "value3"
headers["Content-Type"] = "this is the content type"
for k, v := range headers {
req.Header.Set(k, v)
}
req.Body = io.NopCloser(bytes.NewReader([]byte("this is the request body\nit is awesome")))
res, err := client.Do(req)
if err != nil {
t.Fatalf("http request error: %s", err)
}
if res.StatusCode != http.StatusOK {
t.Fatalf("http request failed: %d != %d", res.StatusCode, http.StatusOK)
}
// Assert that the headers and the body is in the test file
buf, err := os.ReadFile(tempFile.Name())
if err != nil {
panic(err)
}
contents := string(buf)
assertHeader := func(key string, value string) {
if !strings.Contains(contents, key) {
t.Fatalf("Header %s is not present", key)
}
if !strings.Contains(contents, fmt.Sprintf("%s:%s\n", key, value)) {
t.Fatalf("Header %s has not the right value", key)
}
}
for k, v := range headers {
assertHeader(k, v)
}
// Assert the message body got passed as well
// Assert the messaeg body got cropped
}
func generateKeypair(keyfile string, certfile string, hostnames []string) error {
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {

View file

@ -11,6 +11,7 @@ settings:
readtimeout: 10 # if set, maximum number of seconds to receive the full request
writetimeout: 10 # if set, maximum number of seconds to send the full response
maxheadersize: 4096 # maximum header size
maxbodysize: 0 # maximum size of the body before cropping. Setting to 0 will ignore the http request body. Default: 0
# Enable TLS
# Note that it is not recommended to run weblug on the open internet without using a reverse proxy
tls: