github.com/mmatczuk/gohan@v0.0.0-20170206152520-30e45d9bdb69/extension/gohanscript/lib/httpserver.go (about) 1 // Copyright (C) 2016 Juniper Networks, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 // implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package lib 17 18 import ( 19 "bytes" 20 "encoding/json" 21 "io/ioutil" 22 "net/http" 23 //"github.com/k0kubun/pp" 24 "net/http/httptest" 25 26 "github.com/cloudwan/gohan/extension/gohanscript" 27 "github.com/cloudwan/gohan/server" 28 "github.com/cloudwan/gohan/server/middleware" 29 "github.com/cloudwan/gohan/util" 30 "github.com/drone/routes" 31 "github.com/go-martini/martini" 32 "sync" 33 ) 34 35 func init() { 36 gohanscript.RegisterStmtParser("http_server", httpServer) 37 } 38 39 func serveResponse(w http.ResponseWriter, context map[string]interface{}) { 40 response := context["response"] 41 responseCode, ok := context["code"].(int) 42 if !ok { 43 responseCode = 200 44 } 45 if 200 <= responseCode && responseCode < 300 { 46 w.WriteHeader(responseCode) 47 routes.ServeJson(w, response) 48 } else { 49 message := util.MaybeMap(context["exception"]) 50 middleware.HTTPJSONError(w, message["message"].(string), responseCode) 51 } 52 } 53 54 func fillInContext(context middleware.Context, 55 r *http.Request, w http.ResponseWriter, p martini.Params) { 56 context["path"] = r.URL.Path 57 context["http_request"] = r 58 context["http_response"] = w 59 context["params"] = p 60 context["host"] = r.Host 61 context["method"] = r.Method 62 } 63 64 func httpServer(stmt *gohanscript.Stmt) (func(*gohanscript.Context) (interface{}, error), error) { 65 return func(globalContext *gohanscript.Context) (interface{}, error) { 66 m := martini.Classic() 67 var mutex = &sync.Mutex{} 68 history := []interface{}{} 69 server := map[string]interface{}{ 70 "history": history, 71 } 72 m.Handlers() 73 m.Use(middleware.Logging()) 74 m.Use(martini.Recovery()) 75 rawBody := util.MaybeMap(stmt.RawData["http_server"]) 76 paths := util.MaybeMap(rawBody["paths"]) 77 middlewareCode := util.MaybeString(rawBody["middleware"]) 78 if middlewareCode != "" { 79 vm := gohanscript.NewVM() 80 err := vm.LoadString(stmt.File, middlewareCode) 81 82 if err != nil { 83 return nil, err 84 } 85 m.Use(func(w http.ResponseWriter, r *http.Request) { 86 context := globalContext.Extend(nil) 87 fillInContext(context.Data(), r, w, nil) 88 89 reqData, _ := ioutil.ReadAll(r.Body) 90 buff := ioutil.NopCloser(bytes.NewBuffer(reqData)) 91 r.Body = buff 92 var data interface{} 93 if reqData != nil { 94 json.Unmarshal(reqData, &data) 95 } 96 97 context.Set("request", data) 98 vm.Run(context.Data()) 99 }) 100 } 101 m.Use(func(w http.ResponseWriter, r *http.Request) { 102 reqData, _ := ioutil.ReadAll(r.Body) 103 buff := ioutil.NopCloser(bytes.NewBuffer(reqData)) 104 r.Body = buff 105 var data interface{} 106 if reqData != nil { 107 json.Unmarshal(reqData, &data) 108 } 109 mutex.Lock() 110 server["history"] = append(server["history"].([]interface{}), 111 map[string]interface{}{ 112 "method": r.Method, 113 "path": r.URL.String(), 114 "data": data, 115 }) 116 mutex.Unlock() 117 }) 118 for path, body := range paths { 119 methods, ok := body.(map[string]interface{}) 120 if !ok { 121 continue 122 } 123 for method, rawRouteBody := range methods { 124 routeBody, ok := rawRouteBody.(map[string]interface{}) 125 if !ok { 126 continue 127 } 128 code := util.MaybeString(routeBody["code"]) 129 vm := gohanscript.NewVM() 130 err := vm.LoadString(stmt.File, code) 131 if err != nil { 132 return nil, err 133 } 134 switch method { 135 case "get": 136 m.Get(path, func(w http.ResponseWriter, r *http.Request, p martini.Params) { 137 context := globalContext.Extend(nil) 138 fillInContext(context.Data(), r, w, p) 139 vm.Run(context.Data()) 140 serveResponse(w, context.Data()) 141 }) 142 case "post": 143 m.Post(path, func(w http.ResponseWriter, r *http.Request, p martini.Params) { 144 context := globalContext.Extend(nil) 145 fillInContext(context.Data(), r, w, p) 146 requestData, _ := middleware.ReadJSON(r) 147 context.Set("request", requestData) 148 vm.Run(context.Data()) 149 serveResponse(w, context.Data()) 150 }) 151 case "put": 152 m.Put(path, func(w http.ResponseWriter, r *http.Request, p martini.Params) { 153 context := globalContext.Extend(nil) 154 fillInContext(context.Data(), r, w, p) 155 requestData, _ := middleware.ReadJSON(r) 156 context.Set("request", requestData) 157 vm.Run(context.Data()) 158 serveResponse(w, context.Data()) 159 }) 160 case "delete": 161 m.Delete(path, func(w http.ResponseWriter, r *http.Request, p martini.Params) { 162 context := globalContext.Extend(nil) 163 fillInContext(context.Data(), r, w, p) 164 vm.Run(context.Data()) 165 serveResponse(w, context.Data()) 166 }) 167 } 168 } 169 } 170 testMode := stmt.Args["test"].Value(globalContext).(bool) 171 if testMode { 172 ts := httptest.NewServer(m) 173 server["server"] = ts 174 return server, nil 175 } 176 m.RunOnAddr(stmt.Args["address"].Value(globalContext).(string)) 177 return nil, nil 178 }, nil 179 } 180 181 //GetTestServerURL returns URL of ts 182 func GetTestServerURL(server *httptest.Server) string { 183 return server.URL 184 } 185 186 //StopTestServer stops test server 187 func StopTestServer(server *httptest.Server) { 188 server.Close() 189 } 190 191 //GohanServer starts gohan server from configFile 192 func GohanServer(configFile string, test bool) (interface{}, error) { 193 s, err := server.NewServer(configFile) 194 if err != nil { 195 return nil, err 196 } 197 if test { 198 ts := httptest.NewServer(s.Router()) 199 return map[string]interface{}{ 200 "server": ts, 201 "queue": s.Queue(), 202 }, nil 203 } 204 s.Start() 205 return nil, nil 206 }