Пример #1
1
func Example_handler() {
	c := xhandler.Chain{}

	host, _ := os.Hostname()
	conf := xlog.Config{
		// Set some global env fields
		Fields: xlog.F{
			"role": "my-service",
			"host": host,
		},
	}

	// Install the logger handler with default output on the console
	c.UseC(xlog.NewHandler(conf))

	// Plug the xlog handler's input to Go's default logger
	log.SetFlags(0)
	log.SetOutput(xlog.New(conf))

	// Install some provided extra handler to set some request's context fields.
	// Thanks to those handler, all our logs will come with some pre-populated fields.
	c.UseC(xlog.RemoteAddrHandler("ip"))
	c.UseC(xlog.UserAgentHandler("user_agent"))
	c.UseC(xlog.RefererHandler("referer"))
	c.UseC(xlog.RequestIDHandler("req_id", "Request-Id"))

	// Here is your final handler
	h := c.Handler(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		// Get the logger from the context. You can safely assume it will be always there,
		// if the handler is removed, xlog.FromContext will return a NopLogger
		l := xlog.FromContext(ctx)

		// Then log some errors
		if err := errors.New("some error from elsewhere"); err != nil {
			l.Errorf("Here is an error: %v", err)
		}

		// Or some info with fields
		l.Info("Something happend", xlog.F{
			"user":   "******",
			"status": "ok",
		})
	}))
	http.Handle("/", h)

	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.SetOutput(os.Stderr) // make sure we print to console
		log.Fatal(err)
	}
}
Пример #2
0
func main() {
	// Create a REST API resource index
	index := resource.NewIndex()

	// Add a resource on /users[/:user_id]
	users := index.Bind("users", 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", 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)
	}

	c := alice.New()

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

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

	// Install a logger (see https://github.com/rs/xlog)
	c = c.Append(xlog.NewHandler(xlog.Config{}))
	resource.LoggerLevel = resource.LogLevelDebug
	resource.Logger = func(ctx context.Context, level resource.LogLevel, msg string, fields map[string]interface{}) {
		xlog.FromContext(ctx).OutputF(xlog.Level(level), 2, msg, fields)
	}

	// Log API access
	c = c.Append(xaccess.NewHandler())

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

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

	// Serve it
	log.Print("Serving API on http://localhost:8080")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}
