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  }