Merge pull request #266 from hashicorp/f-http
Support for HTTP Response overwriting
This commit is contained in:
commit
4946a66e30
|
@ -96,6 +96,12 @@ func respondLogical(w http.ResponseWriter, r *http.Request, path string, resp *l
|
|||
return
|
||||
}
|
||||
|
||||
// Check if this is a raw response
|
||||
if _, ok := resp.Data[logical.HTTPContentType]; ok {
|
||||
respondRaw(w, r, path, resp)
|
||||
return
|
||||
}
|
||||
|
||||
logicalResp := &LogicalResponse{Data: resp.Data}
|
||||
if resp.Secret != nil {
|
||||
logicalResp.LeaseID = resp.Secret.LeaseID
|
||||
|
@ -140,6 +146,58 @@ func respondLogical(w http.ResponseWriter, r *http.Request, path string, resp *l
|
|||
respondOk(w, httpResp)
|
||||
}
|
||||
|
||||
// respondRaw is used when the response is using HTTPContentType and HTTPRawBody
|
||||
// to change the default response handling. This is only used for specific things like
|
||||
// returning the CRL information on the PKI backends.
|
||||
func respondRaw(w http.ResponseWriter, r *http.Request, path string, resp *logical.Response) {
|
||||
// Ensure this is never a secret or auth response
|
||||
if resp.Secret != nil || resp.Auth != nil {
|
||||
respondError(w, http.StatusInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the status code
|
||||
statusRaw, ok := resp.Data[logical.HTTPStatusCode]
|
||||
if !ok {
|
||||
respondError(w, http.StatusInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
status, ok := statusRaw.(int)
|
||||
if !ok {
|
||||
respondError(w, http.StatusInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the header
|
||||
contentTypeRaw, ok := resp.Data[logical.HTTPContentType]
|
||||
if !ok {
|
||||
respondError(w, http.StatusInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
contentType, ok := contentTypeRaw.(string)
|
||||
if !ok {
|
||||
respondError(w, http.StatusInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the body
|
||||
bodyRaw, ok := resp.Data[logical.HTTPRawBody]
|
||||
if !ok {
|
||||
respondError(w, http.StatusInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
body, ok := bodyRaw.([]byte)
|
||||
if !ok {
|
||||
respondError(w, http.StatusInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Write the response
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
w.WriteHeader(status)
|
||||
w.Write(body)
|
||||
}
|
||||
|
||||
type LogicalResponse struct {
|
||||
LeaseID string `json:"lease_id"`
|
||||
Renewable bool `json:"renewable"`
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
@ -182,3 +184,34 @@ func TestLogical_CreateToken(t *testing.T) {
|
|||
t.Fatalf("should not get cookies: %#v", cookies)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogical_RawHTTP(t *testing.T) {
|
||||
core, _, token := vault.TestCoreUnsealed(t)
|
||||
ln, addr := TestServer(t, core)
|
||||
defer ln.Close()
|
||||
TestServerAuth(t, addr, token)
|
||||
|
||||
resp := testHttpPost(t, addr+"/v1/sys/mounts/foo", map[string]interface{}{
|
||||
"type": "http",
|
||||
})
|
||||
testResponseStatus(t, resp, 204)
|
||||
|
||||
// Get the raw response
|
||||
resp, err := http.Get(addr + "/v1/foo/raw")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
testResponseStatus(t, resp, 200)
|
||||
|
||||
// Test the headers
|
||||
if resp.Header.Get("Content-Type") != "plain/text" {
|
||||
t.Fatalf("Bad: %#v", resp.Header)
|
||||
}
|
||||
|
||||
// Get the body
|
||||
body := new(bytes.Buffer)
|
||||
io.Copy(body, resp.Body)
|
||||
if string(body.Bytes()) != "hello world" {
|
||||
t.Fatalf("Bad: %s", body.Bytes())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,24 @@
|
|||
package logical
|
||||
|
||||
const (
|
||||
// HTTPContentType can be specified in the Data field of a Response
|
||||
// so that the HTTP front end can specify a custom Content-Type associated
|
||||
// with the HTTPRawBody. This can only be used for non-secrets, and should
|
||||
// be avoided unless absolutely necessary, such as implementing a specification.
|
||||
// The value must be a string.
|
||||
HTTPContentType = "http_content_type"
|
||||
|
||||
// HTTPRawBody is the raw content of the HTTP body that goes with the HTTPContentType.
|
||||
// This can only be specified for non-secrets, and should should be similarly
|
||||
// avoided like the HTTPContentType. The value must be a byte slice.
|
||||
HTTPRawBody = "http_raw_body"
|
||||
|
||||
// HTTPStatusCode is the response code the HTTP body that goes with the HTTPContentType.
|
||||
// This can only be specified for non-secrets, and should should be similarly
|
||||
// avoided like the HTTPContentType. The value must be an integer.
|
||||
HTTPStatusCode = "http_status_code"
|
||||
)
|
||||
|
||||
// Response is a struct that stores the response of a request.
|
||||
// It is used to abstract the details of the higher level request protocol.
|
||||
type Response struct {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/audit"
|
||||
|
@ -23,6 +24,9 @@ func TestCore(t *testing.T) *Core {
|
|||
noopBackends["noop"] = func(map[string]string) (logical.Backend, error) {
|
||||
return new(framework.Backend), nil
|
||||
}
|
||||
noopBackends["http"] = func(map[string]string) (logical.Backend, error) {
|
||||
return new(rawHTTP), nil
|
||||
}
|
||||
|
||||
physicalBackend := physical.NewInmem()
|
||||
c, err := NewCore(&CoreConfig{
|
||||
|
@ -89,3 +93,21 @@ func (n *noopAudit) LogRequest(a *logical.Auth, r *logical.Request) error {
|
|||
func (n *noopAudit) LogResponse(a *logical.Auth, r *logical.Request, re *logical.Response, err error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type rawHTTP struct{}
|
||||
|
||||
func (n *rawHTTP) HandleRequest(req *logical.Request) (*logical.Response, error) {
|
||||
return &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
logical.HTTPStatusCode: 200,
|
||||
logical.HTTPContentType: "plain/text",
|
||||
logical.HTTPRawBody: []byte("hello world"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *rawHTTP) SpecialPaths() *logical.Paths {
|
||||
return &logical.Paths{Unauthenticated: []string{"*"}}
|
||||
}
|
||||
|
||||
func (n *rawHTTP) SetLogger(l *log.Logger) {}
|
||||
|
|
Loading…
Reference in a new issue