func ExampleAddChain() {
	c := xhandler.Chain{}

	close := xhandler.CloseHandler
	cors := cors.Default().Handler
	timeout := xhandler.TimeoutHandler(2 * time.Second)
	auth := func(next xhandler.HandlerC) xhandler.HandlerC {
		return xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
			if v := ctx.Value("Authorization"); v == nil {
				http.Error(w, "Not authorized", http.StatusUnauthorized)
				return
			}
			next.ServeHTTPC(ctx, w, r)
		})
	}

	c.Add(close, cors, timeout)

	mux := http.NewServeMux()

	// Use c.Handler to terminate the chain with your final handler
	mux.Handle("/", c.Handler(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to the home page!")
	})))

	// Create a new chain from an existing one, and add route-specific middleware to it
	protected := c.With(auth)

	mux.Handle("/admin", protected.Handler(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "protected endpoint!")
	})))
}
Exemple #2
0
func main() {
	// Create a REST API resource index
	index := resource.NewIndex()

	// Add a resource on /users[/:user_id]
	users := index.Bind("users", resource.New(user, mem.NewHandler(), resource.Conf{
		// We allow all REST methods
		// (rest.ReadWrite is a shortcut for []resource.Mode{resource.Create, resource.Read, resource.Update, resource.Delete, resource,List})
		AllowedModes: resource.ReadWrite,
	}))

	// Bind a sub resource on /users/:user_id/posts[/:post_id]
	// and reference the user on each post using the "user" field of the posts resource.
	posts := users.Bind("posts", "user", resource.New(post, mem.NewHandler(), resource.Conf{
		// Posts can only be read, created and deleted, not updated
		AllowedModes: []resource.Mode{resource.Read, resource.List, resource.Create, resource.Delete},
	}))

	// Add a friendly alias to public posts
	// (equivalent to /users/:user_id/posts?filter={"published":true})
	posts.Alias("public", url.Values{"filter": []string{"{\"published\":true}"}})

	// Create API HTTP handler for the resource graph
	api, err := rest.NewHandler(index)
	if err != nil {
		log.Fatalf("Invalid API configuration: %s", err)
	}

	// Init a xhandler chain (see https://github.com/rs/xhandler)
	c := xhandler.Chain{}

	// Add close notifier handler so context is cancelled when the client closes
	// the connection
	c.UseC(xhandler.CloseHandler)

	// Add timeout handler
	c.UseC(xhandler.TimeoutHandler(2 * time.Second))

	// Install a logger (see https://github.com/rs/xlog)
	c.UseC(xlog.NewHandler(xlog.Config{}))

	// Log API access
	c.UseC(xaccess.NewHandler())

	// Add CORS support with passthrough option on so rest-layer can still
	// handle OPTIONS method
	c.UseC(cors.New(cors.Options{OptionsPassthrough: true}).HandlerC)

	// Bind the API under /api/ path
	http.Handle("/api/", http.StripPrefix("/api/", c.Handler(api)))

	// Serve it
	log.Print("Serving API on http://localhost:8080")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}
func ExampleIf() {
	c := xhandler.Chain{}

	// Add a timeout handler only if the URL path matches a prefix
	c.UseC(xhandler.If(
		func(ctx context.Context, w http.ResponseWriter, r *http.Request) bool {
			return strings.HasPrefix(r.URL.Path, "/with-timeout/")
		},
		xhandler.TimeoutHandler(2*time.Second),
	))

	http.Handle("/", c.Handler(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to the home page!")
	})))
}
Exemple #4
0
func construct(o *webOptions) xhandler.HandlerC {
	var chain xhandler.Chain
	chain.UseC(xhandler.CloseHandler)
	chain.UseC(xhandler.TimeoutHandler(o.reqTimeout))
	chain.UseC(o.componentSetter)
	chain.UseC(o.templateCtxSetter)
	for _, m := range o.middlewares {
		chain.UseC(m)
	}
	chain.UseC(CompileInContext)
	chain.UseC(RenderInContext)
	if o.alwaysHTML {
		return chain.HandlerCF(WriteRenderedHTML)
	}
	return chain.HandlerCF(WriteRendered)
}
func ExampleHandleTimeout() {
	var xh xhandler.HandlerC
	xh = xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello World"))
		if _, ok := ctx.Deadline(); ok {
			w.Write([]byte(" with deadline"))
		}
	})

	// This handler adds a timeout to the handler
	xh = xhandler.TimeoutHandler(5 * time.Second)(xh)

	ctx := context.Background()
	// Bridge context aware handlers with http.Handler using xhandler.Handle()
	http.Handle("/", xhandler.New(ctx, xh))

	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}
