Skip to content

Commit

Permalink
Virtual paths now return correct headers. Can also be cached
Browse files Browse the repository at this point in the history
  • Loading branch information
lonelycode committed Jul 29, 2015
1 parent 80624e7 commit 1d93867
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 33 deletions.
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,8 @@ func loadApps(APISpecs []APISpec, Muxer *http.ServeMux) {
CreateMiddleware(&VersionCheck{TykMiddleware: tykMiddleware}, tykMiddleware),
CreateMiddleware(&TransformMiddleware{tykMiddleware}, tykMiddleware),
CreateMiddleware(&TransformHeaders{TykMiddleware: tykMiddleware}, tykMiddleware),
CreateMiddleware(&VirtualEndpoint{TykMiddleware: tykMiddleware}, tykMiddleware),
CreateMiddleware(&RedisCacheMiddleware{TykMiddleware: tykMiddleware, CacheStore: CacheStore}, tykMiddleware),
CreateMiddleware(&VirtualEndpoint{TykMiddleware: tykMiddleware}, tykMiddleware),
CreateMiddleware(&URLRewriteMiddleware{TykMiddleware: tykMiddleware}, tykMiddleware),
}

Expand Down Expand Up @@ -471,8 +471,8 @@ func loadApps(APISpecs []APISpec, Muxer *http.ServeMux) {
CreateMiddleware(&GranularAccessMiddleware{tykMiddleware}, tykMiddleware),
CreateMiddleware(&TransformMiddleware{tykMiddleware}, tykMiddleware),
CreateMiddleware(&TransformHeaders{TykMiddleware: tykMiddleware}, tykMiddleware),
CreateMiddleware(&VirtualEndpoint{TykMiddleware: tykMiddleware}, tykMiddleware),
CreateMiddleware(&RedisCacheMiddleware{TykMiddleware: tykMiddleware, CacheStore: CacheStore}, tykMiddleware),
CreateMiddleware(&VirtualEndpoint{TykMiddleware: tykMiddleware}, tykMiddleware),
CreateMiddleware(&URLRewriteMiddleware{TykMiddleware: tykMiddleware}, tykMiddleware),
}

Expand Down
16 changes: 14 additions & 2 deletions middleware_redis_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func (m *RedisCacheMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Req
}

