func prepareForCaching(r []reflect.Value, outputParams []string) []byte { var err error buf := new(bytes.Buffer) encd := json.NewEncoder(buf) for i, _ := range r { switch outputParams[i] { case "int": err = encd.Encode(r[i].Int()) panik.On(err) case "map": err = encd.Encode(r[i].Interface().(map[string]interface{})) panik.On(err) case "string": err = encd.Encode(r[i].String()) panik.On(err) case "*st:github.com/tolexo/aqua.Sac": err = encd.Encode(r[i].Elem().Interface().(Sac).Data) default: panic("Unknown type of output to be sent to endpoint cache: " + outputParams[i]) } } return buf.Bytes() }
func NewDebugCache(w string, r string, inner Cacher) Cacher { var err error var rf, wf *os.File if r != "" { rf, err = os.OpenFile(r, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) panik.On(err) } if w != "" { wf, err = os.OpenFile(w, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) panik.On(err) } return debugCache{ base: inner, w: wf, r: rf, } }
func NewMemcache(host string, port int) Cacher { serv, err := gomemcache.Connect(host, port) panik.On(err) m := memCache{ mc: serv, } return m // TODO: close port on destruction }
func decomposeCachedValues(data []byte, outputParams []string) []reflect.Value { var err error buf := bytes.NewBuffer(data) decd := json.NewDecoder(buf) out := make([]reflect.Value, len(outputParams)) for i, o := range outputParams { switch o { case "int": var j int err = decd.Decode(&j) panik.On(err) out[i] = reflect.ValueOf(j) case "map": var m map[string]interface{} err = decd.Decode(&m) panik.On(err) out[i] = reflect.ValueOf(m) case "string": var s string err = decd.Decode(&s) panik.On(err) out[i] = reflect.ValueOf(s) case "*st:github.com/tolexo/aqua.Sac": var m map[string]interface{} err = decd.Decode(&m) panik.On(err) s := NewSac() s.Data = m out[i] = reflect.ValueOf(s) default: panic("Unknown type of output to be decoded from endpoint cache:" + o) } } return out }
func NewRedis(host string, port int, db int) Cacher { serv := redis.NewClient(&redis.Options{ Addr: fmt.Sprintf("%s:%d", host, port), DB: int64(db), }) _, err := serv.Ping().Result() panik.On(err) return redisStore{ r: serv, } // TODO: close port on destruction }
func ModAccessLog(path string) func(http.Handler) http.Handler { f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) panik.On(err) l := log.New(f, "", log.LstdFlags) return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() next.ServeHTTP(w, r) l.Printf("%s %s %.3f", r.Method, r.RequestURI, time.Since(start).Seconds()) }) } }
func MonitorMe(params MonitorParams) { if params.ServiceId != "" { dataDogAgent := GetDataDogAgent() dataDogAgent.Count("throughput", 1, nil, 1) dataDogAgent.Count(params.ServiceId, 1, nil, 1) dataDogAgent.Histogram("resptime", params.RespTime, nil, 1) dataDogAgent.Histogram(params.ServiceId+".resptime", params.RespTime, nil, 1) intervalTag := GetTimeIntervalTag(params.RespTime) dataDogAgent.Histogram("resptimeinterval", params.RespTime, intervalTag, 1) dataDogAgent.Histogram(params.ServiceId+".resptimeinterval", params.RespTime, intervalTag, 1) if params.CacheHit { dataDogAgent.Count("cachehit", 1, nil, 1) dataDogAgent.Count(params.ServiceId+".cachehit", 1, nil, 1) dataDogAgent.Histogram("resptime.c", params.RespTime, nil, 1) dataDogAgent.Histogram(params.ServiceId+".resptime.c", params.RespTime, nil, 1) } else { dataDogAgent.Histogram("resptime.nc", params.RespTime, nil, 1) dataDogAgent.Histogram(params.ServiceId+".resptime.nc", params.RespTime, nil, 1) } if params.ResponseCode > 0 { statusCode := FormatHttpStatusCode(int64(params.ResponseCode)) dataDogAgent.Count(statusCode, 1, nil, 1) dataDogAgent.Count(params.ServiceId+"."+statusCode, 1, nil, 1) } enablelog := conf.Bool("monitor.enablelog", false) if enablelog { logfile := conf.String("monitor.filelog", "") if logfile != "" { t := time.Now() logfileSuffix := fmt.Sprintf("%d-%d-%d", t.Month(), t.Day(), t.Hour()) f, err := os.OpenFile(logfile+logfileSuffix+".csv", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) panik.On(err) l := log.New(f, "", log.LstdFlags) l.Printf("%s %f %s %d %t", params.ServiceId, params.RespTime, intervalTag, params.ResponseCode, params.CacheHit) } } } }
func handleIncoming(e *endPoint) func(http.ResponseWriter, *http.Request) { // return stub if e.info.Stub != "" { return func(w http.ResponseWriter, r *http.Request) { d, err := getContent(e.info.Stub) if err == nil { fmt.Fprintf(w, "%s", d) } else { w.WriteHeader(400) fmt.Fprintf(w, "{ message: \"%s\"}", "Stub path not found") } } } return func(w http.ResponseWriter, r *http.Request) { cacheHit := false // TODO: create less local variables // TODO: move vars to closure level var out []reflect.Value //TODO: capture this using instrumentation handler defer func(reqStartTime time.Time) { go func() { if e.serviceId != "" { respTime := time.Since(reqStartTime).Seconds() * 1000 var responseCode int64 = 200 if out != nil && len(out) == 2 && e.caller.outParams[0] == "int" { responseCode = out[0].Int() } monitorParams := monit.MonitorParams{ ServiceId: e.serviceId, RespTime: respTime, ResponseCode: responseCode, CacheHit: cacheHit, } monit.MonitorMe(monitorParams) } }() if reqR := recover(); reqR != nil { monit.PanicLogger(reqR, e.serviceId, r.RequestURI, time.Now()) } }(time.Now()) //check authentication if e.info.Auth != "" { ok, errMsg := auth.AuthenticateRequest(r, e.info.Auth) if !ok { //print authentication error w.WriteHeader(401) w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Length", strconv.Itoa(len(errMsg))) fmt.Fprintf(w, "%s", errMsg) return } } var useCache bool = false var ttl time.Duration = 0 * time.Second var val []byte var err error if e.info.Ttl != "" { ttl, err = time.ParseDuration(e.info.Ttl) panik.On(err) } useCache = r.Method == "GET" && ttl > 0 && e.stash != nil muxVals := mux.Vars(r) params := make([]string, len(e.muxVars)) for i, v := range e.muxVars { params[i] = muxVals[v] } if e.isStdHttpHandler { //TODO: caching of standard handler e.caller.Do([]reflect.Value{reflect.ValueOf(w), reflect.ValueOf(r)}) } else { ref := convertToType(params, e.caller.inpParams) if e.needsJarInput { ref = append(ref, reflect.ValueOf(NewJar(r))) } if useCache { val, err = e.stash.Get(r.RequestURI) if err == nil { cacheHit = true // fmt.Print(".") out = decomposeCachedValues(val, e.caller.outParams) } else { out = e.caller.Do(ref) if len(out) == 2 && e.caller.outParams[0] == "int" { code := out[0].Int() if code < 200 || code > 299 { useCache = false } } if useCache { bytes := prepareForCaching(out, e.caller.outParams) e.stash.Set(r.RequestURI, bytes, ttl) // fmt.Print(":", len(bytes), r.RequestURI) } } } else { out = e.caller.Do(ref) // fmt.Print("!") } writeOutput(w, e.caller.outParams, out, e.info.Pretty) } } }