From 7418572026f7ec9a35a85baf2cbf24c205740e4c Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Thu, 29 Dec 2016 23:01:14 -0800 Subject: adding initial implementation of HTTP/2 server push to single content api reponses --- system/api/handlers.go | 55 +++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 21 deletions(-) (limited to 'system/api') diff --git a/system/api/handlers.go b/system/api/handlers.go index 7b59dbd..a152301 100644 --- a/system/api/handlers.go +++ b/system/api/handlers.go @@ -11,6 +11,7 @@ import ( "github.com/ponzu-cms/ponzu/system/api/analytics" "github.com/ponzu-cms/ponzu/system/db" "github.com/ponzu-cms/ponzu/system/item" + "github.com/tidwall/gjson" ) func typesHandler(res http.ResponseWriter, req *http.Request) { @@ -98,7 +99,7 @@ func contentHandler(res http.ResponseWriter, req *http.Request) { return } - if _, ok := item.Types[t]; !ok { + if pt, ok := item.Types[t]; !ok { res.WriteHeader(http.StatusNotFound) return } @@ -114,6 +115,8 @@ func contentHandler(res http.ResponseWriter, req *http.Request) { return } + defer push(res, pt, post) + j, err := fmtJSON(json.RawMessage(post)) if err != nil { res.WriteHeader(http.StatusInternalServerError) @@ -180,6 +183,33 @@ func toJSON(data []string) ([]byte, error) { return buf.Bytes(), nil } +func push(res http.ResponseWriter, pt func() interface{}, data []byte) { + // Push(target string, opts *PushOptions) error + if pusher, ok := res.(http.Pusher); ok { + if p, ok := pt().(item.Pushable); ok { + // get fields to pull values from data + fields := p.Push() + + // parse values from data to push + values := gjson.GetManyBytes(data, fields...) + + // push all values from Pushable items' fields + for i := range values { + val := values[i] + val.ForEach(func(k, v gjson.Result) bool { + err := pusher.Push(v.String(), nil) + if err != nil { + log.Println("Error during Push of value:", v.String()) + } + + return true + }) + } + } + } + +} + // sendData() should be used any time you want to communicate // data back to a foreign client func sendData(res http.ResponseWriter, data []byte, code int) { @@ -193,36 +223,19 @@ func sendData(res http.ResponseWriter, data []byte, code int) { } } -// SendPreflight is used to respond to a cross-origin "OPTIONS" request -func SendPreflight(res http.ResponseWriter) { +// sendPreflight is used to respond to a cross-origin "OPTIONS" request +func sendPreflight(res http.ResponseWriter) { res.Header().Set("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type") res.Header().Set("Access-Control-Allow-Origin", "*") res.WriteHeader(200) return } -// SendJSON returns a Response to a client as JSON -func SendJSON(res http.ResponseWriter, j map[string]interface{}) { - var data []byte - var err error - - data, err = json.Marshal(j) - if err != nil { - log.Println(err) - data, _ = json.Marshal(map[string]interface{}{ - "status": "fail", - "message": err.Error(), - }) - } - - sendData(res, data, 200) -} - // CORS wraps a HandleFunc to respond to OPTIONS requests properly func CORS(next http.HandlerFunc) http.HandlerFunc { return db.CacheControl(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { if req.Method == http.MethodOptions { - SendPreflight(res) + sendPreflight(res) return } -- cgit v1.2.3 From e432bd36da61ac0a498f68fee8785c44ae800cc1 Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Thu, 29 Dec 2016 23:08:38 -0800 Subject: move push func to separate file and adding tidwall/gjson to credits --- system/api/handlers.go | 31 ++----------------------------- system/api/push.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 29 deletions(-) create mode 100644 system/api/push.go (limited to 'system/api') diff --git a/system/api/handlers.go b/system/api/handlers.go index a152301..a01f3dc 100644 --- a/system/api/handlers.go +++ b/system/api/handlers.go @@ -11,7 +11,6 @@ import ( "github.com/ponzu-cms/ponzu/system/api/analytics" "github.com/ponzu-cms/ponzu/system/db" "github.com/ponzu-cms/ponzu/system/item" - "github.com/tidwall/gjson" ) func typesHandler(res http.ResponseWriter, req *http.Request) { @@ -99,7 +98,8 @@ func contentHandler(res http.ResponseWriter, req *http.Request) { return } - if pt, ok := item.Types[t]; !ok { + pt, ok := item.Types[t] + if !ok { res.WriteHeader(http.StatusNotFound) return } @@ -183,33 +183,6 @@ func toJSON(data []string) ([]byte, error) { return buf.Bytes(), nil } -func push(res http.ResponseWriter, pt func() interface{}, data []byte) { - // Push(target string, opts *PushOptions) error - if pusher, ok := res.(http.Pusher); ok { - if p, ok := pt().(item.Pushable); ok { - // get fields to pull values from data - fields := p.Push() - - // parse values from data to push - values := gjson.GetManyBytes(data, fields...) - - // push all values from Pushable items' fields - for i := range values { - val := values[i] - val.ForEach(func(k, v gjson.Result) bool { - err := pusher.Push(v.String(), nil) - if err != nil { - log.Println("Error during Push of value:", v.String()) - } - - return true - }) - } - } - } - -} - // sendData() should be used any time you want to communicate // data back to a foreign client func sendData(res http.ResponseWriter, data []byte, code int) { diff --git a/system/api/push.go b/system/api/push.go new file mode 100644 index 0000000..64eb5b0 --- /dev/null +++ b/system/api/push.go @@ -0,0 +1,36 @@ +package api + +import ( + "log" + + "github.com/ponzu-cms/ponzu/system/item" + + "github.com/tidwall/gjson" +) + +func push(res http.ResponseWriter, pt func() interface{}, data []byte) { + // Push(target string, opts *PushOptions) error + if pusher, ok := res.(http.Pusher); ok { + if p, ok := pt().(item.Pushable); ok { + // get fields to pull values from data + fields := p.Push() + + // parse values from data to push + values := gjson.GetManyBytes(data, fields...) + + // push all values from Pushable items' fields + for i := range values { + val := values[i] + val.ForEach(func(k, v gjson.Result) bool { + err := pusher.Push(v.String(), nil) + if err != nil { + log.Println("Error during Push of value:", v.String()) + } + + return true + }) + } + } + } + +} -- cgit v1.2.3 From 8e2f27efb98c838b215bc1512ae56ddcbd356144 Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Thu, 29 Dec 2016 23:10:54 -0800 Subject: adding http package import --- system/api/push.go | 1 + 1 file changed, 1 insertion(+) (limited to 'system/api') diff --git a/system/api/push.go b/system/api/push.go index 64eb5b0..3378f0c 100644 --- a/system/api/push.go +++ b/system/api/push.go @@ -2,6 +2,7 @@ package api import ( "log" + "net/http" "github.com/ponzu-cms/ponzu/system/item" -- cgit v1.2.3 From 5ce7fad2f26878c4dc7c1e73dcc295f12bd6b7d7 Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Thu, 29 Dec 2016 23:34:26 -0800 Subject: adding req as param to push, initially to get URL Path for http.Pusher#Push --- system/api/handlers.go | 2 +- system/api/push.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'system/api') diff --git a/system/api/handlers.go b/system/api/handlers.go index a01f3dc..40a4a1d 100644 --- a/system/api/handlers.go +++ b/system/api/handlers.go @@ -115,7 +115,7 @@ func contentHandler(res http.ResponseWriter, req *http.Request) { return } - defer push(res, pt, post) + defer push(res, req, pt, post) j, err := fmtJSON(json.RawMessage(post)) if err != nil { diff --git a/system/api/push.go b/system/api/push.go index 3378f0c..5db0a53 100644 --- a/system/api/push.go +++ b/system/api/push.go @@ -9,7 +9,7 @@ import ( "github.com/tidwall/gjson" ) -func push(res http.ResponseWriter, pt func() interface{}, data []byte) { +func push(res http.ResponseWriter, req *http.Request, pt func() interface{}, data []byte) { // Push(target string, opts *PushOptions) error if pusher, ok := res.(http.Pusher); ok { if p, ok := pt().(item.Pushable); ok { @@ -23,7 +23,7 @@ func push(res http.ResponseWriter, pt func() interface{}, data []byte) { for i := range values { val := values[i] val.ForEach(func(k, v gjson.Result) bool { - err := pusher.Push(v.String(), nil) + err := pusher.Push(req.URL.Path+v.String(), nil) if err != nil { log.Println("Error during Push of value:", v.String()) } -- cgit v1.2.3 From 36441a63a3e1886fdaed5ae9f4bcc05c18d52a11 Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Fri, 30 Dec 2016 02:04:50 -0800 Subject: adding Hideable interface and initial implementation, allowing user to define content that can be kept private --- system/api/handlers.go | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) (limited to 'system/api') diff --git a/system/api/handlers.go b/system/api/handlers.go index 40a4a1d..ae21500 100644 --- a/system/api/handlers.go +++ b/system/api/handlers.go @@ -36,11 +36,16 @@ func contentsHandler(res http.ResponseWriter, req *http.Request) { return } - if _, ok := item.Types[t]; !ok { + it, ok := item.Types[t] + if !ok { res.WriteHeader(http.StatusNotFound) return } + if hide(it(), res) { + return + } + count, err := strconv.Atoi(q.Get("count")) // int: determines number of posts to return (10 default, -1 is all) if err != nil { if q.Get("count") == "" { @@ -98,14 +103,18 @@ func contentHandler(res http.ResponseWriter, req *http.Request) { return } + if t == "" || id == "" { + res.WriteHeader(http.StatusBadRequest) + return + } + pt, ok := item.Types[t] if !ok { res.WriteHeader(http.StatusNotFound) return } - if t == "" || id == "" { - res.WriteHeader(http.StatusBadRequest) + if hide(pt(), res) { return } @@ -129,14 +138,29 @@ func contentHandler(res http.ResponseWriter, req *http.Request) { func contentHandlerBySlug(res http.ResponseWriter, req *http.Request) { slug := req.URL.Query().Get("slug") + if slug == "" { + res.WriteHeader(http.StatusBadRequest) + return + } + // lookup type:id by slug key in __contentIndex - post, err := db.ContentBySlug(slug) + t, post, err := db.ContentBySlug(slug) if err != nil { log.Println("Error finding content by slug:", slug, err) res.WriteHeader(http.StatusInternalServerError) return } + it, ok := item.Types[t] + if !ok { + res.WriteHeader(http.StatusBadRequest) + return + } + + if hide(it(), res) { + return + } + j, err := fmtJSON(json.RawMessage(post)) if err != nil { res.WriteHeader(http.StatusInternalServerError) @@ -146,6 +170,16 @@ func contentHandlerBySlug(res http.ResponseWriter, req *http.Request) { sendData(res, j, http.StatusOK) } +func hide(it interface{}, res http.ResponseWriter) bool { + // check if should be hidden + if _, ok := it.(item.Hideable); ok { + res.WriteHeader(http.StatusNotFound) + return true + } + + return false +} + func fmtJSON(data ...json.RawMessage) ([]byte, error) { var msg = []json.RawMessage{} for _, d := range data { -- cgit v1.2.3 From c4afd309b78b5392f427933b7cb423472c4fdbf0 Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Fri, 30 Dec 2016 02:09:23 -0800 Subject: adding hide to types handler --- system/api/handlers.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'system/api') diff --git a/system/api/handlers.go b/system/api/handlers.go index ae21500..07201ff 100644 --- a/system/api/handlers.go +++ b/system/api/handlers.go @@ -15,8 +15,10 @@ import ( func typesHandler(res http.ResponseWriter, req *http.Request) { var types = []string{} - for t := range item.Types { - types = append(types, string(t)) + for t, fn := range item.Types { + if !hide(fn(), res) { + types = append(types, t) + } } j, err := toJSON(types) -- cgit v1.2.3 From 9d366bea7c540edc767ebab9f6a0ace26c25e5fe Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Fri, 30 Dec 2016 13:47:55 -0800 Subject: update Hide method in Hideable interface, change GMT label to UTC --- system/api/handlers.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'system/api') diff --git a/system/api/handlers.go b/system/api/handlers.go index 07201ff..af0808d 100644 --- a/system/api/handlers.go +++ b/system/api/handlers.go @@ -16,7 +16,7 @@ import ( func typesHandler(res http.ResponseWriter, req *http.Request) { var types = []string{} for t, fn := range item.Types { - if !hide(fn(), res) { + if !hide(fn(), res, req) { types = append(types, t) } } @@ -44,7 +44,7 @@ func contentsHandler(res http.ResponseWriter, req *http.Request) { return } - if hide(it(), res) { + if hide(it(), res, req) { return } @@ -116,7 +116,7 @@ func contentHandler(res http.ResponseWriter, req *http.Request) { return } - if hide(pt(), res) { + if hide(pt(), res, req) { return } @@ -159,10 +159,12 @@ func contentHandlerBySlug(res http.ResponseWriter, req *http.Request) { return } - if hide(it(), res) { + if hide(it(), res, req) { return } + defer push(res, req, it, post) + j, err := fmtJSON(json.RawMessage(post)) if err != nil { res.WriteHeader(http.StatusInternalServerError) @@ -172,9 +174,19 @@ func contentHandlerBySlug(res http.ResponseWriter, req *http.Request) { sendData(res, j, http.StatusOK) } -func hide(it interface{}, res http.ResponseWriter) bool { +func hide(it interface{}, res http.ResponseWriter, req *http.Request) bool { // check if should be hidden - if _, ok := it.(item.Hideable); ok { + if h, ok := it.(item.Hideable); ok { + err := h.Hide(req) + if err.Error() == item.AllowHiddenItem { + return false + } + + if err != nil { + res.WriteHeader(http.StatusInternalServerError) + return true + } + res.WriteHeader(http.StatusNotFound) return true } -- cgit v1.2.3 From ca002d0f447f0e9d7a71cd502a20b63277a612ce Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Fri, 30 Dec 2016 14:44:00 -0800 Subject: updating error in addond and checking for nil prior to clling method on err --- system/api/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'system/api') diff --git a/system/api/handlers.go b/system/api/handlers.go index af0808d..8b4a387 100644 --- a/system/api/handlers.go +++ b/system/api/handlers.go @@ -178,7 +178,7 @@ func hide(it interface{}, res http.ResponseWriter, req *http.Request) bool { // check if should be hidden if h, ok := it.(item.Hideable); ok { err := h.Hide(req) - if err.Error() == item.AllowHiddenItem { + if err != nil && err.Error() == item.AllowHiddenItem { return false } -- cgit v1.2.3