Пример #3
0
// SendError writes a REST formated error on the http.ResponseWriter
func (s DefaultResponseSender) SendError(ctx context.Context, headers http.Header, err error, skipBody bool) (context.Context, interface{}) {
	code := 500
	message := "Server Error"
	if err != nil {
		message = err.Error()
		if e, ok := err.(*Error); ok {
			code = e.Code
		}
	}
	if code >= 500 {
		xlog.FromContext(ctx).Errorf("Server error: %v", err)
	}
	if !skipBody {
		payload := map[string]interface{}{
			"code":    code,
			"message": message,
		}
		if e, ok := err.(*Error); ok {
			if e.Issues != nil {
				payload["issues"] = e.Issues
			}
		}
		return ctx, payload
	}
	return ctx, nil
}
Пример #4
0
// EmptyBody writes a http.StatusServiceUnavailable to indicate a temporary service failure
// This response should make the client retry.
func EmptyBody(ctx context.Context, rw http.ResponseWriter, r *http.Request) {
	l := xlog.FromContext(ctx)

	l.Info("emptyBody")

	rw.WriteHeader(http.StatusServiceUnavailable)
	rw.Write(nil)
}
Пример #5
0
// LogHandler instantiates a new xlog HTTP handler using the given log.
func LogHandler() func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ptw := passThroughResponseWriter{200, w}
			start := time.Now()
			next.ServeHTTP(&ptw, r)
			xlog.FromContext(r.Context()).Info(http.StatusText(ptw.StatusCode), xlog.F{
				"duration": time.Now().Sub(start).String(),
				"status":   ptw.StatusCode,
			})
		})
	}
}
Пример #6
0
// Check get the riskScore and aggregate characteristics.
func (f *Forensiq) Check(ctx context.Context, creq CheckRequest) (CheckResponse, error) {
	var (
		uri   *url.URL
		cresp CheckResponse
		err   error
		log   = xlog.FromContext(ctx)
		sts   = xstats.FromContext(ctx)
	)

	var req *http.Request
	{
		uri, err = url.Parse(f.Host)
		if err != nil {
			log.Errorf("error parsing the URL: %s%v", err, xlog.F{"host": f.Host})
			return CheckResponse{}, err
		}
		uri.Path = "/check"
		v := creq.toValues()
		v.Set("ck", f.ClientKey)
		v.Set("output", "JSON")
		uri.RawQuery = v.Encode()
		req, err = http.NewRequest("GET", uri.String(), nil)
		if err != nil {
			return CheckResponse{}, err
		}
		req.Header.Set("Content-Type", "application/json")
	}

	{
		begin := time.Now()
		resp, err := ctxhttp.Do(ctx, f.httpClient, req)
		if err != nil {
			return CheckResponse{}, err
		}
		defer resp.Body.Close()
		sts.Timing("forensiq.request_time", time.Since(begin),
			"request:check",
			"status:"+responseStatus(ctx, resp.StatusCode),
			"status_code:"+strconv.Itoa(resp.StatusCode),
		)
		if resp.StatusCode == http.StatusForbidden {
			log.Errorf("client key is invalid%v", xlog.F{"client_key": f.ClientKey})
			return CheckResponse{}, ErrInvalidClientKey
		}
		if err := json.NewDecoder(resp.Body).Decode(&cresp); err != nil {
			return CheckResponse{}, err
		}
	}

	return cresp, nil
}
Пример #7
0
func eternalRequest(ctx context.Context, rw http.ResponseWriter, r *http.Request) {
	l := xlog.FromContext(ctx)

	hours := time.Duration(rand.Intn(24)) * time.Hour
	l.Infof("eternalRequest %v", hours)

	select {
	case <-time.After(hours):
		rw.Write(response)
		return
	case <-ctx.Done():
		l.Infof("Connection dead")
		return
	}
}
Пример #8
0
// Send sends headers with the given status and marshal the data in JSON
func (s DefaultResponseSender) Send(ctx context.Context, w http.ResponseWriter, status int, headers http.Header, body interface{}) {
	headers.Set("Content-Type", "application/json")
	// Apply headers to the response
	for key, values := range headers {
		for _, value := range values {
			w.Header().Add(key, value)
		}
	}
	w.WriteHeader(status)

	if body != nil {
		j, err := json.Marshal(body)
		if err != nil {
			w.WriteHeader(500)
			xlog.FromContext(ctx).Errorf("Can't build response: %v", err)
			msg := fmt.Sprintf("Can't build response: %s", strconv.Quote(err.Error()))
			w.Write([]byte(fmt.Sprintf("{\"code\": 500, \"msg\": \"%s\"}", msg)))
			return
		}
		if _, err = w.Write(j); err != nil {
			xlog.FromContext(ctx).Errorf("Can't send response: %v", err)
		}
	}
}
Пример #9
0
func longRequest(ctx context.Context, rw http.ResponseWriter, r *http.Request) {
	l := xlog.FromContext(ctx)

	minutes := time.Duration(rand.Intn(60)+30) * time.Minute
	l.Infof("longRequest %v", minutes)

	select {
	case <-time.After(minutes):
		rw.Write(response)
		return
	case <-ctx.Done():
		// make sure we cleanup if the client hangs up
		l.Infof("Connection dead")
		return
	}
}
Пример #10
0
func normalResponse(ctx context.Context, rw http.ResponseWriter, r *http.Request) {
	l := xlog.FromContext(ctx)

	millis := time.Duration(rand.Intn(150)+50) * time.Millisecond

	l.Infof("normal response %vms", millis)

	select {
	case <-time.After(millis):
		rw.Write(response)
		return
	case <-ctx.Done():
		l.Infof("Connection dead")
		return
	}
}
Пример #11
0
func Example_log() {
	ctx := context.TODO() // got from xhandler
	l := xlog.FromContext(ctx)

	// Log a simple message
	l.Debug("message")

	if err := errors.New("some error"); err != nil {
		l.Errorf("Some error happened: %v", err)
	}

	// With optional fields
	l.Debugf("foo %s", "bar", xlog.F{
		"field": "value",
	})
}
Пример #12
0
// NewJWTHandler parse and validates JWT token if present and store it in the net/context
func NewJWTHandler(users *resource.Resource, jwtKeyFunc jwt.Keyfunc) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			token, err := request.ParseFromRequest(r, request.OAuth2Extractor, jwtKeyFunc)
			if err == request.ErrNoTokenInRequest {
				// If no token is found, let REST Layer hooks decide if the resource is public or not
				next.ServeHTTP(w, r)
				return
			}
			if err != nil || !token.Valid {
				// Here you may want to return JSON error
				http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
				return
			}
			claims := token.Claims.(jwt.MapClaims)
			userID, ok := claims["user_id"].(string)
			if !ok || userID == "" {
				// The provided token is malformed, user_id claim is missing
				http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
				return
			}
			// Lookup the user by its id
			ctx := r.Context()
			user, err := users.Get(ctx, userID)
			if user != nil && err == resource.ErrUnauthorized {
				// Ignore unauthorized errors set by ourselves (see AuthResourceHook)
				err = nil
			}
			if err != nil {
				// If user resource storage handler returned an error, respond with an error
				if err == resource.ErrNotFound {
					http.Error(w, "Invalid credential", http.StatusForbidden)
				} else {
					http.Error(w, err.Error(), http.StatusInternalServerError)
				}
				return
			}
			// Store it into the request's context
			ctx = NewContextWithUser(ctx, user)
			r = r.WithContext(ctx)
			// If xlog is setup, store the user as logger field
			xlog.FromContext(ctx).SetField("user_id", user.ID)
			next.ServeHTTP(w, r)
		})
	}
}
Пример #13
0
// NewHandler returns a handler that log access information about each request performed
// on the provided sub-handlers. Uses context's github.com/rs/xlog and
// github.com/rs/xstats if present for logging.
func NewHandler() func(next xhandler.HandlerC) xhandler.HandlerC {
	return func(next xhandler.HandlerC) xhandler.HandlerC {
		return xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
			// Time request
			reqStart := time.Now()

			// Sniff the status and content size for logging
			lw := mutil.WrapWriter(w)

			// Call the next handler
			next.ServeHTTPC(ctx, lw, r)

			// Conpute request duration
			reqDur := time.Since(reqStart)

			// Get request status
			status := responseStatus(ctx, lw.Status())

			// Log request stats
			sts := xstats.FromContext(ctx)
			tags := []string{
				"status:" + status,
				"status_code:" + strconv.Itoa(lw.Status()),
			}
			sts.Timing("request_time", reqDur, tags...)
			sts.Histogram("request_size", float64(lw.BytesWritten()), tags...)

			// Log access info
			log := xlog.FromContext(ctx)
			log.Infof("%s %s %03d", r.Method, ellipsize(r.URL.String(), 100), lw.Status(), xlog.F{
				"method":      r.Method,
				"uri":         r.URL.String(),
				"type":        "access",
				"status":      status,
				"status_code": lw.Status(),
				"duration":    reqDur.Seconds(),
				"size":        lw.BytesWritten(),
			})
		})
	}
}
Пример #14
0
func main() {
	// Create a REST API resource index
	index := resource.NewIndex()

	// Bind user on /users
	users := index.Bind("users", user, mem.NewHandler(), resource.Conf{
		AllowedModes: resource.ReadWrite,
	})

	// Init the db with some users (user registration is not handled by this example)
	secret, _ := schema.Password{}.Validate("secret")
	users.Insert(context.Background(), []*resource.Item{
		{ID: "admin", Updated: time.Now(), ETag: "abcd", Payload: map[string]interface{}{
			"id":       "admin",
			"name":     "Dilbert",
			"password": secret,
		}},
		{ID: "john", Updated: time.Now(), ETag: "efgh", Payload: map[string]interface{}{
			"id":       "john",
			"name":     "John Doe",
			"password": secret,
		}},
	})

	// Bind post on /posts
	posts := index.Bind("posts", post, mem.NewHandler(), resource.Conf{
		AllowedModes: resource.ReadWrite,
	})

	// Protect resources
	users.Use(AuthResourceHook{UserField: "id"})
	posts.Use(AuthResourceHook{UserField: "user"})

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

	// Setup logger
	c := alice.New()
	c = c.Append(xlog.NewHandler(xlog.Config{}))
	c = c.Append(xaccess.NewHandler())
	c = c.Append(xlog.RequestHandler("req"))
	c = c.Append(xlog.RemoteAddrHandler("ip"))
	c = c.Append(xlog.UserAgentHandler("ua"))
	c = c.Append(xlog.RefererHandler("ref"))
	c = c.Append(xlog.RequestIDHandler("req_id", "Request-Id"))
	resource.LoggerLevel = resource.LogLevelDebug
	resource.Logger = func(ctx context.Context, level resource.LogLevel, msg string, fields map[string]interface{}) {
		xlog.FromContext(ctx).OutputF(xlog.Level(level), 2, msg, fields)
	}

	// Setup auth middleware
	c = c.Append(NewBasicAuthHandler(users))

	// Bind the API under /
	http.Handle("/", c.Then(api))

	// Serve it
	log.Print("Serving API on http://localhost:8080")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}
