| // Copyright IBM Corp. 2014, 2026 |
| // SPDX-License-Identifier: BUSL-1.1 |
| package http |
| |
| import ( |
| "bytes" |
| "encoding/json" |
| "fmt" |
| "io" |
| "net/http" |
| "reflect" |
| |
| "github.com/hashicorp/terraform/internal/states/statemgr" |
| ) |
| |
| type TestRequestHandleFunc func(w http.ResponseWriter, r *http.Request) |
| |
| type TestHTTPBackend struct { |
| Data []byte |
| Locked bool |
| |
| // LockInfo is set by the calling test code and is not |
| // set when tests use the Lock method on an http backend. |
| LockInfo *statemgr.LockInfo |
| |
| methodFuncs map[string]TestRequestHandleFunc |
| methodCalls map[string]int |
| } |
| |
| func (h *TestHTTPBackend) Handle(w http.ResponseWriter, r *http.Request) { |
| h.countMethodCall(r.Method) |
| called := h.callMethod(r.Method, w, r) |
| if called { |
| return |
| } |
| |
| switch r.Method { |
| case "GET": |
| w.Write(h.Data) |
| case "PUT": |
| buf := new(bytes.Buffer) |
| if _, err := io.Copy(buf, r.Body); err != nil { |
| w.WriteHeader(500) |
| } |
| w.WriteHeader(201) |
| h.Data = buf.Bytes() |
| case "POST": |
| buf := new(bytes.Buffer) |
| if _, err := io.Copy(buf, r.Body); err != nil { |
| w.WriteHeader(500) |
| } |
| h.Data = buf.Bytes() |
| case "LOCK": |
| if h.Locked { |
| w.WriteHeader(423) |
| if h.LockInfo != nil { |
| // Write lock info to response, but only if |
| // the test http backend server with lock info present. |
| jsonLockInfo, err := json.Marshal(h.LockInfo) |
| if err != nil { |
| w.WriteHeader(500) |
| w.Write([]byte("Failed to marshal lock info in test http backend server: " + err.Error())) |
| return |
| } |
| w.Write(jsonLockInfo) |
| } |
| } else { |
| h.Locked = true |
| } |
| case "UNLOCK": |
| h.Locked = false |
| case "DELETE": |
| h.Data = nil |
| w.WriteHeader(200) |
| default: |
| w.WriteHeader(http.StatusNotImplemented) |
| w.Write([]byte(fmt.Sprintf("Unknown method: %s", r.Method))) |
| } |
| } |
| |
| func (h *TestHTTPBackend) countMethodCall(method string) { |
| if h.methodCalls == nil { |
| h.methodCalls = make(map[string]int) |
| } |
| if _, ok := h.methodCalls[method]; !ok { |
| h.methodCalls[method] = 0 |
| } |
| h.methodCalls[method]++ |
| } |
| |
| func (h *TestHTTPBackend) CallCount(method string) int { |
| if h.methodCalls == nil { |
| return 0 |
| } |
| callCount, ok := h.methodCalls[method] |
| if !ok { |
| return 0 |
| } |
| return callCount |
| } |
| |
| func (h *TestHTTPBackend) callMethod(method string, w http.ResponseWriter, r *http.Request) bool { |
| if h.methodFuncs == nil { |
| return false |
| } |
| f, ok := h.methodFuncs[method] |
| if ok { |
| f(w, r) |
| } |
| return ok |
| } |
| |
| func (h *TestHTTPBackend) SetMethodFunc(method string, impl TestRequestHandleFunc) { |
| if h.methodFuncs == nil { |
| h.methodFuncs = make(map[string]TestRequestHandleFunc) |
| } |
| h.methodFuncs[method] = impl |
| } |
| |
| // mod_dav-ish behavior |
| func (h *TestHTTPBackend) HandleWebDAV(w http.ResponseWriter, r *http.Request) { |
| h.countMethodCall(r.Method) |
| if f, ok := h.methodFuncs[r.Method]; ok { |
| f(w, r) |
| return |
| } |
| |
| switch r.Method { |
| case "GET": |
| w.Write(h.Data) |
| case "PUT": |
| buf := new(bytes.Buffer) |
| if _, err := io.Copy(buf, r.Body); err != nil { |
| w.WriteHeader(500) |
| } |
| if reflect.DeepEqual(h.Data, buf.Bytes()) { |
| h.Data = buf.Bytes() |
| w.WriteHeader(204) |
| } else { |
| h.Data = buf.Bytes() |
| w.WriteHeader(201) |
| } |
| case "DELETE": |
| h.Data = nil |
| w.WriteHeader(200) |
| default: |
| w.WriteHeader(http.StatusNotImplemented) |
| w.Write([]byte(fmt.Sprintf("Unknown method: %s", r.Method))) |
| } |
| } |