Example #1
0
/*

 A generic error function

 Utility functions pass   errors up to the caller


 Higher level "request functions" handle errors directly
    often we want to abort further request processing
 	and issue an message into the http response AND into the logs

 Sometimes we only want to write the error into the logs and
    continue operation => continueExecution true

  In addition to the generic error messages
  we may add specific error explanations or values
  via parameter vs - for display and logging
  We also show the source file+location.

  A "global panic catcher" in util_err.Adapter() ...defer(){}
  cooperates - suppressing the stacktrace and
  healing the panic

*/
func E(w http.ResponseWriter, r *http.Request,
	bool_or_err interface{},
	continueExecution bool,
	vs ...interface{}) {

	var err error

	switch bool_or_err.(type) {
	default:
		type_unknown := fmt.Sprintf("%T", bool_or_err)
		err = errors.New("only bool or error - instead: -" + type_unknown + "-")
		panic(err)
	case nil:
		return
	case bool:
		if bool_or_err.(bool) {
			return
		}
		err = errors.New("Not OK (type conv?)")
	case error:
		err = bool_or_err.(error)
	}

	if err != nil {
		line, file := runtimepb.LineFileXUp(1)
		// we cannot determine, whether html is already sent
		// we cannot determine, whether we are in plaintext or html context
		// thus we need the <br>
		s := fmt.Sprintf("ERR: %v  <br>\n\t /%s:%d \n", err, file, line)
		if len(vs) > 0 {
			s = s + "\t" + fmt.Sprint(vs...) + "\n"
		}

		if continueExecution {
			c, _ := util_appengine.SafelyExtractGaeCtxError(r)
			if c == nil {
				log.Printf(s)
			} else {
				aelog.Infof(c, s)
			}
		} else {
			c, _ := util_appengine.SafelyExtractGaeCtxError(r)
			if c == nil {
				log.Printf(s)
			} else {
				aelog.Errorf(c, s)
			}
			w.Header().Set("Content-Type", "text/plain")
			http.Error(w, s, http.StatusInternalServerError)
			panic("abort_handler_processing")
		}
	}

}
Example #2
0
func Pf(w io.Writer, r *http.Request, f string, vs ...interface{}) {

	for idx, v := range vs {
		switch v := v.(type) {
		case []byte:
			if len(v) > 1024*5 {
				appdx := append([]byte(" ...omitted... "), v[len(v)-100:]...)
				vs[idx] = append(v[:1024*5], appdx...)
			}
		case string:
			if len(v) > 1024*5 {
				appdx := " ...omitted... " + v[len(v)-100:]
				vs[idx] = v[:1024*5] + appdx
			}
		}
	}

	// Prepare the string
	var s string
	if len(vs) > 0 {
		s = fmt.Sprintf(f, vs...)
	} else {
		s = f
	}

	if s == "" {
		return
	}

	// Write it to http response or bytes.Buffer
	// unless prefixed with 'lo ' - log only.
	// Thread-safety could be introduced by syncing/locking w.
	if w != nil && !strings.HasPrefix(s, "lo ") {
		w.Write([]byte(s))
		w.Write([]byte{'\n'})
	}

	// Write to log/gae-log
	// Adding src code info

	line, file := runtimepb.LineFileXUp(1)
	// if strings.HasSuffix(file, "log.go")
	if strings.HasSuffix(file, runtimepb.ThisFile()) { // change
		line, file = runtimepb.LineFileXUp(2)
	}

	if len(s) < 60 {
		s = stringspb.ToLen(s, 60)
	}
	s = fmt.Sprintf("%v - %v:%v", s, file, line)

	// Log it
	c, _ := util_appengine.SafelyExtractGaeCtxError(r)
	if c == nil {
		lnp.Printf(s)
	} else {
		aelog.Infof(c, s)
	}

}
Example #3
0
// Adapter() checks the path, takes the time, precomputes values into a map
// provides a global panic catcher
// The typed signature is cleaner than the long version:
//   func Adapter(given func(http.ResponseWriter, *http.Request, map[string]interface{})) http.HandlerFunc {
func Adapter(given ExtendedHandler) http.HandlerFunc {

	return func(w http.ResponseWriter, r *http.Request) {

		start := time.Now()

		c, _ := util_appengine.SafelyExtractGaeCtxError(r)
		lgi := log.Printf
		lge := log.Fatalf
		if c != nil {
			defer logServerTime(c, start)
			// lgi = c.Infof
			lgi = func(format string, v ...interface{}) {
				aelog.Infof(c, format, v...)
			}

			// lge = c.Errorf
			lge = func(format string, v ...interface{}) {
				aelog.Errorf(c, format, v...)
			}

			C = c
		}

		if !authenticate(w, r) {
			return
		}

		//check_against := r.URL.String()
		check_against := r.URL.Path
		matches := validRequestPath.FindStringSubmatch(check_against)
		if matches == nil {
			s := "illegal chars in path: " + check_against
			lgi(s)
			http.Error(w, s, http.StatusInternalServerError)
			return
		}

		s, err := url.Parse(r.URL.String())
		if err != nil {
			panic("Could not url.Parse current url")
		}
		mp := map[string]interface{}{
			"dir":  path.Dir(s.Path),
			"base": path.Base(s.Path),
		}

		defer func() {
			// note: Println works even in panic

			panicSignal := recover()
			if panicSignal != nil {
				miniStacktrace := ""
				for i := 1; i < 11; i++ {
					_, file, line, _ := runtime.Caller(i)
					if strings.Index(file, `/src/pkg/runtime/`) > -1 {
						miniStacktrace += fmt.Sprintf("<br>\n\t\t %s:%d ", file[len(file)-20:], line)
					} else {
						dir := filepath.Dir(file)
						dirLast := filepath.Base(dir)
						file = filepath.Join(dirLast, filepath.Base(file))

						// we cannot determine, whether html is already sent
						// we cannot determine, whether we are in plaintext or html context
						// thus we need the <br>
						miniStacktrace += fmt.Sprintf("<br>\n\t\t /%s:%d ", file, line)
					}
				}

				// headers := w.Header()
				// for k, v := range headers {
				// 	miniStacktrace += fmt.Sprintf("%#v %#v<br>\n", k, v)
				// }
				if panicSignal == "abort_handler_processing" {
					s := fmt.Sprint("\thttp processing aborted\n", miniStacktrace)
					lge(s)
					w.Write([]byte(s))
				} else if panicSignal != nil {
					s := fmt.Sprintf("\tPANIC caught by util_err.Adapter: %v %s\n", panicSignal, miniStacktrace)
					lge(s)
					w.Write([]byte(s))
				}

			}
		}()

		r.Header.Set("adapter_01", "a string set by adapter")

		if c == nil {
			given(w, r, mp)
		} else {
			var given1 AppengineHandler
			given1 = func(c context.Context, w http.ResponseWriter, r *http.Request) {

				given(w, r, mp)

				// automatically set on appengine live, but not on appengine dev
				if r.Header.Get("Content-Type") == "" {
					w.Header().Set("Content-Type", "text/html; charset=utf-8")
				}

				if r.Header.Get("X-Custom-Header-Counter") != "nocounter" {
					cntr := 0
					if true {
						// This seems to cause problems with new applications
						// possible because of missing indize
						distributed_unancestored.Increment(c, mp["dir"].(string)+mp["base"].(string))
						cntr, _ = distributed_unancestored.Count(c, mp["dir"].(string)+mp["base"].(string))
					}
					fmt.Fprintf(w, "<br>\n%v Views<br>\n", cntr)
				}
			}

			if true || appengine.IsDevAppServer() {
				given1(c, w, r)
			} else {
				// wrapped := appstats.NewHandler(given1) // mjibson
				// wrapped.ServeHTTP(w, r)
			}

		}

	}
}