Permalink
Browse files

Merge branch 'master' into release

  • Loading branch information...
2 parents 4fdd554 + a2e19a4 commit 81ee0253920a0cf0f8a1fd79478c00830033648e @paulbellamy committed Jan 26, 2013
View
@@ -1,5 +1,3 @@
-(Disclaimer: Mango is extremely experimental, so don't whine to me when it eats your children.)
-
# Mango
Mango is a modular web-application framework for Go, inspired by [Rack](http://github.com/rack/rack) and [PEP333](http://www.python.org/dev/peps/pep-0333/).
@@ -74,6 +72,12 @@ Where:
Provides JSONP support. If a request has a 'callback' parameter, and your application responds with a Content-Type of "application/json", the JSONP middleware will wrap the response in the callback function and set the Content-Type to "application/javascript".
+* Basic Auth
+
+ Usage: mango.BasicAuth(auth func(username string, password string, Request, error) bool, failure func(Env) (Status, Headers, Body))
+
+ Performs HTTP Basic Auth. The auth function returns true if the username and password are accepted. If failure is nil, a default failure page will be used.
+
## Example App
package main
@@ -83,7 +87,7 @@ Where:
)
func Hello(env mango.Env) (mango.Status, mango.Headers, mango.Body) {
- env.Logger().Println("Got a", env.Request().Method, "request for", env.Request().RawURL)
+ env.Logger().Println("Got a", env.Request().Method, "request for", env.Request().RequestURI)
return 200, mango.Headers{}, mango.Body("Hello World!")
}
@@ -120,7 +124,7 @@ An extremely basic middleware package is simply a function:
func SilenceErrors(env mango.Env, app mango.App) (mango.Status, mango.Headers, mango.Body) {
// Call our upstream app
- status, headers, body := app.Call(env)
+ status, headers, body := app(env)
// If we got an error
if status == 500 {
@@ -181,7 +185,7 @@ To use our middleware we would do:
// Initialize our cats middleware with our list of cat_images
cat_images := []string{"ceiling_cat.jpg", "itteh_bitteh_kittehs.jpg", "monorail_cat.jpg"}
- cats_middleware = Cats(cat_images)
+ cats_middleware := Cats(cat_images)
stack.Middleware(cats_middleware) // Include the Cats middleware in our stack
@@ -202,7 +206,7 @@ routeNotFound handler returning a 404.
)
func hello(env mango.Env) (mango.Status, mango.Headers, mango.Body) {
- env.Logger().Println("Got a", env.Request().Method, "request for", env.Request().RawURL)
+ env.Logger().Println("Got a", env.Request().Method, "request for", env.Request().RequestURI)
return 200, mango.Headers{}, mango.Body("Hello World!")
}
View
@@ -0,0 +1,52 @@
+package mango
+
+import (
+ "encoding/base64"
+ "errors"
+ "strings"
+)
+
+func defaultFailure() (Status, Headers, Body) {
+ return 401, Headers{"WWW-Authenticate": []string{"Basic realm=\"Basic\""}, "Content-Type": []string{"text/html"}}, Body("Access Denied.") // default failure page
+}
+
+func BasicAuth(auth func(string, string, Request, error) bool, failure func(Env) (Status, Headers, Body)) Middleware {
+ return func(env Env, app App) (Status, Headers, Body) {
+
+ if auth == nil { // fail auth by default if you use this middleware
+ return defaultFailure()
+ }
+
+ username, password, err := getAuth(env.Request())
+
+ if auth(username, password, *env.Request(), err) { // check users auth function
+ return app(env)
+ }
+
+ if failure == nil { // if no special failure function
+ return defaultFailure()
+ }
+
+ return failure(env)
+ }
+}
+
+// get username and password from header
+func getAuth(req *Request) (string, string, error) {
+
+ auth64 := req.Header.Get("Authorization")
+
+ if auth64 == "" {
+ return "", "", errors.New("No Authorization Header")
+ }
+
+ auth, err := base64.StdEncoding.DecodeString(strings.Replace(auth64, "Basic ", "", 1))
+
+ if err != nil {
+ return "", "", err
+ }
+
+ result := strings.Split(string(auth), ":")
+
+ return result[0], result[1], nil
+}
View
@@ -0,0 +1,87 @@
+package mango
+
+import (
+ "net/http"
+ "testing"
+)
+
+func successPage(env Env) (Status, Headers, Body) {
+ return 200, Headers{"Content-Type": []string{"text/html"}}, Body("auth success")
+}
+
+func failurePage(env Env) (Status, Headers, Body) {
+ return 403, Headers{"Content-Type": []string{"text/html"}}, Body("auth failed")
+}
+
+// Example auth function
+func auth(username string, password string, req Request, err error) bool {
+
+ if username == "foo" && password == "foo" {
+ return true
+ }
+
+ return false
+}
+
+func TestSuccessAuthRequest(t *testing.T) {
+
+ basicAuthStack := new(Stack)
+ basicAuthStack.Middleware(BasicAuth(auth, failurePage))
+
+ basicAuthApp := basicAuthStack.Compile(successPage)
+
+ request, err := http.NewRequest("GET", "http://localhost:3000/", nil)
+ request.SetBasicAuth("foo", "foo")
+
+ status, _, _ := basicAuthApp(Env{"mango.request": &Request{request}})
+
+ if err != nil {
+ t.Error(err)
+ }
+
+ if status != 200 {
+ t.Error("Request did not succeed, expected status 200, got:", status)
+ }
+}
+
+func TestFailureAuthRequest(t *testing.T) {
+
+ basicAuthStack := new(Stack)
+ basicAuthStack.Middleware(BasicAuth(auth, failurePage))
+
+ basicAuthApp := basicAuthStack.Compile(successPage)
+
+ request, err := http.NewRequest("GET", "http://localhost:3000/", nil)
+ request.SetBasicAuth("fail", "fail")
+
+ status, _, _ := basicAuthApp(Env{"mango.request": &Request{request}})
+
+ if err != nil {
+ t.Error(err)
+ }
+
+ if status != 403 {
+ t.Error("Request did not succeed, expected status 403, got:", status)
+ }
+}
+
+func TestFailByDefault(t *testing.T) {
+
+ basicAuthStack := new(Stack)
+ basicAuthStack.Middleware(BasicAuth(nil, nil))
+
+ basicAuthApp := basicAuthStack.Compile(successPage)
+
+ request, err := http.NewRequest("GET", "http://localhost:3000/", nil)
+
+ status, _, _ := basicAuthApp(Env{"mango.request": &Request{request}})
+
+ if err != nil {
+ t.Error(err)
+ }
+
+ // TODO test header
+ if status != 401 {
+ t.Error("Request did not succeed, expected status 403, got:", status)
+ }
+}
View
@@ -1,12 +0,0 @@
-include $(GOROOT)/src/Make.inc
-
-ALL=hello logger session cats_middleware silence_middleware
-
-all: $(ALL)
-
-clean:
- rm -rf *.[68] $(ALL)
-
-%: %.go
- $(GC) $*.go
- $(LD) -o $@ $*.$O
View
@@ -0,0 +1,67 @@
+package main
+
+import (
+ "../../" // Point this to mango
+ "net/http"
+ "os"
+ "io/ioutil"
+ "math/rand"
+ "regexp"
+)
+
+// Our custom middleware
+func Cats(cat_images []string) mango.Middleware {
+ // Initial setup stuff here
+ // Done on application setup
+
+ // Initialize our regex for finding image links
+ regex := regexp.MustCompile("[^\"']+(.jpg|.png|.gif)")
+
+ // This is our middleware's request handler
+ return func(env mango.Env, app mango.App) (mango.Status, mango.Headers, mango.Body) {
+ // Call the upstream application
+ status, headers, body := app(env)
+
+ // Pick a random cat image
+ image_url := cat_images[rand.Int()%len(cat_images)]
+
+ // Substitute in our cat picture
+ body = mango.Body(regex.ReplaceAllString(string(body), image_url))
+
+ // Send the modified response onwards
+ return status, headers, body
+ }
+}
+
+func Hello(env mango.Env) (mango.Status, mango.Headers, mango.Body) {
+ env.Logger().Println("Got a", env.Request().Method, "request for", env.Request().URL)
+
+ response, err := http.Get("http://www.example.com/")
+ if err != nil {
+ env.Logger().Printf("%s", err)
+ os.Exit(1)
+ }
+ defer response.Body.Close()
+
+ contents, err := ioutil.ReadAll(response.Body)
+ if err != nil {
+ env.Logger().Printf("%s", err)
+ os.Exit(1)
+ }
+
+ return 200, mango.Headers{}, mango.Body(contents)
+}
+
+func main() {
+
+ stack := new(mango.Stack)
+ stack.Address = ":3000"
+
+ // Initialize our cats middleware with our list of cat_images
+ cat_images := []string{"http://images.cheezburger.com/completestore/2010/7/4/9440dc57-52a6-4122-9ab3-efd4daa0ff60.jpg", "http://images.icanhascheezburger.com/completestore/2008/12/10/128733944185267668.jpg"}
+ cats_middleware := Cats(cat_images)
+
+ stack.Middleware(cats_middleware) // Include the Cats middleware in our stack
+
+ stack.Run(Hello)
+}
@@ -1,30 +0,0 @@
-package cats
-
-import (
- "mango"
- "rand"
- "regexp"
-)
-
-func Cats(cat_images []string) mango.Middleware {
- // Initial setup stuff here
- // Done on application setup
-
- // Initialize our regex for finding image links
- regex := regexp.MustCompile("[^\"']+(.jpg|.png|.gif)")
-
- // This is our middleware's request handler
- return func(env mango.Env, app mango.App) (mango.Status, mango.Headers, mango.Body) {
- // Call the upstream application
- status, headers, body := app(env)
-
- // Pick a random cat image
- image_url := cat_images[rand.Int()%len(cat_images)]
-
- // Substitute in our cat picture
- body = mango.Body(regex.ReplaceAllString(string(body), image_url))
-
- // Send the modified response onwards
- return status, headers, body
- }
-}
@@ -1,7 +1,7 @@
package main
import (
- "mango"
+ "../../" // Point this to mango
)
func Hello(env mango.Env) (mango.Status, mango.Headers, mango.Body) {
@@ -1,10 +1,13 @@
package main
import (
- "mango"
+ "../../" // Point this at mango
+ "log"
+ "os"
)
func Hello(env mango.Env) (mango.Status, mango.Headers, mango.Body) {
+ env.Logger().Println("Got a", env.Request().Method, "request for", env.Request().URL)
return 200, mango.Headers{}, mango.Body("Hello World!")
}
@@ -1,7 +1,7 @@
package main
import (
- "mango"
+ "../../" // Point this to mango
)
// Our default handler
@@ -1,15 +1,15 @@
package main
import (
- "mango"
+ "../../" // Point this to mango
)
func Hello(env mango.Env) (mango.Status, mango.Headers, mango.Body) {
// to add a session attribute just add it to the map
env.Session()["new_session_attribute"] = "Never Gonna Give You Up"
// To remove a session attribute delete it from the map
- env.Session()["old_session_attribute"] = nil, false
+ delete(env.Session(), "old_session_attribute")
return 200, mango.Headers{}, mango.Body("Hello World!")
}
@@ -0,0 +1,37 @@
+package main
+
+import (
+ "../../" // Point this to mango
+)
+
+// Our custom middleware
+func SilenceErrors(env mango.Env, app mango.App) (mango.Status, mango.Headers, mango.Body) {
+ // Call our upstream app
+ status, headers, body := app(env)
+
+ // If we got an error
+ if status == 500 {
+ // Silence it!
+ status = 200
+ headers = mango.Headers{}
+ body = "Silence is golden!"
+ }
+
+ // Pass the response back to the client
+ return status, headers, body
+}
+
+// Our default handler
+func Hello(env mango.Env) (mango.Status, mango.Headers, mango.Body) {
+ //Return 500 to trigger the silence
+ return 500, mango.Headers{}, mango.Body("Hello World!")
+}
+
+func main() {
+ stack := new(mango.Stack)
+ stack.Address = ":3000"
+
+ stack.Middleware(SilenceErrors) // Include our custom middleware
+
+ stack.Run(Hello)
+}
Oops, something went wrong.

0 comments on commit 81ee025

Please sign in to comment.