Exemple #6
0
func ExampleMux() {
	c := xhandler.Chain{}

	// Append a context-aware middleware handler
	c.UseC(xhandler.CloseHandler)

	// Another context-aware middleware handler
	c.UseC(xhandler.TimeoutHandler(2 * time.Second))

	mux := xmux.New()

	// Use c.Handler to terminate the chain with your final handler
	mux.GET("/welcome/:name", xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome %s!", xmux.Param(ctx, "name"))
	}))

	if err := http.ListenAndServe(":8080", c.Handler(mux)); err != nil {
		log.Fatal(err)
	}
}
func ExampleChain() {
	c := xhandler.Chain{}
	// Append a context-aware middleware handler
	c.UseC(xhandler.CloseHandler)

	// Mix it with a non-context-aware middleware handler
	c.Use(cors.Default().Handler)

	// Another context-aware middleware handler
	c.UseC(xhandler.TimeoutHandler(2 * time.Second))

	mux := http.NewServeMux()

	// Use c.Handler to terminate the chain with your final handler
	mux.Handle("/", c.Handler(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to the home page!")
	})))

	// You can reuse the same chain for other handlers
	mux.Handle("/api", c.Handler(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to the API!")
	})))
}
Exemple #8
0
func main() {
	if !checkFlags() {
		os.Exit(1)
	}

	sessionStore := sessions.NewCookieStore([]byte(*sessionHashKey), []byte(*sessionBlockKey))

	// OpenID Connect Providers

	// Google
	oidcGoogleLoginRoute := "/logingoogle"
	oidcGoogleCBRoute := "/gcallback"
	oidcGoogle := &oidc.Google{
		ClientID:     *oidcGoogleClientID,
		ClientSecret: *oidcGoogleClientSecret,
		RedirectURI:  *publicURL + oidcGoogleCBRoute,
		SessionStore: sessionStore,
	}

	// PayPal
	oidcPaypalLoginRoute := "/loginpaypal"
	oidcPaypalCBRoute := "/pcallback"
	oidcPaypal := &oidc.Paypal{
		ClientID:     *oidcPaypalClientID,
		ClientSecret: *oidcPaypalClientSecret,
		RedirectURI:  *publicURL + oidcPaypalCBRoute,
		SessionStore: sessionStore,
	}

	// Dynamodb
	cfg := &aws.Config{}
	if *dynamodbEndpoint != "" {
		cfg.Endpoint = aws.String(*dynamodbEndpoint)
	}
	sess := session.New(cfg)
	if *debug {
		sess.Config.LogLevel = aws.LogLevel(aws.LogDebug)
	}

	// Model
	var m model.Model
	m = awsdynamo.NewModelFromSession(sess)

	// Controller
	// OAuth / OpenID Connect
	authCGoogle := controller.NewAuthController(m.UserPeer(), oidcGoogle, "google")
	authCPaypal := controller.NewAuthController(m.UserPeer(), oidcPaypal, "paypal")

	// Post Controller
	postContrData := &postDataProvider{
		PostPeer: m.PostPeer(),
		UserPeer: m.UserPeer(),
	}
	postController := &controller.PostController{
		Model: postContrData,
	}

	// Middleware
	baseChain := xhandler.Chain{}
	baseChain.UseC(xhandler.TimeoutHandler(2 * time.Second))

	// Session management
	sessionMiddleware := middleware.Session{}
	sessionMiddleware.Init([]byte(*sessionHashKey), []byte(*sessionBlockKey))
	baseChain.UseC(sessionMiddleware.Enable("posty-session"))

	// Chain for authenticated routes
	authedChain := xhandler.Chain{}
	authedChain = append(authedChain, baseChain...)
	authedChain.UseC(middleware.AuthenticatedFilter("/login"))
	authedChain.UseC(middleware.UserContext())

	// Chain for authenticated routes with json response
	jsonChain := xhandler.Chain{}
	jsonChain = append(jsonChain, authedChain...)
	jsonChain.UseC(middleware.JSONWrapper())

	// Chain for unauthenticated routes
	unauthedChain := xhandler.Chain{}
	unauthedChain = append(unauthedChain, baseChain...)
	unauthedChain.UseC(middleware.UnauthenticatedFilter("/"))

	// Main Context
	ctx := context.Background()
	route := func(chain xhandler.Chain, handler xhandler.HandlerC) web.Handler {
		return handle(ctx, chain.HandlerC(handler))
	}

	// Routes
	mux := web.New()
	mux.Get("/api/posts", route(jsonChain, xhandler.HandlerFuncC(postController.Posts)))
	mux.Post("/api/posts", route(jsonChain, xhandler.HandlerFuncC(postController.Create)))
	mux.Delete("/api/posts/:id", route(jsonChain, xhandler.HandlerFuncC(postController.Remove)))
	// OIDC Routes
	mux.Get(oidcGoogleLoginRoute, route(unauthedChain, authCGoogle.Login()))
	mux.Get(oidcGoogleCBRoute, route(unauthedChain, authCGoogle.Callback("/")))
	mux.Get(oidcPaypalLoginRoute, route(unauthedChain, authCPaypal.Login()))
	mux.Get(oidcPaypalCBRoute, route(unauthedChain, authCPaypal.Callback("/")))
	mux.Get("/logout", route(authedChain, authCGoogle.Logout("/login")))

	// Static file
	mux.Get("/login", route(unauthedChain, serveSingleFile(filepath.Join(*frontendPath, "login.html"))))
	mux.Get("/", route(authedChain, serveSingleFile(filepath.Join(*frontendPath, "index.html"))))
	mux.Get("/static/*", route(baseChain, serveFiles(filepath.Join(*frontendPath, "/static"), "/static/")))

	log.Infof("Listening on %s", *listen)
	log.Fatal(http.ListenAndServe(":8080", gctx.ClearHandler(mux)))
}
Exemple #9
0
func Example() {
	var (
		// Define a user resource schema
		user = schema.Schema{
			"id": schema.Field{
				Required: true,
				// When a field is read-only, on default values or hooks can
				// set their value. The client can't change it.
				ReadOnly: true,
				// This is a field hook called when a new user is created.
				// The schema.NewID hook is a provided hook to generate a
				// unique id when no value is provided.
				OnInit: &schema.NewID,
				// The Filterable and Sortable allows usage of filter and sort
				// on this field in requests.
				Filterable: true,
				Sortable:   true,
				Validator: &schema.String{
					Regexp: "^[0-9a-f]{32}$",
				},
			},
			"created": schema.Field{
				Required:   true,
				ReadOnly:   true,
				Filterable: true,
				Sortable:   true,
				OnInit:     &schema.Now,
				Validator:  &schema.Time{},
			},
			"updated": schema.Field{
				Required:   true,
				ReadOnly:   true,
				Filterable: true,
				Sortable:   true,
				OnInit:     &schema.Now,
				// The OnUpdate hook is called when the item is edited. Here we use
				// provided Now hook which just return the current time.
				OnUpdate:  &schema.Now,
				Validator: &schema.Time{},
			},
			// Define a name field as required with a string validator
			"name": schema.Field{
				Required:   true,
				Filterable: true,
				Validator: &schema.String{
					MaxLen: 150,
				},
			},
		}

		// Define a post resource schema
		post = schema.Schema{
			// schema.*Field are shortcuts for common fields (identical to users' same fields)
			"id":      schema.IDField,
			"created": schema.CreatedField,
			"updated": schema.UpdatedField,
			// Define a user field which references the user owning the post.
			// See bellow, the content of this field is enforced by the fact
			// that posts is a sub-resource of users.
			"user": schema.Field{
				Required:   true,
				Filterable: true,
				Validator: &schema.Reference{
					Path: "users",
				},
			},
			"public": schema.Field{
				Filterable: true,
				Validator:  &schema.Bool{},
			},
			// Sub-documents are handled via a sub-schema
			"meta": schema.Field{
				Schema: &schema.Schema{
					"title": schema.Field{
						Required: true,
						Validator: &schema.String{
							MaxLen: 150,
						},
					},
					"body": schema.Field{
						Validator: &schema.String{
							MaxLen: 100000,
						},
					},
				},
			},
		}
	)

	// Create a REST API root resource
	index := resource.NewIndex()

	// Add a resource on /users[/:user_id]
	users := index.Bind("users", resource.New(user, mem.NewHandler(), resource.Conf{
		// We allow all REST methods
		// (rest.ReadWrite is a shortcut for []rest.Mode{Create, Read, Update, Delete, List})
		AllowedModes: resource.ReadWrite,
	}))

	// Bind a sub resource on /users/:user_id/posts[/:post_id]
	// and reference the user on each post using the "user" field of the posts resource.
	posts := users.Bind("posts", "user", resource.New(post, mem.NewHandler(), resource.Conf{
		// Posts can only be read, created and deleted, not updated
		AllowedModes: []resource.Mode{resource.Read, resource.List, resource.Create, resource.Delete},
	}))

	// Add a friendly alias to public posts
	// (equivalent to /users/:user_id/posts?filter={"public":true})
	posts.Alias("public", url.Values{"filter": []string{"{\"public\"=true}"}})

	// Create API HTTP handler for the resource graph
	api, err := rest.NewHandler(index)
	if err != nil {
		log.Fatalf("Invalid API configuration: %s", err)
	}

	// Init a xhandler chain (see https://github.com/rs/xhandler)
	c := xhandler.Chain{}

	// Add close notifier handler so context is cancelled when the client closes
	// the connection
	c.UseC(xhandler.CloseHandler)

	// Add timeout handler
	c.UseC(xhandler.TimeoutHandler(2 * time.Second))

	// Install a logger (see https://github.com/rs/xlog)
	c.UseC(xlog.NewHandler(xlog.Config{}))

	// Log API access
	c.UseC(xaccess.NewHandler())

	// Add CORS support with passthrough option on so rest-layer can still
	// handle OPTIONS method
	c.UseC(cors.New(cors.Options{OptionsPassthrough: true}).HandlerC)

	// Bind the API under /api/ path
	http.Handle("/api/", http.StripPrefix("/api/", c.Handler(api)))

	// Serve it
	log.Print("Serving API on http://localhost:8080")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}