コード例 #1
0
ファイル: file.go プロジェクト: zaolab/sunnified
func (this *FileDownloadView) Publish(ctxt *web.Context) (err error) {
	var file *os.File

	if file, err = os.Open(this.FilePath); err == nil {
		defer file.Close()

		fbase := path.Base(this.FilePath)
		if this.FileName == "" {
			this.FileName = fbase
		}

		header := ctxt.Response.Header()
		header.Set("Content-Type", this.ContentType(ctxt))
		header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, this.FileName))

		var modtime time.Time
		if stat, err := file.Stat(); err == nil {
			modtime = stat.ModTime()
		}

		http.ServeContent(ctxt.Response, ctxt.Request, fbase, modtime, file)
	} else {
		ctxt.SetErrorCode(404)
	}

	return
}
コード例 #2
0
ファイル: handler.go プロジェクト: zaolab/sunnified
func (this *DynamicHandler) GetControlManager(context *web.Context) (cm *controller.ControlManager) {
	this.mutex.RLock()
	var mod, control, act string = this.module, this.controller, this.action
	this.mutex.RUnlock()

	var acterr error
	var action string

	// if mod is not found, the default mod will be used
	// this allows user to use shortcut path
	// especially when there is only one mod
	if s, err := context.PData.String("module"); err == nil {
		if this.ctrlgroup.HasModule(s) || !this.ctrlgroup.HasController(mod, s) {
			mod = s
		} else {
			// controller.HasController(mod, s) == true
			// move {action} to UPath, {controller} to {action}, {mod} to {controller}
			if tmpact, err := context.PData.String("action"); err == nil {
				lenupath := len(context.UPath) + 1
				tmpupath := make(web.UPath, lenupath)
				tmpupath[0] = tmpact
				copy(tmpupath[1:lenupath], context.UPath)
				context.UPath = tmpupath
			}
			if tmpctrl, err := context.PData.String("controller"); err == nil {
				context.PData["action"] = tmpctrl
			}

			context.PData["controller"] = s
			context.PData["module"] = mod
		}
	}
	if s, err := context.PData.String("controller"); err == nil {
		control = s
	}
	if action, acterr = context.PData.String("action"); acterr == nil {
		act = action
	}
	if act == "" {
		act = "_"
	}

	if ctrl := this.ctrlgroup.Controller(mod, control); ctrl != nil {
		// allows for shortcut action to index
		if acterr == nil && act != "_" && !ctrl.HasAction(act) && ctrl.HasAction("index") {
			act = "_"
			lenupath := len(context.UPath) + 1
			tmpupath := make(web.UPath, lenupath)
			tmpupath[0] = action
			copy(tmpupath[1:lenupath], context.UPath)
			context.UPath = tmpupath
			delete(context.PData, "action")
		}

		cm = controller.NewControlManager(context, ctrl, act)
	}

	return
}
コード例 #3
0
ファイル: htmlhead.go プロジェクト: zaolab/sunnified
func (this *HTMLHeadMiddleWare) View(ctxt *web.Context, vw mvc.View) {
	var head *HTMLHead
	if dview, ok := vw.(mvc.DataView); ok && ctxt.MapResourceValue("htmlhead", &head) == nil && head != nil {
		dview.SetData("Htmlhead_Title", head.Title())
		dview.SetData("Htmlhead_Css", head.Css())
		dview.SetData("Htmlhead_Scripts", head.Scripts())
	}
}
コード例 #4
0
ファイル: json.go プロジェクト: zaolab/sunnified
func (this JsonView) Publish(ctxt *web.Context) error {
	ctxt.SetHeader("Content-Type", "application/json; charset=utf-8")

	if this == nil || len(this) == 0 {
		ctxt.Response.Write([]byte{'{', '}'})
		return nil
	}

	jsone := json.NewEncoder(ctxt.Response)
	if err := jsone.Encode(this.getEncodingInterface()); err != nil {
		return err
	}
	return nil
}
コード例 #5
0
ファイル: ctrlmeta.go プロジェクト: zaolab/sunnified
func GetXReqMethod(ctxt *web.Context) ReqMethod {
	var xreq = ctxt.XMethod()

	switch xreq {
	case "POST":
		return REQMETHOD_POST
	case "PUT":
		return REQMETHOD_PUT
	case "DELETE":
		return REQMETHOD_DELETE
	default:
	}

	return REQMETHOD_GET
}
コード例 #6
0
ファイル: ctrl.go プロジェクト: zaolab/sunnified
func (this *ControllerHandler) GetControlManager(context *web.Context) (cm *controller.ControlManager) {
	var (
		act    string = this.action
		action string
		acterr error
	)

	if action, acterr = context.PData.String("action"); acterr == nil {
		act = action
	}
	if act == "" {
		act = "_"
	}

	if acterr == nil && act != "_" && !this.controlmeta.HasAction(act) && this.controlmeta.HasAction("index") {
		act = "_"
		lenupath := len(context.UPath) + 1
		tmpupath := make(web.UPath, lenupath)
		tmpupath[0] = action
		copy(tmpupath[1:lenupath], context.UPath)
		context.UPath = tmpupath
		delete(context.PData, "action")
	}

	cm = controller.NewControlManager(context, this.controlmeta, act)

	return
}
コード例 #7
0
ファイル: csrf.go プロジェクト: zaolab/sunnified
func (this CsrfMiddleWare) View(ctxt *web.Context, vw mvc.View) {
	var csrftoken CsrfTokenGetter

	if ctxt.MapResourceValue("csrftoken", &csrftoken) == nil && csrftoken.context != nil {
		if dview, ok := vw.(mvc.DataView); ok {
			dview.SetData("Csrftoken_Value", csrftoken.Value())
			dview.SetData("Csrftoken_Name", csrftoken.Name())
			dview.SetData("Csrftoken_Formtoken", csrftoken.FormToken())
		}
		if fview, ok := vw.(mvc.TmplView); ok {
			fview.SetViewFunc("URLWToken", func(path string) string {
				return csrftoken.URLWToken(path)
			})
			fview.SetViewFunc("URLWTokenQ", csrftoken.URLWToken)
		}
	}
}
コード例 #8
0
ファイル: htmlhead.go プロジェクト: zaolab/sunnified
func (this *HTMLHeadMiddleWare) Body(ctxt *web.Context) {
	head := &HTMLHead{
		cssbatch:    this.cssbatch,
		scriptbatch: this.scriptbatch,
		css:         make([]string, 0, 1),
		scripts:     make([]string, 0, 1),
		addedcss:    make([]string, 0, 1),
		addedscript: make([]string, 0, 1),
	}
	ctxt.SetResource("htmlhead", head)
	ctxt.SetTitle_ = head.SetTitle

	if this.defaultTitle != "" {
		head.SetTitle(this.defaultTitle)
	}
	if this.defaultCss != nil && len(this.defaultCss) > 0 {
		head.AddCss(this.defaultCss...)
	}
	if this.defaultScripts != nil && len(this.defaultScripts) > 0 {
		head.AddScript(this.defaultScripts...)
	}
}
コード例 #9
0
ファイル: result.go プロジェクト: zaolab/sunnified
func (this *ResultView) Render(ctxt *web.Context) (b []byte, err error) {
	if tmpl, ext, err := this.getTmpl(mvc.GetMvcMeta(ctxt)); err == nil {
		buf := &bytes.Buffer{}
		var jsonp string
		if ext == ".jsonp" {
			jsonp = ctxt.RequestValue("callback")
			if jsonp == "" {
				jsonp = ctxt.RequestValue("jsonp")
			}
			jsonp = strings.TrimSpace(jsonp)
		}
		if jsonp != "" {
			writeJsonpStart(jsonp, buf)
		}
		tmpl.Execute(buf, this.VM)
		if jsonp != "" {
			writeJsonpEnd(jsonp, buf)
		}
		b = buf.Bytes()
	}

	return
}
コード例 #10
0
ファイル: cache.go プロジェクト: zaolab/sunnified
func (this CacheMiddleWare) Request(ctxt *web.Context) {
	switch this.profile {
	case CACHE_PROFILE_NOCACHE:
		ctxt.PrivateNoCache()
	case CACHE_PROFILE_NOSTORE:
		ctxt.PrivateNoStore()
	case CACHE_PROFILE_PUBLIC:
		ctxt.PublicCache(0)
	}
}
コード例 #11
0
ファイル: sunny.go プロジェクト: zaolab/sunnified
func (this *SunnyApp) ServeRequestedEndPoint(w http.ResponseWriter, r *http.Request, rep *router.RequestedEndPoint) {
	atomic.AddInt32(&this.runners, 1)
	defer this.decrunners()

	if atomic.LoadInt32(&this.closed) == 1 || w == nil || r == nil {
		return
	}

	sw := &SunnyResponseWriter{
		Status:         200,
		ResponseWriter: w,
		midwares:       nil,
	}
	w = sw

	var sunctxt *web.Context

	if rep == nil {
		goto notfound
	}

	defer func() {
		if err := recover(); err != nil {
			if re, ok := err.(web.Redirection); ok {
				this.triggerevent(sunctxt, "redirect", map[string]interface{}{"redirection": re})
			} else if e, ok := err.(web.ContextError); ok {
				this.triggerevent(sunctxt, "contexterror", map[string]interface{}{"context.error": e})
				handler.ErrorHtml(w, r, e.Code())
			} else {
				log.Println(err)
				this.triggererror(sunctxt, err)
			}
		}

		if sunctxt != nil {
			log.Println(fmt.Sprintf("ip: %s; r: %s %s; d: %s; %d",
				sunctxt.RemoteAddress().String(), r.Method, r.URL.Path, time.Since(sunctxt.StartTime()).String(),
				w.(*SunnyResponseWriter).Status))
			sunctxt.Close()
		}
		if r.MultipartForm != nil {
			r.MultipartForm.RemoveAll()
		}
	}()

	sunctxt = web.NewSunnyContext(w, r, this.id)
	sunctxt.Event = this.ev.NewSubRouter(event.M{"sunny.context": sunctxt})
	sunctxt.UPath = rep.UPath
	sunctxt.PData = rep.PData
	sunctxt.Ext = rep.Ext
	sunctxt.MaxFileSize = this.MaxFileSize
	sunctxt.ParseRequestData()
	sw.ctxt = sunctxt

	for n, f := range this.resources {
		sunctxt.SetResource(n, f())
	}

	for _, midware := range this.MiddleWares {
		midware.Request(sunctxt)
		defer midware.Cleanup(sunctxt)
	}

	if router.HandleHeaders(sunctxt, rep.EndPoint, rep.Handler) {
		return
	}

	sw.midwares = this.mwareresp
	for _, midware := range this.MiddleWares {
		midware.Body(sunctxt)
	}

	if ctrl, ok := rep.Handler.(controller.ControlHandler); ok {
		ctrlmgr := ctrl.GetControlManager(sunctxt)

		if ctrlmgr == nil {
			goto notfound
		}

		sunctxt.Module = ctrlmgr.ModuleName()
		sunctxt.Controller = ctrlmgr.ControllerName()
		sunctxt.Action = ctrlmgr.ActionName()

		if err := sunctxt.WaitRequestData(); err != nil {
			setreqerror(err, w)
			return
		}

		// TODO: Controller should not matter which is called first..
		// make it a goroutine once determined sunctxt and ctrlmgr is completely thread-safe
		for _, midware := range this.MiddleWares {
			midware.Controller(sunctxt, ctrlmgr)
		}

		state, vw := ctrlmgr.PrepareAndExecute()
		defer ctrlmgr.Cleanup()

		if vw != nil && !sunctxt.IsRedirecting() && !sunctxt.HasError() {
			setFuncMap(sunctxt, vw)

			// TODO: View should not matter which is called first..
			// make it a goroutine once determined sunctxt and ctrlmgr is completely thread-safe
			for _, midware := range this.MiddleWares {
				midware.View(sunctxt, vw)
			}

			if err := ctrlmgr.PublishView(); err != nil {
				log.Println(err)
			}
		}

		if sunctxt.HasError() {
			this.triggerevent(sunctxt, "contexterror", map[string]interface{}{"context.error": sunctxt.AppError()})
			handler.ErrorHtml(w, r, sunctxt.ErrorCode())
		} else if sunctxt.IsRedirecting() {
			this.triggerevent(sunctxt, "redirect", map[string]interface{}{"redirection": sunctxt.Redirection()})
		} else if state != -1 && (state < 200 || state >= 300) {
			handler.ErrorHtml(w, r, state)
		}
	} else {
		if err := sunctxt.WaitRequestData(); err != nil {
			setreqerror(err, w)
			return
		}

		if h, ok := rep.Handler.(web.ContextHandler); ok {
			h.ServeContextHTTP(sunctxt)
		} else {
			rep.Handler.ServeHTTP(w, r)
		}
	}

	return

notfound:
	handler.NotFound(w, r)
}
コード例 #12
0
ファイル: result.go プロジェクト: zaolab/sunnified
func (this *ResultView) Publish(ctxt *web.Context) (err error) {
	names := mvc.GetMvcMeta(ctxt)
	if names[mvc.MVC_ACTION] == "" {
		names[mvc.MVC_ACTION] = "_"
	}

	var tmpl *template.Template
	var ext string

	/*
		var mc = memcache.New("127.0.0.1:11211")
		var item *memcache.Item
		if item, err = mc.Get(ctxt.Request.RequestURI); err == nil {
			ctxt.SetHeader("Content-Type", GetContentType(ext))
			if ctxt.ReqHeaderHas("Accept-Encoding", "gzip") {
				ctxt.SetHeader("Content-Encoding", "gzip")
			}
			ctxt.Response.Write(item.Value)
			return
		}
	*/
	tmpl, ext, err = this.getTmpl(names)

	if err == nil {
		var isjsonp bool
		var jsonp string
		var method = ctxt.Method()

		if ext == ".jsonp" {
			jsonp = ctxt.RequestValue("callback")
			if jsonp == "" {
				jsonp = ctxt.RequestValue("jsonp")
			}

			if (method == "GET" || method == "HEAD") && jsonp != "" && ext == ".jsonp" && validate.IsJSONPCallback(jsonp) {
				ctxt.SetHeader("Content-Type", "application/javascript")
				ctxt.SetHeader("Content-Disposition", "attachment; filename=jsonp.jsonp")
				ctxt.SetHeader("X-Content-Type-Options", "nosniff")
				isjsonp = true
			} else {
				err = errors.New("Invalid jsonp callback")
				log.Println(err)
				ctxt.SetErrorCode(403)
				return
			}
		} else {
			ctxt.SetHeader("Content-Type", GetContentType(ext))
		}

		if method != "HEAD" {
			var err error
			var b *bytes.Buffer = bytes.NewBuffer(make([]byte, 0, 5120))
			var tw io.Writer = io.MultiWriter(ctxt.Response, b)
			var gzipwriter *gzip.Writer

			if ctxt.ReqHeaderHas("Accept-Encoding", "gzip") {
				ctxt.SetHeader("Content-Encoding", "gzip")
				gzipwriter, _ = gzip.NewWriterLevel(tw, gzip.BestSpeed)
				tw = gzipwriter
			}

			ctxt.SetHeader("Vary", "Accept-Encoding")
			ctxt.Response.WriteHeader(200)

			if isjsonp {
				writeJsonpStart(jsonp, tw)
			}

			err = tmpl.Execute(tw, this.VM)

			if err != nil {
				// Header already sent... multiple write headers
				//panic(err)
				log.Println(err)
			}

			if isjsonp {
				writeJsonpEnd(jsonp, tw)
			}

			if gzipwriter != nil {
				gzipwriter.Close()
			}

			//mc.Set(&memcache.Item{Key: ctxt.Request.RequestURI, Value: b.Bytes(), Expiration: 3600})

			if flushw, ok := ctxt.RootResponse().(http.Flusher); ok {
				flushw.Flush()
			}
		} else {
			ctxt.Response.WriteHeader(200)
		}
	} else {
		log.Println(err)
		ctxt.SetErrorCode(500)
	}

	return
}
コード例 #13
0
ファイル: viewfunc.go プロジェクト: zaolab/sunnified
// TODO: refactor this
func setFuncMap(sunctxt *web.Context, vw mvc.View) {
	if fview, ok := vw.(mvc.TmplView); ok {
		fview.SetViewFunc("URLQ", sunctxt.URL)
		fview.SetViewFunc("URL", func(s string) string {
			return sunctxt.URL(s)
		})
		fview.SetViewFunc("Request", func() *http.Request {
			return sunctxt.Request
		})
		fview.SetViewFunc("QueryStr", sunctxt.QueryStr)
		fview.SetViewFunc("TimeNow", sunctxt.StartTime)
		fview.SetViewFunc("Nl2br", func(s string) template.HTML {
			s = strings.Replace(s, "\r\n", "\n", -1)
			s = strings.Replace(s, "\r", "\n", -1)
			return template.HTML(strings.Replace(template.HTMLEscapeString(s), "\n", "<br>\n", -1))
		})
		fview.SetViewFunc("SelectOption", func(selected ...string) template.HTMLAttr {
			if len(selected) == 2 && selected[0] == selected[1] {
				return " selected "
			}
			return ""
		})
		fview.SetViewFunc("SelectMultiOption", func(value []string, selected string) template.HTMLAttr {
			if validate.IsIn(selected, value...) {
				return " selected "
			}
			return ""
		})
		fview.SetViewFunc("CheckOption", func(selected ...string) template.HTMLAttr {
			if len(selected) == 2 && selected[0] == selected[1] {
				return " checked "
			}
			return ""
		})
		fview.SetViewFunc("RawHtml", func(s string) template.HTML {
			return template.HTML(s)
		})
		fview.SetViewFunc("User", func() interface{} {
			if sunctxt.Session != nil {
				return sunctxt.Session.AuthUser()
			}
			return nil
		})
		fview.SetViewFunc("Session", func() web.SessionManager {
			if sunctxt.Session != nil {
				return sunctxt.Session
			}
			return nil
		})
		fview.SetViewFunc("Json", func(i interface{}) template.HTML {
			b, _ := json.Marshal(i)
			return template.HTML(b)
		})
		fview.SetViewFunc("Implode", func(join string, slice []string) template.HTML {
			buf := bytes.Buffer{}
			for _, s := range slice {
				buf.WriteString(template.HTMLEscapeString(s))
				buf.WriteString(join)
			}
			if len(slice) > 0 {
				buf.Truncate(buf.Len() - len(join))
			}
			return template.HTML(buf.String())
		})
		fview.SetViewFunc("Flashes", sunctxt.AllFlashes)
		fview.SetViewFunc("CropText", func(s string, l int) string {
			if len(s) > l {
				s = s[:l-3] + "..."
			}
			return s
		})
		fview.SetViewFunc("IRange", func(i ...int) (arr []int) {
			count := len(i)
			switch count {
			case 0:
				arr = make([]int, 0)
			case 1:
				arr = make([]int, i[0]+1)
				for k := range arr {
					arr[k] = k
				}
			case 2:
				arr = make([]int, int(math.Abs(float64(i[1]-i[0])))+1)
				if i[0] > i[1] {
					for k := range arr {
						arr[k] = i[0]
						i[0]--
					}
				} else {
					for k := range arr {
						arr[k] = i[0]
						i[0]++
					}
				}
			case 3:
				if i[0] > i[1] {
					i[1] = i[1] - 1
				} else {
					i[1] = i[1] + 1
				}
				size := (float64(i[1]) - float64(i[0])) / float64(i[2])
				if size < 0 {
					return
				}
				arr = make([]int, int(math.Floor(size+0.5)))
				for k := range arr {
					arr[k] = i[0]
					i[0] = i[0] + i[2]
				}
			}
			return
		})
		fview.SetViewFunc("Limit", func(slice interface{}, limit int) interface{} {
			refslice := reflect.ValueOf(slice)
			if (refslice.Kind() == reflect.Slice || refslice.Kind() == reflect.Array) &&
				refslice.Cap() > limit {
				slice = refslice.Slice(0, limit).Interface()
			}
			return slice
		})
		fview.SetViewFunc("Add", func(num1 int, num2 int) int {
			return num1 + num2
		})
		fview.SetViewFunc("Sub", func(num1 int, num2 int) int {
			return num1 - num2
		})
	}
}
コード例 #14
0
ファイル: html.go プロジェクト: zaolab/sunnified
func (this *HtmlView) Publish(ctxt *web.Context) (err error) {
	names := mvc.GetMvcMeta(ctxt)
	if names[mvc.MVC_ACTION] == "" {
		names[mvc.MVC_ACTION] = "_"
	}

	var tmpl *template.Template
	tmpl, err = this.getTmpl(names)

	if err == nil {
		var method = ctxt.Method()

		ctxt.SetHeader("Content-Type", "text/html; charset=utf-8")

		if method != "HEAD" {
			var err error
			var tw io.Writer = ctxt.Response
			var gzipwriter *gzip.Writer

			if ctxt.ReqHeaderHas("Accept-Encoding", "gzip") {
				ctxt.SetHeader("Content-Encoding", "gzip")
				gzipwriter, _ = gzip.NewWriterLevel(ctxt.Response, gzip.BestSpeed)
				tw = gzipwriter
			}

			ctxt.SetHeader("Vary", "Accept-Encoding")
			ctxt.Response.WriteHeader(200)

			err = tmpl.Execute(tw, this.VM)

			if err != nil {
				// Header already sent... multiple write headers
				//panic(err)
				log.Println(err)
			}

			if gzipwriter != nil {
				gzipwriter.Close()
			}

			if flushw, ok := ctxt.Response.(http.Flusher); ok {
				flushw.Flush()
			}
		} else {
			ctxt.Response.WriteHeader(200)
		}
	} else {
		log.Println(err)
		ctxt.SetErrorCode(500)
	}

	return
}
コード例 #15
0
ファイル: csrf.go プロジェクト: zaolab/sunnified
func (this CsrfMiddleWare) Controller(ctxt *web.Context, _ *controller.ControlManager) {
	token := csrfgate.CSRFToken(ctxt.Response, ctxt.Request)
	csrftoken := CsrfTokenGetter{context: ctxt, token: token}
	ctxt.SetResource("csrftoken", csrftoken)
}
コード例 #16
0
ファイル: websocket.go プロジェクト: zaolab/sunnified
func (this *WebSocketHandler) ServeContextHTTP(context *web.Context) {
	var ctrlmgr *controller.ControlManager = controller.NewControlManager(context, this.ctrl, "_")

	if err := context.ToWebSocket(nil, nil); err != nil {
		context.RaiseAppError("Unable to upgrade to websocket: " + err.Error())
	}

	defer func() {
		if err := recover(); err != nil {
			log.Println(err)
		}
	}()

	ctrlmgr.Prepare()

	var (
		ctrler  = ctrlmgr.Controller()
		listen  = &methMeta{method: ctrler.MethodByName("Listen_")}
		methods = make(map[string]*methMeta)
		meth    *methMeta
		ok      bool
		t       reflect.Type
	)

	if listen.method.IsValid() {
		t = listen.method.Type()
		if t.NumIn() != 2 || t.In(0) != intType || t.In(1) != retbType {
			listen.method = reflect.Value{}
		}
	}
	listen.rettype = getRetType(listen.method, t)
	listen.isvalid = listen.method.IsValid()

	for {
		msgT, p, err := context.WebSocket.ReadMessage()

		if err != nil {
			if m := ctrler.MethodByName("Error_"); m.IsValid() && m.Type().NumIn() == 1 &&
				m.Type().In(0) == errType {

				m.Call([]reflect.Value{reflect.ValueOf(err)})
			}

			break
		}

		if this.allowcmd && msgT == websocket.TextMessage && len(p) > 0 && p[0] == '/' {
			args := WSArgs(argSplit.Split(strings.TrimSpace(string(p[1:len(p)])), -1))
			cmd := strings.Replace(strings.Title(args[0]), "-", "_", -1)

			if cmd[len(cmd)-1] != '_' && validCmd.MatchString(cmd) {
				args = args[1:len(args)]

				if meth, ok = methods[cmd]; !ok {
					m := ctrler.MethodByName(cmd)

					if m.IsValid() {
						t = m.Type()
						if t.NumIn() != 1 || t.In(0) != argsType {
							m = reflect.Value{}
						}
						meth = &methMeta{
							method:  m,
							rettype: getRetType(m, t),
							isvalid: m.IsValid(),
						}
					} else {
						meth = &methMeta{
							method:  m,
							rettype: 0,
							isvalid: false,
						}
					}

					methods[cmd] = meth
				}

				if meth.isvalid {
					write(meth, context.WebSocket, meth.method.Call([]reflect.Value{reflect.ValueOf(args)}))
					continue
				}
			}
		}

		if listen.isvalid {
			write(listen, context.WebSocket,
				listen.method.Call([]reflect.Value{reflect.ValueOf(msgT), reflect.ValueOf(p)}))
		}
	}
}