Пример #15
0
func Example() {
	var (
		// Define a user resource schema
		user = schema.Schema{
			Fields: schema.Fields{
				"id": {
					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": {
					Required:   true,
					ReadOnly:   true,
					Filterable: true,
					Sortable:   true,
					OnInit:     schema.Now,
					Validator:  &schema.Time{},
				},
				"updated": {
					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": {
					Required:   true,
					Filterable: true,
					Validator: &schema.String{
						MaxLen: 150,
					},
				},
			},
		}

		// Define a post resource schema
		post = schema.Schema{
			Fields: schema.Fields{
				// 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": {
					Required:   true,
					Filterable: true,
					Validator: &schema.Reference{
						Path: "users",
					},
				},
				"public": {
					Filterable: true,
					Validator:  &schema.Bool{},
				},
				// Sub-documents are handled via a sub-schema
				"meta": {
					Schema: &schema.Schema{
						Fields: schema.Fields{
							"title": {
								Required: true,
								Validator: &schema.String{
									MaxLen: 150,
								},
							},
							"body": {
								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", 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", 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 an alice handler chain (use your preferred one)
	c := alice.New()

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

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

	// Install a logger (see https://github.com/rs/xlog)
	c = c.Append(xlog.NewHandler(xlog.Config{}))
	resource.LoggerLevel = resource.LogLevelDebug
	resource.Logger = func(ctx context.Context, level resource.LogLevel, msg string, fields map[string]interface{}) {
		xlog.FromContext(ctx).OutputF(xlog.Level(level), 2, msg, fields)
	}

	// Log API access
	c = c.Append(xaccess.NewHandler())

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

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

	// Serve it
	log.Print("Serving API on http://localhost:8080")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}
Пример #16
0
func main() {
	index := resource.NewIndex()

	users := index.Bind("users", user, mem.NewHandler(), resource.Conf{
		AllowedModes: resource.ReadWrite,
	})

	users.Alias("admin", url.Values{"filter": []string{`{"admin": true}`}})

	posts := index.Bind("posts", post, mem.NewHandler(), resource.Conf{
		AllowedModes: resource.ReadWrite,
	})

	posts.Bind("followers", "post", postFollower, mem.NewHandler(), resource.Conf{
		AllowedModes: resource.ReadWrite,
	})

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

	// Setup logger
	c := alice.New()
	c = c.Append(xlog.NewHandler(xlog.Config{}))
	c = c.Append(xaccess.NewHandler())
	c = c.Append(xlog.RequestHandler("req"))
	c = c.Append(xlog.RemoteAddrHandler("ip"))
	c = c.Append(xlog.UserAgentHandler("ua"))
	c = c.Append(xlog.RefererHandler("ref"))
	c = c.Append(xlog.RequestIDHandler("req_id", "Request-Id"))
	resource.LoggerLevel = resource.LogLevelDebug
	resource.Logger = func(ctx context.Context, level resource.LogLevel, msg string, fields map[string]interface{}) {
		xlog.FromContext(ctx).OutputF(xlog.Level(level), 2, msg, fields)
	}

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

	// Create and bind the graphql endpoint
	graphql, err := graphql.NewHandler(index)
	if err != nil {
		log.Fatal(err)
	}
	http.Handle("/graphql", c.Then(graphql))
	http.HandleFunc("/graphiql", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
<!DOCTYPE html>
<html>
<head>
  <style>
    html, body {height: 100%; margin: 0; overflow: hidden; width: 100%;}
  </style>
  <link href="//cdn.jsdelivr.net/graphiql/0.4.9/graphiql.css" rel="stylesheet" />
  <script src="//cdn.jsdelivr.net/fetch/0.9.0/fetch.min.js"></script>
  <script src="//cdn.jsdelivr.net/react/0.14.7/react.min.js"></script>
  <script src="//cdn.jsdelivr.net/react/0.14.7/react-dom.min.js"></script>
  <script src="//cdn.jsdelivr.net/graphiql/0.4.9/graphiql.min.js"></script>
</head>
<body>
  <script>
    // Collect the URL parameters
    var parameters = {};
    window.location.search.substr(1).split('&').forEach(function (entry) {
      var eq = entry.indexOf('=');
      if (eq >= 0) {
        parameters[decodeURIComponent(entry.slice(0, eq))] =
          decodeURIComponent(entry.slice(eq + 1));
      }
    });

    // Produce a Location query string from a parameter object.
    function locationQuery(params) {
      return '/graphql?' + Object.keys(params).map(function (key) {
        return encodeURIComponent(key) + '=' +
          encodeURIComponent(params[key]);
      }).join('&');
    }

    // Derive a fetch URL from the current URL, sans the GraphQL parameters.
    var graphqlParamNames = {
      query: true,
      variables: true,
      operationName: true
    };

    var otherParams = {};
    for (var k in parameters) {
      if (parameters.hasOwnProperty(k) && graphqlParamNames[k] !== true) {
        otherParams[k] = parameters[k];
      }
    }
    var fetchURL = locationQuery(otherParams);

    // Defines a GraphQL fetcher using the fetch API.
    function graphQLFetcher(graphQLParams) {
      return fetch(fetchURL, {
        method: 'post',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(graphQLParams),
        credentials: 'include',
      }).then(function (response) {
        return response.text();
      }).then(function (responseBody) {
        try {
          return JSON.parse(responseBody);
        } catch (error) {
          return responseBody;
        }
      });
    }

    // When the query and variables string is edited, update the URL bar so
    // that it can be easily shared.
    function onEditQuery(newQuery) {
      parameters.query = newQuery;
      updateURL();
    }

    function onEditVariables(newVariables) {
      parameters.variables = newVariables;
      updateURL();
    }

    function updateURL() {
      history.replaceState(null, null, locationQuery(parameters));
    }

    // Render <GraphiQL /> into the body.
    React.render(
      React.createElement(GraphiQL, {
        fetcher: graphQLFetcher,
        onEditQuery: onEditQuery,
        onEditVariables: onEditVariables,
		defaultQuery: "{\
  postsList{\
    i: id,\
    m: meta{\
      t: title,\
      b: body},\
    thumb_small_url: thumbnail_url(height:80)\
  }\
}",
      }),
      document.body
    );
  </script>
</body>
</html>`))
	})

	// Inject some fixtures
	fixtures := [][]string{
		{"PUT", "/users/johndoe", `{"name": "John Doe", "ip": "1.2.3.4", "password": "******", "admin": true}`},
		{"PUT", "/users/fan1", `{"name": "Fan 1", "ip": "1.2.3.4", "password": "******"}}`},
		{"PUT", "/users/fan2", `{"name": "Fan 2", "ip": "1.2.3.4", "password": "******"}}`},
		{"PUT", "/users/fan3", `{"name": "Fan 3", "ip": "1.2.3.4", "password": "******"}}`},
		{"PUT", "/users/fan4", `{"name": "Fan 4", "ip": "1.2.3.4", "password": "******"}}`},
		{"PUT", "/posts/ar5qrgukj5l7a6eq2ps0",
			`{
				"user": "******",
				"thumbnail_url": "http://dom.com/image.png",
				"meta": {
					"title": "First Post",
					"body": "This is my first post"
				}
			}`},
		{"POST", "/posts/ar5qrgukj5l7a6eq2ps0/followers", `{"user": "******"}`},
		{"POST", "/posts/ar5qrgukj5l7a6eq2ps0/followers", `{"user": "******"}`},
		{"POST", "/posts/ar5qrgukj5l7a6eq2ps0/followers", `{"user": "******"}`},
	}
	for _, fixture := range fixtures {
		req, err := http.NewRequest(fixture[0], fixture[1], strings.NewReader(fixture[2]))
		if err != nil {
			log.Fatal(err)
		}
		w := httptest.NewRecorder()
		api.ServeHTTP(w, req)
		if w.Code >= 400 {
			log.Fatalf("Error returned for `%s %s`: %v", fixture[0], fixture[1], w)
		}
	}

	// Serve it
	log.Print("Serving API on http://localhost:8080")
	log.Print("Visit http://localhost:8080/graphiql for a GraphiQL UI")
	log.Println("Play with (httpie):\n",
		"- http :8080/graphql query=='{postsList{id,thumb_s_url:thumbnail_url(height:80)}}'\n",
		"- http :8080/graphql query=='{postsList{i:id,m:meta{t:title, b:body},thumb_small_url:thumbnail_url(height:80)}}'\n",
		"- http :8080/graphql query=='{postsList{id,meta{title},user{id,name}}}'\n",
		"- http :8080/graphql query=='{posts(id:\"ar5qrgukj5l7a6eq2ps0\"){followers{post{id,meta{title}},user{id,name}}}}'\n",
		"- http :8080/graphql query=='{posts(id:\"ar5qrgukj5l7a6eq2ps0\"){id,meta{title},followers(limit:2){user{id,name}}}}'")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}
Пример #17
0
func main() {
	flag.Parse()

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

	// Bind user on /users
	users := index.Bind("users", user, mem.NewHandler(), resource.Conf{
		AllowedModes: resource.ReadWrite,
	})

	// Init the db with some users (user registration is not handled by this example)
	secret, _ := schema.Password{}.Validate("secret")
	users.Insert(context.Background(), []*resource.Item{
		{ID: "admin", Updated: time.Now(), ETag: "abcd", Payload: map[string]interface{}{
			"id":       "jack",
			"name":     "Jack Sparrow",
			"password": secret,
		}},
		{ID: "john", Updated: time.Now(), ETag: "efgh", Payload: map[string]interface{}{
			"id":       "john",
			"name":     "John Doe",
			"password": secret,
		}},
	})

	// Bind post on /posts
	posts := index.Bind("posts", post, mem.NewHandler(), resource.Conf{
		AllowedModes: resource.ReadWrite,
	})

	// Protect resources
	users.Use(AuthResourceHook{UserField: "id"})
	posts.Use(AuthResourceHook{UserField: "user"})

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

	// Setup logger
	c := alice.New()
	c = c.Append(xlog.NewHandler(xlog.Config{}))
	c = c.Append(xaccess.NewHandler())
	c = c.Append(xlog.RequestHandler("req"))
	c = c.Append(xlog.RemoteAddrHandler("ip"))
	c = c.Append(xlog.UserAgentHandler("ua"))
	c = c.Append(xlog.RefererHandler("ref"))
	c = c.Append(xlog.RequestIDHandler("req_id", "Request-Id"))
	resource.LoggerLevel = resource.LogLevelDebug
	resource.Logger = func(ctx context.Context, level resource.LogLevel, msg string, fields map[string]interface{}) {
		xlog.FromContext(ctx).OutputF(xlog.Level(level), 2, msg, fields)
	}

	// Setup auth middleware
	jwtSecretBytes := []byte(*jwtSecret)
	c = c.Append(NewJWTHandler(users, func(t *jwt.Token) (interface{}, error) {
		println("paf")
		if t.Method != jwt.SigningMethodHS256 {
			return nil, jwt.ErrInvalidKey
		}
		return jwtSecretBytes, nil
	}))

	// Bind the API under /
	http.Handle("/", c.Then(api))

	// Demo tokens
	jackToken := jwt.New(jwt.SigningMethodHS256)
	jackClaims := jackToken.Claims.(jwt.MapClaims)
	jackClaims["user_id"] = "jack"
	jackTokenString, err := jackToken.SignedString(jwtSecretBytes)
	if err != nil {
		log.Fatal(err)
	}
	johnToken := jwt.New(jwt.SigningMethodHS256)
	johnClaims := johnToken.Claims.(jwt.MapClaims)
	johnClaims["user_id"] = "john"
	johnTokenString, err := johnToken.SignedString(jwtSecretBytes)
	if err != nil {
		log.Fatal(err)
	}

	// Serve it
	log.Print("Serving API on http://localhost:8080")
	log.Printf("Your token secret is %q, change it with the `-jwt-secret' flag", *jwtSecret)
	log.Print("Play with tokens:\n",
		"\n",
		"- http :8080/posts access_token==", johnTokenString, " title=\"John's post\"\n",
		"- http :8080/posts access_token==", johnTokenString, "\n",
		"- http :8080/posts\n",
		"\n",
		"- http :8080/posts access_token==", jackTokenString, " title=\"Jack's post\"\n",
		"- http :8080/posts access_token==", jackTokenString, "\n",
	)
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}
Пример #18
0
func unoconvHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	l := xlog.FromContext(ctx)

	//The whole request body is parsed and up to a total of 34MB bytes of its file parts are stored in memory,
	//with the remainder stored on disk in temporary files.
	r.ParseMultipartForm(32 << 20)
	file, handler, err := r.FormFile("file")
	if err != nil {
		l.Error(err)
		return
	}
	defer file.Close()

	//add the filename to access log
	l.SetField("filename", handler.Filename)

	//create a temporary file and copy the file from the form to it
	tempfile, err := ioutil.TempFile(os.TempDir(), "unoconv-api")
	if err != nil {
		l.Error(err)
		return
	}

	switch filepath.Ext(handler.Filename) {
	case ".txt":
		//read the files content
		data, err := ioutil.ReadAll(file)
		if err != nil {
			l.Error(err)
			return
		}

		//try to convert the textfile (data) to utf-8 and write it to tempfile
		charset, err := toUTF8(data, tempfile)
		l.SetField("charset", charset)
		l.SetField("convertedToUTF8", true)
		if err != nil {
			//Could not convert to utf-8, write the original data to tempfile
			l.Error(err)
			l.SetField("convertedToUTF8", false)
			io.Copy(tempfile, bytes.NewBuffer(data))
		}
	default:
		io.Copy(tempfile, file)
	}

	tempfile.Close()

	//append the file extension to the temporary file's name
	filename := tempfile.Name() + filepath.Ext(handler.Filename)
	os.Rename(tempfile.Name(), filename)
	defer os.Remove(filename)

	//Run unoconv to convert the file
	//unoconv's stdout is plugged directly to the httpResponseWriter
	err = uno.convert(filename, xmux.Param(ctx, "filetype"), w)
	if err != nil {
		l.Error(err)
		return
	}
}
Пример #19
0
func main() {
	index := resource.NewIndex()

	index.Bind("posts", post, restrix.Wrap("posts", mem.NewHandler()), resource.Conf{
		AllowedModes: resource.ReadWrite,
	})

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

	// Setup logger
	c := alice.New()
	c = c.Append(xlog.NewHandler(xlog.Config{}))
	c = c.Append(xaccess.NewHandler())
	resource.LoggerLevel = resource.LogLevelDebug
	resource.Logger = func(ctx context.Context, level resource.LogLevel, msg string, fields map[string]interface{}) {
		xlog.FromContext(ctx).OutputF(xlog.Level(level), 2, msg, fields)
	}

	// Bind the API under the root path
	http.Handle("/", c.Then(api))

	// Configure hystrix commands
	hystrix.Configure(map[string]hystrix.CommandConfig{
		"posts.MultiGet": {
			Timeout:               500,
			MaxConcurrentRequests: 200,
			ErrorPercentThreshold: 25,
		},
		"posts.Find": {
			Timeout:               1000,
			MaxConcurrentRequests: 100,
			ErrorPercentThreshold: 25,
		},
		"posts.Insert": {
			Timeout:               1000,
			MaxConcurrentRequests: 50,
			ErrorPercentThreshold: 25,
		},
		"posts.Update": {
			Timeout:               1000,
			MaxConcurrentRequests: 50,
			ErrorPercentThreshold: 25,
		},
		"posts.Delete": {
			Timeout:               1000,
			MaxConcurrentRequests: 10,
			ErrorPercentThreshold: 10,
		},
		"posts.Clear": {
			Timeout:               10000,
			MaxConcurrentRequests: 5,
			ErrorPercentThreshold: 10,
		},
	})

	// Start the metrics stream handler
	hystrixStreamHandler := hystrix.NewStreamHandler()
	hystrixStreamHandler.Start()
	log.Print("Serving Hystrix metrics on http://localhost:8081")
	go http.ListenAndServe(net.JoinHostPort("", "8081"), hystrixStreamHandler)

	// Inject some fixtures
	fixtures := [][]string{
		{"POST", "/posts", `{"title": "First Post", "body": "This is my first post"}`},
		{"POST", "/posts", `{"title": "Second Post", "body": "This is my second post"}`},
		{"POST", "/posts", `{"title": "Third Post", "body": "This is my third post"}`},
	}
	for _, fixture := range fixtures {
		req, err := http.NewRequest(fixture[0], fixture[1], strings.NewReader(fixture[2]))
		if err != nil {
			log.Fatal(err)
		}
		w := httptest.NewRecorder()
		api.ServeHTTP(w, req)
		if w.Code >= 400 {
			log.Fatalf("Error returned for `%s %s`: %v", fixture[0], fixture[1], w)
		}
	}

	// Serve it
	log.Print("Serving API on http://localhost:8080")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}
Пример #20
0
func main() {
	index := resource.NewIndex()

	index.Bind("users", user, mem.NewHandler(), resource.Conf{
		AllowedModes: resource.ReadWrite,
	})

	posts := index.Bind("posts", post, mem.NewHandler(), resource.Conf{
		AllowedModes: resource.ReadWrite,
	})

	posts.Bind("followers", "post", postFollower, mem.NewHandler(), resource.Conf{
		AllowedModes: resource.ReadWrite,
	})

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

	// Setup logger
	c := alice.New()
	c = c.Append(xlog.NewHandler(xlog.Config{}))
	c = c.Append(xaccess.NewHandler())
	resource.LoggerLevel = resource.LogLevelDebug
	resource.Logger = func(ctx context.Context, level resource.LogLevel, msg string, fields map[string]interface{}) {
		xlog.FromContext(ctx).OutputF(xlog.Level(level), 2, msg, fields)
	}

	// Bind the API under the root path
	http.Handle("/", c.Then(api))

	// Inject some fixtures
	fixtures := [][]string{
		{"PUT", "/users/johndoe", `{"name": "John Doe", "ip": "1.2.3.4", "password": "******"}`},
		{"PUT", "/users/fan1", `{"name": "Fan 1", "ip": "1.2.3.4", "password": "******"}}`},
		{"PUT", "/users/fan2", `{"name": "Fan 2", "ip": "1.2.3.4", "password": "******"}}`},
		{"PUT", "/users/fan3", `{"name": "Fan 3", "ip": "1.2.3.4", "password": "******"}}`},
		{"PUT", "/users/fan4", `{"name": "Fan 4", "ip": "1.2.3.4", "password": "******"}}`},
		{"PUT", "/posts/ar5qrgukj5l7a6eq2ps0",
			`{
				"user": "******",
				"thumbnail_url": "http://dom.com/image.png",
				"meta": {
					"title": "First Post",
					"body": "This is my first post"
				}
			}`},
		{"POST", "/posts/ar5qrgukj5l7a6eq2ps0/followers", `{"user": "******"}`},
		{"POST", "/posts/ar5qrgukj5l7a6eq2ps0/followers", `{"user": "******"}`},
		{"POST", "/posts/ar5qrgukj5l7a6eq2ps0/followers", `{"user": "******"}`},
	}
	for _, fixture := range fixtures {
		req, err := http.NewRequest(fixture[0], fixture[1], strings.NewReader(fixture[2]))
		if err != nil {
			log.Fatal(err)
		}
		w := httptest.NewRecorder()
		api.ServeHTTP(w, req)
		if w.Code >= 400 {
			log.Fatalf("Error returned for `%s %s`: %v", fixture[0], fixture[1], w)
		}
	}

	// Serve it
	log.Print("Serving API on http://localhost:8080")
	log.Println("Play with (httpie):\n",
		"- http :8080/posts fields=='id,thumb_s_url:thumbnail_url(height:80)'\n",
		"- http :8080/posts fields=='i:id,m:meta{t:title,b:body},thumb_small_url:thumbnail_url(height:80)'\n",
		"- http :8080/posts fields=='id,meta,user{id,name}'\n",
		"- http :8080/posts/ar5qrgukj5l7a6eq2ps0/followers fields=='post{id,meta{title}},user{id,name}'\n",
		"- http :8080/posts/ar5qrgukj5l7a6eq2ps0 fields=='id,meta{title},followers(limit:2){user{id,name}}'")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}