rob05c commented on a change in pull request #2262: Part 1 of Range Request 
handling
URL: 
https://github.com/apache/incubator-trafficcontrol/pull/2262#discussion_r188314219
 
 

 ##########
 File path: grove/plugin/range_req_handler.go
 ##########
 @@ -0,0 +1,225 @@
+package plugin
+
+/*
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+       "crypto/rand"
+       "encoding/hex"
+       "encoding/json"
+       "fmt"
+       "net/http"
+       "strconv"
+       "strings"
+
+       "github.com/apache/incubator-trafficcontrol/grove/web"
+       "github.com/apache/incubator-trafficcontrol/lib/go-log"
+)
+
+type byteRange struct {
+       Start int64
+       End   int64
+}
+
+type rangeRequestConfig struct {
+       Mode string `json::"mode"`
+}
+
+func init() {
+       AddPlugin(10000, Funcs{
+               load:                rangeReqHandleLoad,
+               onRequest:           rangeReqHandlerOnRequest,
+               beforeCacheLookUp:   rangeReqHandleBeforeCacheLookup,
+               beforeParentRequest: rangeReqHandleBeforeParent,
+               beforeRespond:       rangeReqHandleBeforeRespond,
+       })
+}
+
+// rangeReqHandleLoad loads the configuration
+func rangeReqHandleLoad(b json.RawMessage) interface{} {
+       cfg := rangeRequestConfig{}
+       log.Errorf("rangeReqHandleLoad loading: %s", b)
+
+       err := json.Unmarshal(b, &cfg)
+       if err != nil {
+               log.Errorln("range_rew_handler  loading config, unmarshalling 
JSON: " + err.Error())
+               return nil
+       }
+       if !(cfg.Mode == "get_full_serve_range" || cfg.Mode == "patch") {
+               log.Errorf("Unknown mode for range_req_handler plugin: %s", 
cfg.Mode)
+       }
+       log.Debugf("range_rew_handler: load success: %+v\n", cfg)
+       return &cfg
+}
+
+// rangeReqHandlerOnRequest determines if there is a Range header, and puts 
the ranges in *d.Context as a []byteRanges
+func rangeReqHandlerOnRequest(icfg interface{}, d OnRequestData) bool {
+       rHeader := d.R.Header.Get("Range")
+       if rHeader == "" {
+               log.Debugf("No Range header found")
+               return false
+       }
+       log.Debugf("Range string is: %s", rHeader)
+       // put the ranges [] in the context so we can use it later
+       byteRanges := parseRangeHeader(rHeader)
+       *d.Context = byteRanges
+       return false
+}
+
+func rangeReqHandleBeforeCacheLookup(icfg interface{}, d 
BeforeCacheLookUpData, overRideFunc func(string)) {
+       cfg, ok := icfg.(*rangeRequestConfig)
+       if !ok {
+               log.Errorf("range_req_handler config '%v' type '%T' expected 
*rangeRequestConfig\n", icfg, icfg)
+               return
+       }
+       if cfg.Mode == "store_ranges" {
+               sep := "?"
+               if strings.Contains(d.DefaultCacheKey, "?") {
+                       sep = "&"
+               }
+               newKey := d.DefaultCacheKey + sep + 
"grove_range_req_handler_plugin_data=" + d.Req.Header.Get("Range")
+               overRideFunc(newKey)
+               log.Debugf("range_req_handler: store_ranges default key:%s, new 
key:%s", d.DefaultCacheKey, newKey)
+       }
+}
+
+// rangeReqHandleBeforeParent changes the parent request if needed (mode == 
get_full_serve_range)
+func rangeReqHandleBeforeParent(icfg interface{}, d BeforeParentRequestData) {
+       log.Debugf("rangeReqHandleBeforeParent calling.")
+       rHeader := d.Req.Header.Get("Range")
+       if rHeader == "" {
+               log.Debugf("No Range header found")
+               return
+       }
+       log.Debugf("Range string is: %s", rHeader)
+       cfg, ok := icfg.(*rangeRequestConfig)
+       if !ok {
+               log.Errorf("range_req_handler config '%v' type '%T' expected 
*rangeRequestConfig\n", icfg, icfg)
+               return
+       }
+       if cfg.Mode == "get_full_serve_range" {
+               // get_full_serve_range means get the whole thing from 
parent/org, but serve the requested range. Just remove the Range header from 
the upstream request
+               d.Req.Header.Del("Range")
+       }
+       return
+}
+
+// rangeReqHandleBeforeRespond builds the 206 response
+// Assume all the needed ranges have been put in cache before, which is the 
truth for "get_full_serve_range" mode which gets the whole object into cache.
+func rangeReqHandleBeforeRespond(icfg interface{}, d BeforeRespondData) {
+       log.Debugf("rangeReqHandleBeforeRespond calling\n")
+       ictx := d.Context
+       ctx, ok := (*ictx).([]byteRange)
+       if !ok {
+               log.Errorf("Invalid context: %v", ictx)
+       }
+       if len(ctx) == 0 {
+               return // there was no (valid) range header
+       }
+
+       cfg, ok := icfg.(*rangeRequestConfig)
+       if !ok {
+               log.Errorf("range_req_handler config '%v' type '%T' expected 
*rangeRequestConfig\n", icfg, icfg)
+               return
+       }
+       if cfg.Mode == "store_ranges" {
+               return // no need to do anything here.
+       }
+
+       multipartBoundaryString := ""
+       originalContentType := d.Hdr.Get("Content-type")
+       *d.Hdr = web.CopyHeader(*d.Hdr) // copy the headers, we don't want to 
mod the cacheObj
+       if len(ctx) > 1 {
+               //multipart = true
+               multipartBoundaryBytes := make([]byte, 8)
 
 Review comment:
   I think this should be 16 bytes, a UUID. 8 bytes might not be enough to be 
globally unique, and might appear in the body, breaking everything. Also, 
generating a random number is expensive, and this is in the request path, where 
performance is critical. Can you generate the UUID once, and reuse it? Should 
be able to do that with a `startup` hook, and set it in the context.

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

Reply via email to