var stat RequestStatus
var isVirtual bool
// Only allow idempotent (safe) methods
if r.Method == "GET" || r.Method == "OPTIONS" || r.Method == "HEAD" {
// Lets see if we can throw a sledgehammer at this
Expand All @@ -81,6 +82,7 @@ func (m *RedisCacheMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Req
// New request checker, more targetted, less likely to fail
_, versionPaths, _, _ := m.TykMiddleware.Spec.GetVersionData(r)
found, _ := m.TykMiddleware.Spec.CheckSpecMatchesStatus(r.URL.Path, r.Method, versionPaths, Cached)
isVirtual, _ = m.TykMiddleware.Spec.CheckSpecMatchesStatus(r.URL.Path, r.Method, versionPaths, VirtualPath)
if found {
stat = StatusCached
}
Expand Down Expand Up @@ -109,8 +111,17 @@ func (m *RedisCacheMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Req
log.Debug("Cache enabled, but record not found")
// Pass through to proxy AND CACHE RESULT

// This passes through and will write the value to the writer, but spit out a copy for the cache
reqVal := m.sh.ServeHTTPWithCache(w, r)
reqVal := new(http.Response)

if isVirtual {
log.Debug("This is a virtual function")
thisVP := VirtualEndpoint{m.TykMiddleware}
reqVal = thisVP.ServeHTTPForCache(w, r)
} else {
// This passes through and will write the value to the writer, but spit out a copy for the cache
log.Debug("Not virtual, passing")
reqVal = m.sh.ServeHTTPWithCache(w, r)
}

cacheThisRequest := true
cacheTTL := m.Spec.APIDefinition.CacheOptions.CacheTimeout
Expand Down Expand Up @@ -148,6 +159,7 @@ func (m *RedisCacheMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Req
}

retObj := bytes.NewReader([]byte(retBlob))
log.Debug("Cache got: ", retBlob)

asBufioReader := bufio.NewReader(retObj)
newRes, resErr := http.ReadResponse(asBufioReader, r)
Expand Down
70 changes: 41 additions & 29 deletions middleware_virtual_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,8 @@ type VMResponseObject struct {
// DynamicMiddleware is a generic middleware that will execute JS code before continuing
type VirtualEndpoint struct {
*TykMiddleware
MiddlewareClassName string
UseSession bool
}

// type VirtualMeta struct {
// ResponseFunctionName string `bson:"response_function_name" json:"response_function_name"`
// FunctionSourceType string `bson:"function_source_type" json:"function_source_type"`
// FunctionSourceURI string `bson:"function_source_uri" json:"function_source_uri"`
// Path string `bson:"path" json:"path"`
// Method string `bson:"method" json:"method"`
// UseSession bool `bson:"use_session" json:"use_session"`
// }

func PreLoadVirtualMetaCode(meta *tykcommon.VirtualMeta, j *JSVM) {
if j == nil {
log.Error("No JSVM loaded, cannot init methods")
Expand Down Expand Up @@ -88,8 +77,7 @@ func (d *VirtualEndpoint) GetConfig() (interface{}, error) {
return thisModuleConfig, nil
}

// ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (d *VirtualEndpoint) ProcessRequest(w http.ResponseWriter, r *http.Request, configuration interface{}) (error, int) {
func (d *VirtualEndpoint) ServeHTTPForCache(w http.ResponseWriter, r *http.Request) *http.Response {

// Check if we are even using this MW
var stat RequestStatus
Expand All @@ -102,11 +90,11 @@ func (d *VirtualEndpoint) ProcessRequest(w http.ResponseWriter, r *http.Request,
if found {
stat = StatusVirtualPath
} else {
return nil, 200
return nil
}

if stat != StatusVirtualPath {
return nil, 200
return nil
}

t1 := time.Now().UnixNano()
Expand All @@ -117,7 +105,7 @@ func (d *VirtualEndpoint) ProcessRequest(w http.ResponseWriter, r *http.Request,
originalBody, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Error("Failed to read request body! ", err)
return nil, 200
return nil
}

thisRequestData := RequestObject{
Expand All @@ -134,7 +122,7 @@ func (d *VirtualEndpoint) ProcessRequest(w http.ResponseWriter, r *http.Request,
asJsonRequestObj, encErr := json.Marshal(thisRequestData)
if encErr != nil {
log.Error("Failed to encode request object for virtual endpoint: ", encErr)
return nil, 500
return nil
}

// Encode the configuration data too
Expand All @@ -147,7 +135,7 @@ func (d *VirtualEndpoint) ProcessRequest(w http.ResponseWriter, r *http.Request,
asJsonConfigData, encErr := json.Marshal(configData)
if encErr != nil {
log.Error("Failed to encode request object for virtual endpoint: ", encErr)
return nil, 500
return nil
}

var thisSessionState = SessionState{}
Expand All @@ -163,7 +151,7 @@ func (d *VirtualEndpoint) ProcessRequest(w http.ResponseWriter, r *http.Request,

if sessEncErr != nil {
log.Error("Failed to encode session for VM: ", sessEncErr)
return nil, 500
return nil
}

// Run the middleware
Expand All @@ -177,7 +165,7 @@ func (d *VirtualEndpoint) ProcessRequest(w http.ResponseWriter, r *http.Request,
if decErr != nil {
log.Error("Failed to decode virtual endpoint response data on return from VM: ", decErr)
log.Error("--> Returned: ", returnDataStr)
return nil, 500
return nil
}

// Save the sesison data (if modified)
Expand All @@ -188,19 +176,43 @@ func (d *VirtualEndpoint) ProcessRequest(w http.ResponseWriter, r *http.Request,

log.Debug("JSVM Virtual Endpoint execution took: (ns) ", time.Now().UnixNano()-t1)

d.DoDynamicReply(w, &newResponseData.Response)
// Write to reply with some alternate data
responseMessage := []byte(newResponseData.Response.Body)

return nil, 666
}
// Create an http.Response object so we can send it tot he cache middleware
newResponse := new(http.Response)
newResponse.Header = make(map[string][]string)

requestTime := time.Now().UTC().Format(http.TimeFormat)
w.Header().Add("Server", "tyk")
w.Header().Add("Date", requestTime)

func (v *VirtualEndpoint) DoDynamicReply(w http.ResponseWriter, resp *ResponseObject) {
// Reply with some alternate data
responseMessage := []byte(resp.Body)
for header, value := range resp.Headers {
for header, value := range newResponseData.Response.Headers {
w.Header().Add(header, value)
newResponse.Header.Add(header, value)
}

w.WriteHeader(resp.Code)
// Write to the client
w.WriteHeader(newResponseData.Response.Code)
fmt.Fprintf(w, string(responseMessage))
return

newResponse.ContentLength = int64(len(responseMessage))
newResponse.Body = ioutil.NopCloser(bytes.NewReader(responseMessage))
newResponse.StatusCode = newResponseData.Response.Code
newResponse.Proto = "HTTP/1.0"
newResponse.ProtoMajor = 1
newResponse.ProtoMinor = 0
newResponse.Header.Add("Server", "tyk")
newResponse.Header.Add("Date", requestTime)

return newResponse

}

// ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (d *VirtualEndpoint) ProcessRequest(w http.ResponseWriter, r *http.Request, configuration interface{}) (error, int) {

d.ServeHTTPForCache(w, r)

return nil, 666
}

0 comments on commit 1d93867

Please sign in to comment.