Skip to content

Commit

Permalink
Cleaned up a bit, added Api Add/Update handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin committed May 8, 2014
1 parent 81d1d96 commit 2c89baa
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 39 deletions.
60 changes: 60 additions & 0 deletions api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package main

import(
"fmt"
"net/http"
"encoding/json"
)

type ApiModifyKeySuccess struct {
Key string `json:"key"`
Status string `json:"status"`
Action string `json:"action"`
}

func addKeyHandler(w http.ResponseWriter, r *http.Request) {
keyName := r.URL.Path[len("/tyk/key/"):]
success := true

if r.Method == "POST" || r.Method == "PUT" {
decoder := json.NewDecoder(r.Body)
var newSession SessionState
err := decoder.Decode(&newSession)

if err != nil {
log.Error("Couldn't decode new session object")
log.Error(err)
success = false
} else {
// Update our session object (create it)
authManager.UpdateSession(keyName, newSession)
}

var responseMessage []byte
var action string
if r.Method == "POST" {
action = "added"
} else {
action = "modified"
}

if success {
response := ApiModifyKeySuccess{
keyName,
"ok",
action}

responseMessage, err = json.Marshal(&response)

if err != nil {
log.Error("Could not create response message")
log.Error(err)
responseMessage = []byte(systemError)
}
}

fmt.Fprintf(w, string(responseMessage))
} else {
fmt.Fprintf(w, string(systemError))
}
}
47 changes: 47 additions & 0 deletions gateway.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package main

import(
"fmt"
"net/http"
"net/http/httputil"
)

type ApiError struct {
Message string
}

func handler(p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {

// Check for API key existence
authHeaderValue := r.Header.Get("authorisation")
if authHeaderValue != "" {
// Check if API key valid
key_authorised, thisSessionState := authManager.IsKeyAuthorised(authHeaderValue)
if key_authorised {
// If valid, check if within rate limit
forwardMessage := sessionLimiter.ForwardMessage(&thisSessionState)
if forwardMessage {
success_handler(w, r, p)
} else {
handle_error(w, r, "Rate limit exceeded", 429)
}
authManager.UpdateSession(authHeaderValue, thisSessionState)
} else {
handle_error(w, r, "Key not authorised", 403)
}
} else {
handle_error(w, r, "Authorisation field missing", 400)
}
}
}

func success_handler(w http.ResponseWriter, r *http.Request, p *httputil.ReverseProxy) {
p.ServeHTTP(w, r)
}

func handle_error(w http.ResponseWriter, r *http.Request, err string, err_code int) {
w.WriteHeader(err_code)
thisError := ApiError{fmt.Sprintf("%s", err)}
templates.ExecuteTemplate(w, "error.json", &thisError)
}
42 changes: 8 additions & 34 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import(
"fmt"
"net/url"
"net/http"
"html/template"
"net/http/httputil"
"github.com/Sirupsen/logrus"
"github.com/docopt/docopt.go"
Expand All @@ -24,13 +25,18 @@ var log = logrus.New()
var authManager = AuthorisationManager{}
var sessionLimiter = SessionLimiter{}
var config = Config{}
var templates = &template.Template{}
var systemError string = "{\"status\": \"system error, please contact administrator\"}"

func setupGlobals() {
if config.Storage.Type == "memory" {
authManager = AuthorisationManager{
InMemoryStorageManager{
map[string]string{}}}
}

template_file := fmt.Sprintf("%s/error.json", config.TemplatePath)
templates = template.Must(template.ParseFiles(template_file))
}

func init() {
Expand Down Expand Up @@ -73,13 +79,15 @@ func init() {
}

func main() {
createSampleSession()
remote, err := url.Parse(config.TargetUrl)
if err != nil {
log.Error("Culdn't parse target URL")
log.Error(err)
}

proxy := httputil.NewSingleHostReverseProxy(remote)
http.HandleFunc("/tyk/key/", addKeyHandler)
http.HandleFunc(config.ListenPath, handler(proxy))
targetPort := fmt.Sprintf(":%d", config.ListenPort)
err = http.ListenAndServe(targetPort, nil)
Expand All @@ -88,38 +96,4 @@ func main() {
}
}

func handler(p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {

// Check for API key existence
authHeaderValue := r.Header.Get("authorisation")
if authHeaderValue != "" {
// Check if API key valid
key_authorised, thisSessionState := authManager.IsKeyAuthorised(authHeaderValue)
if key_authorised {
// If valid, check if within rate limit
forwardMessage := sessionLimiter.ForwardMessage(&thisSessionState)
if forwardMessage {
success_handler(w, r, p)
} else {
handle_error(w, r, "Rate limit exceeded", 429)
}
authManager.UpdateSession(authHeaderValue, thisSessionState)
} else {
handle_error(w, r, "Key not authorised", 403)
}
} else {
handle_error(w, r, "Authorisation field missing", 400)
}
}
}

func success_handler(w http.ResponseWriter, r *http.Request, p *httputil.ReverseProxy) {
p.ServeHTTP(w, r)
}

func handle_error(w http.ResponseWriter, r *http.Request, err string, err_code int) {
w.WriteHeader(err_code)
// TODO: This should be a template
fmt.Fprintf(w, "NOT AUTHORISED: %s", err)
}
8 changes: 4 additions & 4 deletions sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import (

// SessionState objects represent a current API session, mainly used for rate limiting.
type SessionState struct {
LastCheck int64
Allowance float64
Rate float64
Per float64
LastCheck int64 `json:"last_check"`
Allowance float64 `json:"allowance"`
Rate float64 `json:"rate"`
Per float64 `json:"per"`
}

// SessionLimiter is the rate limiter for the API, use ForwardMessage() to
Expand Down
2 changes: 1 addition & 1 deletion templates/error.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"error": {{error_message}}
"error": "{{.Message}}"
}

0 comments on commit 2c89baa

Please sign in to comment.