<commit_before>package main

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"net/http/pprof"
	"os"
	"strings"
	"time"

	"github.com/grpc-ecosystem/go-grpc-middleware"
	"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
	"github.com/grpc-ecosystem/go-grpc-middleware/tags"
	"github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/mwitkow/go-conntrack"
	"github.com/mwitkow/go-conntrack/connhelpers"
	"github.com/mwitkow/go-flagz"
	"github.com/mwitkow/go-httpwares/logging/logrus"
	"github.com/mwitkow/go-httpwares/tags"
	"github.com/mwitkow/go-httpwares/tracing/debug"
	"github.com/mwitkow/grpc-proxy/proxy"
	pb_config "github.com/mwitkow/kedge/_protogen/kedge/config"
	grpc_director "github.com/mwitkow/kedge/grpc/director"
	http_director "github.com/mwitkow/kedge/http/director"
	"github.com/mwitkow/kedge/lib/http/ctxtags"
	"github.com/mwitkow/kedge/lib/logstash"
	"github.com/mwitkow/kedge/lib/sharedflags"
	"github.com/pressly/chi"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	log "github.com/sirupsen/logrus"
	"golang.org/x/net/trace"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"github.com/mwitkow/kedge/lib/discovery"
	"context"
	"github.com/mwitkow/go-httpwares/metrics/prometheus"
	"github.com/mwitkow/go-httpwares/reporter"
)

var (
	flagBindAddr    = sharedflags.Set.String("server_bind_address", "0.0.0.0", "address to bind the server to")
	flagGrpcTlsPort = sharedflags.Set.Int("server_grpc_tls_port", 8444, "TCP TLS port to listen on for secure gRPC calls. If 0, no gRPC-TLS will be open.")
	flagHttpTlsPort = sharedflags.Set.Int("server_http_tls_port", 8443, "TCP port to listen on for HTTPS. If gRPC call will hit it will bounce to gRPC handler. If 0, no TLS will be open.")
	flagHttpPort    = sharedflags.Set.Int("server_http_port", 8080, "TCP port to listen on for HTTP1.1/REST calls for debug endpoints like metrics, flagz page or optional pprof (insecure, but private only IP are allowed). If 0, no insecure HTTP will be open.")

	flagHttpMaxWriteTimeout = sharedflags.Set.Duration("server_http_max_write_timeout", 10*time.Second, "HTTP server config, max write duration.")
	flagHttpMaxReadTimeout  = sharedflags.Set.Duration("server_http_max_read_timeout", 10*time.Second, "HTTP server config, max read duration.")
	flagGrpcWithTracing     = sharedflags.Set.Bool("server_tracing_grpc_enabled", true, "Whether enable gRPC tracing (could be expensive).")

	flagLogstashAddress = sharedflags.Set.String("logstash_hostport", "", "Host:port of logstash for remote logging. If empty remote logging is disabled.")

	flagLogTestBackendpoolResolution = sharedflags.Set.Bool("log_backend_resolution_on_addition", false, "With this option "+
		"kedge will always try to resolve and log (only) new backend entry. Useful for debugging backend routings.")

	flagDynamicRoutingDiscoveryEnabled = sharedflags.Set.Bool("kedge_dynamic_routings_enabled", false,
		"If enabled, kedge will watch on service changes (services which has particular label) and generates " +
			"director & backendpool routings. It will update them directly into into flagz value, so you can see the current routings anytime in debug/flagz")
)

func main() {
	log.SetOutput(os.Stdout)
	if err := sharedflags.Set.Parse(os.Args); err != nil {
		log.WithError(err).Fatalf("failed parsing flags")
	}
	if err := flagz.ReadFileFlags(sharedflags.Set); err != nil {
		log.WithError(err).Fatalf("failed reading flagz from files")
	}

	if *flagLogstashAddress != "" {
		formatter, err := logstash.NewFormatter()
		if err != nil {
			log.WithError(err).Fatal("Failed to get hostname for logstash formatter")
		}

		hook, err := logstash.NewHook(*flagLogstashAddress, formatter)
		if err != nil {
			log.WithError(err).Fatal("Failed to create new logstash hook")
		}
		log.AddHook(hook)
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	if *flagDynamicRoutingDiscoveryEnabled {
		log.Info("Flag 'kedge_dynamic_routings_enabled' is true. Enabling dynamic routing with base configuration fetched from provided" +
			"directorConfig and backendpoolConfig.")
		routingDiscovery, err := discovery.NewFromFlags(
			log.StandardLogger(),
			flagConfigDirector.Get().(*pb_config.DirectorConfig),
			flagConfigBackendpool.Get().(*pb_config.BackendPoolConfig),
		)
		if err != nil {
			log.WithError(err).Fatal("Failed to create routingDiscovery")
		}

		go func() {
			err := routingDiscovery.DiscoverAndSetFlags(
				ctx,
				flagConfigDirector,
				flagConfigBackendpool,
			)
			if err != nil {
				log.WithError(err).Fatal("Dynamic Routing Discovery failed")
			}
		}()
	}

	grpc.EnableTracing = *flagGrpcWithTracing
	logEntry := log.NewEntry(log.StandardLogger())
	grpc_logrus.ReplaceGrpcLogger(logEntry)
	tlsConfig, err := buildTLSConfigFromFlags()
	if err != nil {
		log.Fatalf("failed building TLS config from flags: %v", err)
	}

	httpDirector := http_director.New(httpBackendPool, httpRouter, httpAddresser, logEntry)
	grpcDirector := grpc_director.New(grpcBackendPool, grpcRouter)

	// GRPC kedge.
	grpcDirectorServer := grpc.NewServer(
		grpc.CustomCodec(proxy.Codec()), // needed for director to function.
		grpc.UnknownServiceHandler(proxy.TransparentHandler(grpcDirector)),
		grpc_middleware.WithUnaryServerChain(
			grpc_ctxtags.UnaryServerInterceptor(),
			grpc_logrus.UnaryServerInterceptor(logEntry),
			grpc_prometheus.UnaryServerInterceptor,
		),
		grpc_middleware.WithStreamServerChain(
			grpc_ctxtags.StreamServerInterceptor(),
			grpc_logrus.StreamServerInterceptor(logEntry),
			grpc_prometheus.StreamServerInterceptor,
		),
		grpc.Creds(credentials.NewTLS(tlsConfig)),
	)

	// HTTPS proxy chain.
	httpDirectorChain := chi.Chain(
		http_ctxtags.Middleware("proxy"),
		http_reporter.Middleware(http_prometheus.ServerMetrics(http_prometheus.WithLatency())),
		http_debug.Middleware(),
		http_logrus.Middleware(logEntry, http_logrus.WithLevels(kedgeCodeToLevel)),

	)

	// HTTP debug chain.
	httpDebugChain := chi.Chain(
		http_ctxtags.Middleware("debug"),
		http_debug.Middleware(),
	)
	// httpNonAuthDebugChain chain is shares the same base but will not include auth. It is for metrics and _healthz.
	httpNonAuthDebugChain := httpDebugChain

	authorizer, err := authorizerFromFlags(logEntry)
	if err != nil {
		log.WithError(err).Fatal("failed to create authorizer.")
	}

	if authorizer != nil {
		httpDirectorChain = append(httpDirectorChain, http_director.AuthMiddleware(authorizer))
		logEntry.Info("configured OIDC authorization for HTTPS proxy.")
	}

	// Bouncer.
	httpsBouncerServer := httpsBouncerServer(grpcDirectorServer, httpDirectorChain.Handler(httpDirector), logEntry)

	if authorizer != nil && *flagEnableOIDCAuthForDebugEnpoints {
		httpDebugChain = append(httpDebugChain, http_director.AuthMiddleware(authorizer))
		logEntry.Info("configured OIDC authorization for HTTP debug server.")
	}

	// Debug.
	httpDebugServer, err := debugServer(logEntry, httpDebugChain, httpNonAuthDebugChain)
	if err != nil {
		log.WithError(err).Fatal("failed to create debug Server.")
	}

	errChan := make(chan error)
	var grpcTlsListener net.Listener
	var httpPlainListener net.Listener
	var httpTlsListener net.Listener
	if *flagGrpcTlsPort != 0 {
		grpcTlsListener = buildListenerOrFail("grpc_tls", *flagGrpcTlsPort)
	}
	if *flagHttpPort != 0 {
		httpPlainListener = buildListenerOrFail("http_plain", *flagHttpPort)
	}
	if *flagHttpTlsPort != 0 {
		httpTlsListener = buildListenerOrFail("http_tls", *flagHttpTlsPort)
		http2TlsConfig, err := connhelpers.TlsConfigWithHttp2Enabled(tlsConfig)
		if err != nil {
			log.Fatalf("failed setting up HTTP2 TLS config: %v", err)
		}
		httpTlsListener = tls.NewListener(httpTlsListener, http2TlsConfig)
	}

	if grpcTlsListener != nil {
		log.Infof("listening for gRPC TLS on: %v", grpcTlsListener.Addr().String())
		go func() {
			if err := grpcDirectorServer.Serve(grpcTlsListener); err != nil {
				errChan <- fmt.Errorf("grpc_tls server error: %v", err)
			}
		}()
	}
	if httpTlsListener != nil {
		log.Infof("listening for HTTP TLS on: %v", httpTlsListener.Addr().String())
		go func() {
			if err := httpsBouncerServer.Serve(httpTlsListener); err != nil {
				errChan <- fmt.Errorf("http_tls server error: %v", err)
			}
		}()
	}
	if httpPlainListener != nil {
		log.Infof("listening for HTTP Plain on: %v", httpPlainListener.Addr().String())
		go func() {
			if err := httpDebugServer.Serve(httpPlainListener); err != nil {
				errChan <- fmt.Errorf("http_plain server error: %v", err)
			}
		}()
	}

	err = <-errChan // this waits for some server breaking
	log.WithError(err).Fatalf("Fail")
}

// httpsBouncerHandler decides what kind of requests it is and redirects to GRPC if needed.
func httpsBouncerServer(grpcHandler *grpc.Server, httpHandler http.Handler, logEntry *log.Entry) *http.Server {
	httpBouncerHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		if req.URL.Path == "/_healthz" {
			healthEndpoint(w, req)
			return
		}
		if strings.HasPrefix(req.Header.Get("content-type"), "application/grpc") {
			grpcHandler.ServeHTTP(w, req)
			return
		}
		httpHandler.ServeHTTP(w, req)
	}).ServeHTTP

	return &http.Server{
		WriteTimeout: *flagHttpMaxWriteTimeout,
		ReadTimeout:  *flagHttpMaxReadTimeout,
		ErrorLog:     http_logrus.AsHttpLogger(logEntry.WithField(ctxtags.TagForScheme, "tls")),
		Handler:      http.HandlerFunc(httpBouncerHandler),
	}
}

func debugServer(logEntry *log.Entry, middlewares chi.Middlewares, noAuthMiddlewares chi.Middlewares) (*http.Server, error) {
	m := chi.NewMux()
	m.Handle("/_healthz", noAuthMiddlewares.HandlerFunc(healthEndpoint))
	m.Handle("/debug/metrics", noAuthMiddlewares.Handler(promhttp.Handler()))

	m.Handle("/_version",
		// The only one worth to log.
		chi.Chain(http_logrus.Middleware(logEntry.WithField(ctxtags.TagForScheme, "plain"), http_logrus.WithLevels(kedgeCodeToLevel))).
			Handler(middlewares.HandlerFunc(versionEndpoint)))
	m.Handle("/debug/flagz", middlewares.HandlerFunc(flagz.NewStatusEndpoint(sharedflags.Set).ListFlags))

	m.Handle("/debug/pprof/", middlewares.HandlerFunc(pprof.Index))
	m.Handle("/debug/pprof/cmdline", middlewares.HandlerFunc(pprof.Cmdline))
	m.Handle("/debug/pprof/profile", middlewares.HandlerFunc(pprof.Profile))
	m.Handle("/debug/pprof/symbol", middlewares.HandlerFunc(pprof.Symbol))
	m.Handle("/debug/pprof/trace", middlewares.HandlerFunc(pprof.Trace))
	m.Handle("/debug/traces", middlewares.HandlerFunc(trace.Traces))
	m.Handle("/debug/events", middlewares.HandlerFunc(trace.Events))

	return &http.Server{
		WriteTimeout: *flagHttpMaxWriteTimeout,
		ReadTimeout:  *flagHttpMaxReadTimeout,
		ErrorLog:     http_logrus.AsHttpLogger(logEntry.WithField(ctxtags.TagForScheme, "tls")),
		Handler:      m,
	}, nil
}

func buildListenerOrFail(name string, port int) net.Listener {
	addr := fmt.Sprintf("%s:%d", *flagBindAddr, port)
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatalf("failed listening for '%v' on %v: %v", name, port, err)
	}
	return conntrack.NewListener(listener,
		conntrack.TrackWithName(name),
		conntrack.TrackWithTcpKeepAlive(20*time.Second),
		conntrack.TrackWithTracing(),
	)
}

func healthEndpoint(resp http.ResponseWriter, req *http.Request) {
	resp.Header().Set("content-type", "text/plain")
	resp.WriteHeader(http.StatusOK)
	fmt.Fprintf(resp, "kedge isok")
}

func kedgeCodeToLevel(httpStatusCode int) log.Level {
	if httpStatusCode < 400 || httpStatusCode == http.StatusNotFound {
		return log.DebugLevel
	} else if httpStatusCode < 500 {
		return log.WarnLevel
	}
	return log.ErrorLevel
}
<commit_msg>Fixed server imports as well.<commit_after>package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"net/http/pprof"
	"os"
	"strings"
	"time"

	"github.com/grpc-ecosystem/go-grpc-middleware"
	"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
	"github.com/grpc-ecosystem/go-grpc-middleware/tags"
	"github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/mwitkow/go-conntrack"
	"github.com/mwitkow/go-conntrack/connhelpers"
	"github.com/mwitkow/go-flagz"
	"github.com/mwitkow/go-httpwares/logging/logrus"
	"github.com/mwitkow/go-httpwares/metrics"
	"github.com/mwitkow/go-httpwares/metrics/prometheus"
	"github.com/mwitkow/go-httpwares/tags"
	"github.com/mwitkow/go-httpwares/tracing/debug"
	"github.com/mwitkow/grpc-proxy/proxy"
	pb_config "github.com/mwitkow/kedge/_protogen/kedge/config"
	grpc_director "github.com/mwitkow/kedge/grpc/director"
	http_director "github.com/mwitkow/kedge/http/director"
	"github.com/mwitkow/kedge/lib/discovery"
	"github.com/mwitkow/kedge/lib/http/ctxtags"
	"github.com/mwitkow/kedge/lib/logstash"
	"github.com/mwitkow/kedge/lib/sharedflags"
	"github.com/pressly/chi"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	log "github.com/sirupsen/logrus"
	"golang.org/x/net/trace"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

var (
	flagBindAddr    = sharedflags.Set.String("server_bind_address", "0.0.0.0", "address to bind the server to")
	flagGrpcTlsPort = sharedflags.Set.Int("server_grpc_tls_port", 8444, "TCP TLS port to listen on for secure gRPC calls. If 0, no gRPC-TLS will be open.")
	flagHttpTlsPort = sharedflags.Set.Int("server_http_tls_port", 8443, "TCP port to listen on for HTTPS. If gRPC call will hit it will bounce to gRPC handler. If 0, no TLS will be open.")
	flagHttpPort    = sharedflags.Set.Int("server_http_port", 8080, "TCP port to listen on for HTTP1.1/REST calls for debug endpoints like metrics, flagz page or optional pprof (insecure, but private only IP are allowed). If 0, no insecure HTTP will be open.")

	flagHttpMaxWriteTimeout = sharedflags.Set.Duration("server_http_max_write_timeout", 10*time.Second, "HTTP server config, max write duration.")
	flagHttpMaxReadTimeout  = sharedflags.Set.Duration("server_http_max_read_timeout", 10*time.Second, "HTTP server config, max read duration.")
	flagGrpcWithTracing     = sharedflags.Set.Bool("server_tracing_grpc_enabled", true, "Whether enable gRPC tracing (could be expensive).")

	flagLogstashAddress = sharedflags.Set.String("logstash_hostport", "", "Host:port of logstash for remote logging. If empty remote logging is disabled.")

	flagLogTestBackendpoolResolution = sharedflags.Set.Bool("log_backend_resolution_on_addition", false, "With this option "+
		"kedge will always try to resolve and log (only) new backend entry. Useful for debugging backend routings.")

	flagDynamicRoutingDiscoveryEnabled = sharedflags.Set.Bool("kedge_dynamic_routings_enabled", false,
		"If enabled, kedge will watch on service changes (services which has particular label) and generates "+
			"director & backendpool routings. It will update them directly into into flagz value, so you can see the current routings anytime in debug/flagz")
)

func main() {
	log.SetOutput(os.Stdout)
	if err := sharedflags.Set.Parse(os.Args); err != nil {
		log.WithError(err).Fatalf("failed parsing flags")
	}
	if err := flagz.ReadFileFlags(sharedflags.Set); err != nil {
		log.WithError(err).Fatalf("failed reading flagz from files")
	}

	if *flagLogstashAddress != "" {
		formatter, err := logstash.NewFormatter()
		if err != nil {
			log.WithError(err).Fatal("Failed to get hostname for logstash formatter")
		}

		hook, err := logstash.NewHook(*flagLogstashAddress, formatter)
		if err != nil {
			log.WithError(err).Fatal("Failed to create new logstash hook")
		}
		log.AddHook(hook)
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	if *flagDynamicRoutingDiscoveryEnabled {
		log.Info("Flag 'kedge_dynamic_routings_enabled' is true. Enabling dynamic routing with base configuration fetched from provided" +
			"directorConfig and backendpoolConfig.")
		routingDiscovery, err := discovery.NewFromFlags(
			log.StandardLogger(),
			flagConfigDirector.Get().(*pb_config.DirectorConfig),
			flagConfigBackendpool.Get().(*pb_config.BackendPoolConfig),
		)
		if err != nil {
			log.WithError(err).Fatal("Failed to create routingDiscovery")
		}

		go func() {
			err := routingDiscovery.DiscoverAndSetFlags(
				ctx,
				flagConfigDirector,
				flagConfigBackendpool,
			)
			if err != nil {
				log.WithError(err).Fatal("Dynamic Routing Discovery failed")
			}
		}()
	}

	grpc.EnableTracing = *flagGrpcWithTracing
	logEntry := log.NewEntry(log.StandardLogger())
	grpc_logrus.ReplaceGrpcLogger(logEntry)
	tlsConfig, err := buildTLSConfigFromFlags()
	if err != nil {
		log.Fatalf("failed building TLS config from flags: %v", err)
	}

	httpDirector := http_director.New(httpBackendPool, httpRouter, httpAddresser, logEntry)
	grpcDirector := grpc_director.New(grpcBackendPool, grpcRouter)

	// GRPC kedge.
	grpcDirectorServer := grpc.NewServer(
		grpc.CustomCodec(proxy.Codec()), // needed for director to function.
		grpc.UnknownServiceHandler(proxy.TransparentHandler(grpcDirector)),
		grpc_middleware.WithUnaryServerChain(
			grpc_ctxtags.UnaryServerInterceptor(),
			grpc_logrus.UnaryServerInterceptor(logEntry),
			grpc_prometheus.UnaryServerInterceptor,
		),
		grpc_middleware.WithStreamServerChain(
			grpc_ctxtags.StreamServerInterceptor(),
			grpc_logrus.StreamServerInterceptor(logEntry),
			grpc_prometheus.StreamServerInterceptor,
		),
		grpc.Creds(credentials.NewTLS(tlsConfig)),
	)

	// HTTPS proxy chain.
	httpDirectorChain := chi.Chain(
		http_ctxtags.Middleware("proxy"),
		http_metrics.Middleware(http_prometheus.ServerMetrics(http_prometheus.WithLatency())),
		http_debug.Middleware(),
		http_logrus.Middleware(logEntry, http_logrus.WithLevels(kedgeCodeToLevel)),
	)

	// HTTP debug chain.
	httpDebugChain := chi.Chain(
		http_ctxtags.Middleware("debug"),
		http_debug.Middleware(),
	)
	// httpNonAuthDebugChain chain is shares the same base but will not include auth. It is for metrics and _healthz.
	httpNonAuthDebugChain := httpDebugChain

	authorizer, err := authorizerFromFlags(logEntry)
	if err != nil {
		log.WithError(err).Fatal("failed to create authorizer.")
	}

	if authorizer != nil {
		httpDirectorChain = append(httpDirectorChain, http_director.AuthMiddleware(authorizer))
		logEntry.Info("configured OIDC authorization for HTTPS proxy.")
	}

	// Bouncer.
	httpsBouncerServer := httpsBouncerServer(grpcDirectorServer, httpDirectorChain.Handler(httpDirector), logEntry)

	if authorizer != nil && *flagEnableOIDCAuthForDebugEnpoints {
		httpDebugChain = append(httpDebugChain, http_director.AuthMiddleware(authorizer))
		logEntry.Info("configured OIDC authorization for HTTP debug server.")
	}

	// Debug.
	httpDebugServer, err := debugServer(logEntry, httpDebugChain, httpNonAuthDebugChain)
	if err != nil {
		log.WithError(err).Fatal("failed to create debug Server.")
	}

	errChan := make(chan error)
	var grpcTlsListener net.Listener
	var httpPlainListener net.Listener
	var httpTlsListener net.Listener
	if *flagGrpcTlsPort != 0 {
		grpcTlsListener = buildListenerOrFail("grpc_tls", *flagGrpcTlsPort)
	}
	if *flagHttpPort != 0 {
		httpPlainListener = buildListenerOrFail("http_plain", *flagHttpPort)
	}
	if *flagHttpTlsPort != 0 {
		httpTlsListener = buildListenerOrFail("http_tls", *flagHttpTlsPort)
		http2TlsConfig, err := connhelpers.TlsConfigWithHttp2Enabled(tlsConfig)
		if err != nil {
			log.Fatalf("failed setting up HTTP2 TLS config: %v", err)
		}
		httpTlsListener = tls.NewListener(httpTlsListener, http2TlsConfig)
	}

	if grpcTlsListener != nil {
		log.Infof("listening for gRPC TLS on: %v", grpcTlsListener.Addr().String())
		go func() {
			if err := grpcDirectorServer.Serve(grpcTlsListener); err != nil {
				errChan <- fmt.Errorf("grpc_tls server error: %v", err)
			}
		}()
	}
	if httpTlsListener != nil {
		log.Infof("listening for HTTP TLS on: %v", httpTlsListener.Addr().String())
		go func() {
			if err := httpsBouncerServer.Serve(httpTlsListener); err != nil {
				errChan <- fmt.Errorf("http_tls server error: %v", err)
			}
		}()
	}
	if httpPlainListener != nil {
		log.Infof("listening for HTTP Plain on: %v", httpPlainListener.Addr().String())
		go func() {
			if err := httpDebugServer.Serve(httpPlainListener); err != nil {
				errChan <- fmt.Errorf("http_plain server error: %v", err)
			}
		}()
	}

	err = <-errChan // this waits for some server breaking
	log.WithError(err).Fatalf("Fail")
}

// httpsBouncerHandler decides what kind of requests it is and redirects to GRPC if needed.
func httpsBouncerServer(grpcHandler *grpc.Server, httpHandler http.Handler, logEntry *log.Entry) *http.Server {
	httpBouncerHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		if req.URL.Path == "/_healthz" {
			healthEndpoint(w, req)
			return
		}
		if strings.HasPrefix(req.Header.Get("content-type"), "application/grpc") {
			grpcHandler.ServeHTTP(w, req)
			return
		}
		httpHandler.ServeHTTP(w, req)
	}).ServeHTTP

	return &http.Server{
		WriteTimeout: *flagHttpMaxWriteTimeout,
		ReadTimeout:  *flagHttpMaxReadTimeout,
		ErrorLog:     http_logrus.AsHttpLogger(logEntry.WithField(ctxtags.TagForScheme, "tls")),
		Handler:      http.HandlerFunc(httpBouncerHandler),
	}
}

func debugServer(logEntry *log.Entry, middlewares chi.Middlewares, noAuthMiddlewares chi.Middlewares) (*http.Server, error) {
	m := chi.NewMux()
	m.Handle("/_healthz", noAuthMiddlewares.HandlerFunc(healthEndpoint))
	m.Handle("/debug/metrics", noAuthMiddlewares.Handler(promhttp.Handler()))

	m.Handle("/_version",
		// The only one worth to log.
		chi.Chain(http_logrus.Middleware(logEntry.WithField(ctxtags.TagForScheme, "plain"), http_logrus.WithLevels(kedgeCodeToLevel))).
			Handler(middlewares.HandlerFunc(versionEndpoint)))
	m.Handle("/debug/flagz", middlewares.HandlerFunc(flagz.NewStatusEndpoint(sharedflags.Set).ListFlags))

	m.Handle("/debug/pprof/", middlewares.HandlerFunc(pprof.Index))
	m.Handle("/debug/pprof/cmdline", middlewares.HandlerFunc(pprof.Cmdline))
	m.Handle("/debug/pprof/profile", middlewares.HandlerFunc(pprof.Profile))
	m.Handle("/debug/pprof/symbol", middlewares.HandlerFunc(pprof.Symbol))
	m.Handle("/debug/pprof/trace", middlewares.HandlerFunc(pprof.Trace))
	m.Handle("/debug/traces", middlewares.HandlerFunc(trace.Traces))
	m.Handle("/debug/events", middlewares.HandlerFunc(trace.Events))

	return &http.Server{
		WriteTimeout: *flagHttpMaxWriteTimeout,
		ReadTimeout:  *flagHttpMaxReadTimeout,
		ErrorLog:     http_logrus.AsHttpLogger(logEntry.WithField(ctxtags.TagForScheme, "tls")),
		Handler:      m,
	}, nil
}

func buildListenerOrFail(name string, port int) net.Listener {
	addr := fmt.Sprintf("%s:%d", *flagBindAddr, port)
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatalf("failed listening for '%v' on %v: %v", name, port, err)
	}
	return conntrack.NewListener(listener,
		conntrack.TrackWithName(name),
		conntrack.TrackWithTcpKeepAlive(20*time.Second),
		conntrack.TrackWithTracing(),
	)
}

func healthEndpoint(resp http.ResponseWriter, req *http.Request) {
	resp.Header().Set("content-type", "text/plain")
	resp.WriteHeader(http.StatusOK)
	fmt.Fprintf(resp, "kedge isok")
}

func kedgeCodeToLevel(httpStatusCode int) log.Level {
	if httpStatusCode < 400 || httpStatusCode == http.StatusNotFound {
		return log.DebugLevel
	} else if httpStatusCode < 500 {
		return log.WarnLevel
	}
	return log.ErrorLevel
}
<|endoftext|>


<commit_before>// Prg1: NPRG030
// ADS2: NTIN061
// Haskell: NAIL097
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/iamwave/samorozvrh/sisparse"
	"io/ioutil"
	"log"
	"net/http"
	"path"
	"strconv"
	"strings"
)

const FRONTEND_DIR = "frontend/dist"

var rootDir string

func sisQueryHandler(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Path[len("/sisquery/"):]
	if strings.Contains(query, "/") {
		fmt.Fprintf(w, `{"error":"Query should not contain slashes"}`)
		return
	}
	var res string
	var err error

	log.Printf("Sisquery: %s", ellipsis(query, 10))
	if isCached(query) {
		log.Println("  (using cache)")
		res, err = getCache(query)
	} else {
		log.Println("  (querying)")
		var events [][]sisparse.Event
		events, err = sisparse.GetCourseEvents(query)
		if err == nil {
			var s []byte
			s, err = json.Marshal(events)
			if err == nil {
				res = fmt.Sprintf(`{"data":%s}`, string(s))
				err = setCache(query, res)
			}
		}
	}

	if err != nil {
		log.Printf("Sisquery error: %s", err)
		fmt.Fprintf(w, `{"error":"%s"}`, err)
	} else {
		log.Printf(`Sisquery answer: %s`, ellipsis(res, 30))
		fmt.Fprint(w, res)
	}
}

func solverQueryHandler(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(r.Body)
	r.Body.Close()
	if err != nil {
		log.Printf("Solverquery error: %s", err)
		fmt.Fprintf(w, `{"error":"%s"}`, err)
		return
	}
	if len(body) == 0 {
		log.Printf("Solverquery error: %s", err)
		fmt.Fprint(w, `{"error":"Request body must be non-empty"}`)
		return
	}

	log.Printf("Solverquery: %s\n", ellipsis(string(body), 30))
	res, err := Solve(body)
	if err != nil {
		log.Printf("Solverquery error: %s", err)
		fmt.Fprintf(w, `{"error":"%s"}`, err)
	} else {
		log.Printf("Solverquery answer: %s", ellipsis(string(res), 30))
		fmt.Fprint(w, string(res))
	}
}

func main() {
	rdir := flag.String("rootdir", ".", "path to Samorozvrh root directory")
	port := flag.Int("port", 8080, "port on which to start the server")
	flag.Parse()
	rootDir = *rdir

	http.HandleFunc("/sisquery/", sisQueryHandler)
	http.HandleFunc("/solverquery/", solverQueryHandler)

	fs := http.FileServer(http.Dir(path.Join(rootDir, FRONTEND_DIR)))
	http.Handle("/", fs)

	log.Printf("Listening on: %d", *port)
	err := http.ListenAndServe(":"+strconv.Itoa(*port), nil)
	if err != nil {
		log.Fatalf("Could not start server: %s\n", err)
	}
}

func ellipsis(s string, n int) string {
	if len(s) < n {
		return s
	} else {
		return s[:n] + "..."
	}
}
<commit_msg>Stricter checks on queries<commit_after>// Prg1: NPRG030
// ADS2: NTIN061
// Haskell: NAIL097
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/iamwave/samorozvrh/sisparse"
	"io/ioutil"
	"log"
	"net/http"
	"path"
	"regexp"
	"strconv"
	"strings"
)

const FRONTEND_DIR = "frontend/dist"

var rootDir string

func sisQueryHandler(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Path[len("/sisquery/"):]
	query = strings.Trim(query, " ")

	if len(query) > 32 {
		fmt.Fprintf(w, `{"error":"The query is too long"}`)
		return
	}

	re := regexp.MustCompile("^[0-9a-zA-z]*$")

	if !re.MatchString(query) {
		fmt.Fprintf(w, `{"error":"Query must contain only alphanumeric characters"}`)
		return
	}
	var res string
	var err error

	query = strings.ToUpper(query)
	log.Printf("Sisquery: %s", ellipsis(query, 10))

	if isCached(query) {
		log.Println("  (using cache)")
		res, err = getCache(query)
	} else {
		log.Println("  (querying)")
		var events [][]sisparse.Event
		events, err = sisparse.GetCourseEvents(query)
		if err == nil {
			var s []byte
			s, err = json.Marshal(events)
			if err == nil {
				res = fmt.Sprintf(`{"data":%s}`, string(s))
				err = setCache(query, res)
			}
		}
	}

	if err != nil {
		log.Printf("Sisquery error: %s", err)
		fmt.Fprintf(w, `{"error":"%s"}`, err)
	} else {
		log.Printf(`Sisquery answer: %s`, ellipsis(res, 30))
		fmt.Fprint(w, res)
	}
}

func solverQueryHandler(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(r.Body)
	r.Body.Close()
	if err != nil {
		log.Printf("Solverquery error: %s", err)
		fmt.Fprintf(w, `{"error":"%s"}`, err)
		return
	}
	if len(body) == 0 {
		log.Printf("Solverquery error: %s", err)
		fmt.Fprint(w, `{"error":"Request body must be non-empty"}`)
		return
	}

	log.Printf("Solverquery: %s\n", ellipsis(string(body), 30))
	res, err := Solve(body)
	if err != nil {
		log.Printf("Solverquery error: %s", err)
		fmt.Fprintf(w, `{"error":"%s"}`, err)
	} else {
		log.Printf("Solverquery answer: %s", ellipsis(string(res), 30))
		fmt.Fprint(w, string(res))
	}
}

func main() {
	rdir := flag.String("rootdir", ".", "path to Samorozvrh root directory")
	port := flag.Int("port", 8080, "port on which to start the server")
	flag.Parse()
	rootDir = *rdir

	http.HandleFunc("/sisquery/", sisQueryHandler)
	http.HandleFunc("/solverquery/", solverQueryHandler)

	fs := http.FileServer(http.Dir(path.Join(rootDir, FRONTEND_DIR)))
	http.Handle("/", fs)

	log.Printf("Listening on: %d", *port)
	err := http.ListenAndServe(":"+strconv.Itoa(*port), nil)
	if err != nil {
		log.Fatalf("Could not start server: %s\n", err)
	}
}

func ellipsis(s string, n int) string {
	if len(s) < n {
		return s
	} else {
		return s[:n] + "..."
	}
}
<|endoftext|>


<commit_before>package blockstore

import (
	"errors"

	context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
)

// Next to each option is it aproximate memory usage per unit
type CacheOpts struct {
	HasBloomFilterSize   int // 1 bit
	HasBloomFilterHashes int // No size, 7 is usually best, consult bloom papers
	HasARCCacheSize      int // 32 bytes
}

func DefaultCacheOpts() CacheOpts {
	return CacheOpts{
		HasBloomFilterSize:   512 * 8 * 1024,
		HasBloomFilterHashes: 7,
		HasARCCacheSize:      64 * 1024,
	}
}

func CachedBlockstore(bs GCBlockstore,
	ctx context.Context, opts CacheOpts) (cbs GCBlockstore, err error) {
	if ctx == nil {
		ctx = context.TODO() // For tests
	}

	if opts.HasBloomFilterSize < 0 || opts.HasBloomFilterHashes < 0 ||
		opts.HasARCCacheSize < 0 {
		return nil, errors.New("all options for cache need to be greater than zero")
	}

	if opts.HasBloomFilterSize != 0 && opts.HasBloomFilterHashes == 0 {
		return nil, errors.New("bloom filter hash count can't be 0 when there is size set")
	}
	cbs, err = bloomCached(bs, ctx, opts.HasBloomFilterSize, opts.HasBloomFilterHashes,
		opts.HasARCCacheSize)

	return cbs, err
}
<commit_msg>blocks/blockstore: improve logic a bit<commit_after>package blockstore

import (
	"errors"

	context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
)

// Next to each option is it aproximate memory usage per unit
type CacheOpts struct {
	HasBloomFilterSize   int // 1 bit
	HasBloomFilterHashes int // No size, 7 is usually best, consult bloom papers
	HasARCCacheSize      int // 32 bytes
}

func DefaultCacheOpts() CacheOpts {
	return CacheOpts{
		HasBloomFilterSize:   512 * 8 * 1024,
		HasBloomFilterHashes: 7,
		HasARCCacheSize:      64 * 1024,
	}
}

func CachedBlockstore(bs GCBlockstore,
	ctx context.Context, opts CacheOpts) (cbs GCBlockstore, err error) {
	if ctx == nil {
		ctx = context.TODO() // For tests
	}
	cbs = bs

	if opts.HasBloomFilterSize < 0 || opts.HasBloomFilterHashes < 0 ||
		opts.HasARCCacheSize < 0 {
		return nil, errors.New("all options for cache need to be greater than zero")
	}

	if opts.HasBloomFilterSize != 0 && opts.HasBloomFilterHashes == 0 {
		return nil, errors.New("bloom filter hash count can't be 0 when there is size set")
	}
	if opts.HasBloomFilterSize != 0 {
		cbs, err = bloomCached(cbs, ctx, opts.HasBloomFilterSize, opts.HasBloomFilterHashes,
			opts.HasARCCacheSize)
	}

	return cbs, err
}
<|endoftext|>


<commit_before>// Copyright 2016 HeadwindFly. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"github.com/headwindfly/clevergo"
	"log"
	"os"
	"path"
)

var (
	helloCleverGo = []byte("Hello CleverGo!\n")
	resourcesPath = path.Join(os.Getenv("GOPATH"), "src", "github.com", "headwindfly", "clevergo", "examples")
)

type User struct {
	Name string `json:"name" xml:"name"`
	Team string `json:"team" xml:"team"`
}

func hello(ctx *clevergo.Context) {
	ctx.Write(helloCleverGo)
}

func html(ctx *clevergo.Context) {
	ctx.HTML("Hello CleverGo!\n")
}

func json(ctx *clevergo.Context) {
	ctx.JSON(User{
		Name: "HeadwindFly",
		Team: "CleverGo",
	})
}

func jsonp(ctx *clevergo.Context) {
	callback := ctx.FormValue("callback")
	ctx.JSONP(User{
		Name: "HeadwindFly",
		Team: "CleverGo",
	}, callback)
}

func xml(ctx *clevergo.Context) {
	ctx.XML(User{
		Name: "HeadwindFly",
		Team: "CleverGo",
	}, "")
}

func params(ctx *clevergo.Context) {
	name := ctx.RouterParams.ByName("name")
	ctx.Textf("Hello %s.", name)
}

func multiParams(ctx *clevergo.Context) {
	param1 := ctx.RouterParams.ByName("param1")
	param2 := ctx.RouterParams.ByName("param2")
	ctx.Textf("Your params is %s and %s", param1, param2)
}

func main() {
	// Create a router instance.
	router := clevergo.NewRouter()

	// Register route handler.
	router.GET("/", clevergo.HandlerFunc(hello))
	router.GET("/html", clevergo.HandlerFunc(html))
	router.GET("/json", clevergo.HandlerFunc(json))
	router.GET("/jsonp", clevergo.HandlerFunc(jsonp))
	router.GET("/xml", clevergo.HandlerFunc(xml))
	// Navigate to http://127.0.0.1:8080/params/yourname.
	router.GET("/params/:name", clevergo.HandlerFunc(params))
	// Navigate to http://127.0.0.1:8080/multi-params/param1/param2.
	router.GET("/multi-params/:param1/:param2", clevergo.HandlerFunc(multiParams))

	// Static resource files.
	// Navigate to http://127.0.0.1:8080/examples/base.go
	router.ServeFiles("/examples/*filepath", resourcesPath)

	// Start server.
	log.Fatal(clevergo.ListenAndServe(":8080", router.Handler))
}
<commit_msg>Added websocket example<commit_after>// Copyright 2016 HeadwindFly. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"github.com/headwindfly/clevergo"
	"github.com/headwindfly/websocket"
	"html/template"
	"log"
)

// Create a fasthttp upgrader.
var upgrader = websocket.FastHTTPUpgrader{}

// Create a websocket connection handler.
var connHandler = func(c *websocket.Conn) {
	for {
		mt, message, err := c.ReadMessage()
		if err != nil {
			log.Println("read:", err)
			break
		}
		log.Printf("recv: %s", message)
		err = c.WriteMessage(mt, message)
		if err != nil {
			log.Println("write:", err)
			break
		}
	}
}

func echo(ctx *clevergo.Context) {
	upgrader.Upgrade(ctx.RequestCtx, connHandler)
}

func home(ctx *clevergo.Context) {
	ctx.Response.Header.Set("Content-Type", "text/html; charset=utf-8")
	homeTemplate.Execute(ctx, "ws://"+string(ctx.Host())+"/echo")
}

func main() {
	// Create a router instance.
	router := clevergo.NewRouter()

	// Register route handler.
	router.GET("/", clevergo.HandlerFunc(home))
	router.GET("/echo", clevergo.HandlerFunc(echo))

	// Start server.
	log.Fatal(clevergo.ListenAndServe(":8080", router.Handler))
}

var homeTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>

<meta charset="utf-8">

Click "Open" to create a connection to the server,
"Send" to send a message to the server and "Close" to close the connection.
You can change the message and send multiple times.

<form>
<button id="open">Open</button>
<button id="close">Close</button>

<input id="input" type="text" value="Hello world!">
<button id="send">Send</button>
</form>

`))
<|endoftext|>


<commit_before>package docker

import (
	"bytes"
	"fmt"
	"io"
	"time"

	"github.com/nyarly/inlinefiles/templatestore"
	"github.com/opentable/sous/lib"
	"github.com/opentable/sous/util/logging"
	"github.com/opentable/sous/util/logging/messages"
	"github.com/opentable/sous/util/shell"
)

//go:generate inlinefiles --vfs=templateVFS tmpl/ templates_vfs.go

type (
	// Builder represents a single build of a project.
	Builder struct {
		ImageMapper               sous.Inserter
		DockerRegistryHost        string
		SourceShell, ScratchShell shell.Shell
		Pack                      sous.Buildpack
		log                       logging.LogSink
	}
	// BuildTarget represents a single target within a Build.
	BuildTarget interface {
		BuildImage()
		BuildContainer()
	}
)

// NewBuilder creates a new build using source code in the working
// directory of sourceShell, and using the working dir of scratchShell as
// temporary storage.
func NewBuilder(nc sous.Inserter, drh string, sourceShell, scratchShell shell.Shell, ls logging.LogSink) (*Builder, error) {
	b := &Builder{
		ImageMapper:        nc,
		DockerRegistryHost: drh,
		SourceShell:        sourceShell,
		ScratchShell:       scratchShell,
		log:                ls,
	}

	files, err := scratchShell.List()
	if err != nil {
		return nil, err
	}

	if len(files) != 0 {
		return nil, fmt.Errorf("scratch dir %s was not empty", scratchShell.Dir())
	}

	return b, nil
}

// ApplyMetadata implements sous.Labeller on Builder.
// It applies container metadata etc. to a container.
func (b *Builder) ApplyMetadata(br *sous.BuildResult) error {
	for _, prod := range br.Products {
		err := b.applyMetadata(prod)
		if err != nil {
			return err
		}
	}
	return nil
}

// Register registers the build artifact to the the registry
func (b *Builder) Register(br *sous.BuildResult) error {
	for _, prod := range br.Products {
		err := b.pushToRegistry(prod)
		if err != nil {
			return err
		}

		err = b.recordName(prod)
		if err != nil {
			return err
		}
	}
	return nil
}

func (b *Builder) debug(msg string) {
	messages.ReportLogFieldsMessage(msg, logging.DebugLevel, b.log)
}

func (b *Builder) info(msg string) {
	messages.ReportLogFieldsMessage(msg, logging.InformationLevel, b.log)
}

func (b *Builder) applyMetadata(bp *sous.BuildProduct) error {
	bp.VersionName = b.VersionTag(bp.Source, bp.Kind)
	bp.RevisionName = b.RevisionTag(bp.Source, bp.Kind, time.Now())

	c := b.SourceShell.Cmd("docker", "build", "-t", bp.VersionName, "-t", bp.RevisionName, "-")
	bf := b.metadataDockerfile(bp)
	c.SetStdin(bf)

	return c.Succeed()
}

func (b *Builder) metadataDockerfile(bp *sous.BuildProduct) io.Reader {
	bf := bytes.Buffer{}
	sv := bp.Source
	md, err := templatestore.LoadText(templateVFS, "metadata", "metadataDockerfile.tmpl")
	if err != nil {
		panic(err)
	}

	md.Execute(&bf, struct {
		ImageID    string
		Labels     map[string]string
		Advisories []string
	}{
		bp.ID,
		Labels(sv),
		bp.Advisories,
	})
	return &bf
}

// pushToRegistry sends the built image to the registry
func (b *Builder) pushToRegistry(bp *sous.BuildProduct) error {
	verr := b.SourceShell.Run("docker", "push", bp.VersionName)
	rerr := b.SourceShell.Run("docker", "push", bp.RevisionName)

	if verr == nil {
		return rerr
	}
	return verr
}

// recordName inserts metadata about the newly built image into our local name cache
func (b *Builder) recordName(bp *sous.BuildProduct) error {
	sv := bp.Source
	in := bp.VersionName
	b.SourceShell.ConsoleEcho(fmt.Sprintf("[recording \"%s\" as the docker name for \"%s\"]", in, sv.String()))
	var qs []sous.Quality
	for _, adv := range bp.Advisories {
		qs = append(qs, sous.Quality{Name: adv, Kind: "advisory"})
	}
	return b.ImageMapper.Insert(sv, in, "", qs)
}

// VersionTag computes an image tag from a SourceVersion's version
func (b *Builder) VersionTag(v sous.SourceID, kind string) string {
	return versionTag(b.DockerRegistryHost, v, kind, b.log)
}

// RevisionTag computes an image tag from a SourceVersion's revision id
func (b *Builder) RevisionTag(v sous.SourceID, kind string, time time.Time) string {
	return revisionTag(b.DockerRegistryHost, v, kind, time, b.log)
}
<commit_msg>docker: NameCache records revision<commit_after>package docker

import (
	"bytes"
	"fmt"
	"io"
	"time"

	"github.com/nyarly/inlinefiles/templatestore"
	"github.com/opentable/sous/lib"
	"github.com/opentable/sous/util/logging"
	"github.com/opentable/sous/util/logging/messages"
	"github.com/opentable/sous/util/shell"
)

//go:generate inlinefiles --vfs=templateVFS tmpl/ templates_vfs.go

type (
	// Builder represents a single build of a project.
	Builder struct {
		ImageMapper               sous.Inserter
		DockerRegistryHost        string
		SourceShell, ScratchShell shell.Shell
		Pack                      sous.Buildpack
		log                       logging.LogSink
	}
	// BuildTarget represents a single target within a Build.
	BuildTarget interface {
		BuildImage()
		BuildContainer()
	}
)

// NewBuilder creates a new build using source code in the working
// directory of sourceShell, and using the working dir of scratchShell as
// temporary storage.
func NewBuilder(nc sous.Inserter, drh string, sourceShell, scratchShell shell.Shell, ls logging.LogSink) (*Builder, error) {
	b := &Builder{
		ImageMapper:        nc,
		DockerRegistryHost: drh,
		SourceShell:        sourceShell,
		ScratchShell:       scratchShell,
		log:                ls,
	}

	files, err := scratchShell.List()
	if err != nil {
		return nil, err
	}

	if len(files) != 0 {
		return nil, fmt.Errorf("scratch dir %s was not empty", scratchShell.Dir())
	}

	return b, nil
}

// ApplyMetadata implements sous.Labeller on Builder.
// It applies container metadata etc. to a container.
func (b *Builder) ApplyMetadata(br *sous.BuildResult) error {
	for _, prod := range br.Products {
		err := b.applyMetadata(prod)
		if err != nil {
			return err
		}
	}
	return nil
}

// Register registers the build artifact to the the registry
func (b *Builder) Register(br *sous.BuildResult) error {
	for _, prod := range br.Products {
		err := b.pushToRegistry(prod)
		if err != nil {
			return err
		}

		err = b.recordName(prod)
		if err != nil {
			return err
		}
	}
	return nil
}

func (b *Builder) debug(msg string) {
	messages.ReportLogFieldsMessage(msg, logging.DebugLevel, b.log)
}

func (b *Builder) info(msg string) {
	messages.ReportLogFieldsMessage(msg, logging.InformationLevel, b.log)
}

func (b *Builder) applyMetadata(bp *sous.BuildProduct) error {
	bp.VersionName = b.VersionTag(bp.Source, bp.Kind)
	bp.RevisionName = b.RevisionTag(bp.Source, bp.Kind, time.Now())

	c := b.SourceShell.Cmd("docker", "build", "-t", bp.VersionName, "-t", bp.RevisionName, "-")
	bf := b.metadataDockerfile(bp)
	c.SetStdin(bf)

	return c.Succeed()
}

func (b *Builder) metadataDockerfile(bp *sous.BuildProduct) io.Reader {
	bf := bytes.Buffer{}
	sv := bp.Source
	md, err := templatestore.LoadText(templateVFS, "metadata", "metadataDockerfile.tmpl")
	if err != nil {
		panic(err)
	}

	md.Execute(&bf, struct {
		ImageID    string
		Labels     map[string]string
		Advisories []string
	}{
		bp.ID,
		Labels(sv),
		bp.Advisories,
	})
	return &bf
}

// pushToRegistry sends the built image to the registry
func (b *Builder) pushToRegistry(bp *sous.BuildProduct) error {
	verr := b.SourceShell.Run("docker", "push", bp.VersionName)
	rerr := b.SourceShell.Run("docker", "push", bp.RevisionName)

	if verr == nil {
		return rerr
	}
	return verr
}

// recordName inserts metadata about the newly built image into our local name cache
func (b *Builder) recordName(bp *sous.BuildProduct) error {
	sv := bp.Source
	sv.Version.Meta = bp.RevisionName
	in := bp.VersionName
	b.SourceShell.ConsoleEcho(fmt.Sprintf("[recording \"%s\" as the docker name for \"%s\"]", in, sv.String()))
	var qs []sous.Quality
	for _, adv := range bp.Advisories {
		qs = append(qs, sous.Quality{Name: adv, Kind: "advisory"})
	}
	return b.ImageMapper.Insert(sv, in, "", qs)
}

// VersionTag computes an image tag from a SourceVersion's version
func (b *Builder) VersionTag(v sous.SourceID, kind string) string {
	return versionTag(b.DockerRegistryHost, v, kind, b.log)
}

// RevisionTag computes an image tag from a SourceVersion's revision id
func (b *Builder) RevisionTag(v sous.SourceID, kind string, time time.Time) string {
	return revisionTag(b.DockerRegistryHost, v, kind, time, b.log)
}
<|endoftext|>


<commit_before>package client

import (
	"context"
	"strings"

	ccli "github.com/micro/cli/v2"
	"github.com/micro/go-micro/v2/auth"
	"github.com/micro/go-micro/v2/client"
	"github.com/micro/go-micro/v2/client/grpc"
	"github.com/micro/go-micro/v2/metadata"
	"github.com/micro/go-micro/v2/router"
	"github.com/micro/micro/v2/client/cli/util"
	cliutil "github.com/micro/micro/v2/client/cli/util"
	"github.com/micro/micro/v2/internal/config"
)

// New returns a wrapped grpc client which will inject the
// token found in config into each request
func New(ctx *ccli.Context) client.Client {
	env := cliutil.GetEnv(ctx)
	token, _ := config.Get("micro", "auth", env.Name, "token")
	return &wrapper{grpc.NewClient(), token, env.Name, ctx}
}

type wrapper struct {
	client.Client
	token string
	env   string
	ctx   *ccli.Context
}

func (a *wrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
	if len(a.token) > 0 {
		ctx = metadata.Set(ctx, "Authorization", auth.BearerScheme+a.token)
	}

	// network will be used by the router to filter available routes
	var network string
	if util.IsLocal(a.ctx) || util.IsServer(a.ctx) {
		network = router.DefaultNetwork
	} else {
		network = strings.ReplaceAll(a.env, "/", "-")
	}

	ctx = metadata.Set(ctx, "Micro-Network", network)
	return a.Client.Call(ctx, req, rsp, opts...)
}
<commit_msg>internal/client: temp disable setting micro network on cli calls<commit_after>package client

import (
	"context"

	ccli "github.com/micro/cli/v2"
	"github.com/micro/go-micro/v2/auth"
	"github.com/micro/go-micro/v2/client"
	"github.com/micro/go-micro/v2/client/grpc"
	"github.com/micro/go-micro/v2/metadata"
	cliutil "github.com/micro/micro/v2/client/cli/util"
	"github.com/micro/micro/v2/internal/config"
)

// New returns a wrapped grpc client which will inject the
// token found in config into each request
func New(ctx *ccli.Context) client.Client {
	env := cliutil.GetEnv(ctx)
	token, _ := config.Get("micro", "auth", env.Name, "token")
	return &wrapper{grpc.NewClient(), token, env.Name, ctx}
}

type wrapper struct {
	client.Client
	token string
	env   string
	ctx   *ccli.Context
}

func (a *wrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
	if len(a.token) > 0 {
		ctx = metadata.Set(ctx, "Authorization", auth.BearerScheme+a.token)
	}

	// network will be used by the router to filter available routes.
	// var network string
	// if util.IsLocal(a.ctx) || util.IsServer(a.ctx) {
	// 	network = router.DefaultNetwork
	// } else {
	// 	network = strings.ReplaceAll(a.env, "/", "-")
	// }

	// ctx = metadata.Set(ctx, "Micro-Network", network)
	return a.Client.Call(ctx, req, rsp, opts...)
}
<|endoftext|>


<commit_before>package docker

import (
	"os"
	"runtime"

	api "github.com/fsouza/go-dockerclient"
)

// Config represents the docker configuration.
type Config struct {
	Host string
}

// Docker represents a docker client.
type Docker struct {
	client *api.Client
	host   string
}

// NewDocker creates a new docker client.
func NewDocker(args ...*Config) (*Docker, error) {
	var client *api.Client
	var err error
	var host string

	// Find docker host for local machine.
	if len(args) > 0 {
		host = args[0].Host
	} else if os.Getenv("DOCKER_HOST") != "" {
		host = os.Getenv("DOCKER_HOST")
	} else if runtime.GOOS == "windows" {
		host = "http://localhost:2375"
	} else {
		host = "unix:///var/run/docker.sock"
	}

	client, err = api.NewClient(host)

	if err != nil {
		return nil, err
	}

	return &Docker{
		client: client,
		host:   host,
	}, nil
}

// Host will return the docker host that is used.
func (d *Docker) Host() string {
	return d.host
}

// RemoveContainer removes a container by name.
func (d *Docker) RemoveContainer(name string) error {
	container, err := d.findContainer(name)

	if err != nil {
		return err
	}

	return d.removeContainer(container)
}
<commit_msg>Fix issue with docker host<commit_after>package docker

import (
	"os"
	"runtime"

	api "github.com/fsouza/go-dockerclient"
)

// Config represents the docker configuration.
type Config struct {
	Host string
}

// Docker represents a docker client.
type Docker struct {
	client *api.Client
	host   string
}

// NewDocker creates a new docker client.
func NewDocker(args ...*Config) (*Docker, error) {
	var client *api.Client
	var err error
	var host string

	// Find docker host for local machine.
	if len(args) > 0 && len(args[0].Host) > 0 {
		host = args[0].Host
	} else if os.Getenv("DOCKER_HOST") != "" {
		host = os.Getenv("DOCKER_HOST")
	} else if runtime.GOOS == "windows" {
		host = "http://localhost:2375"
	} else {
		host = "unix:///var/run/docker.sock"
	}

	client, err = api.NewClient(host)

	if err != nil {
		return nil, err
	}

	return &Docker{
		client: client,
		host:   host,
	}, nil
}

// Host will return the docker host that is used.
func (d *Docker) Host() string {
	return d.host
}

// RemoveContainer removes a container by name.
func (d *Docker) RemoveContainer(name string) error {
	container, err := d.findContainer(name)

	if err != nil {
		return err
	}

	return d.removeContainer(container)
}
<|endoftext|>


<commit_before>// Copyright 2019 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package jsutil

import (
	"syscall/js"
)

// isTypedArrayWritable represents whether TypedArray is writable or not.
// TypedArray's properties are not writable in the Web standard, but are writable with go2cpp.
// This enables to avoid unnecessary allocations of js.Value.
var isTypedArrayWritable = js.Global().Get("go2cpp").Truthy()

// temporaryBuffer is a temporary buffer used at gl.readPixels or gl.texSubImage2D.
// The read data is converted to Go's byte slice as soon as possible.
// To avoid often allocating ArrayBuffer, reuse the buffer whenever possible.
var temporaryBuffer = js.Global().Get("ArrayBuffer").New(16)

func TemporaryUint8Array(byteLength int) js.Value {
	if bufl := temporaryBuffer.Get("byteLength").Int(); bufl < byteLength {
		for bufl < byteLength {
			bufl *= 2
		}
		temporaryBuffer = js.Global().Get("ArrayBuffer").New(bufl)
	}
	return uint8Array(temporaryBuffer, 0, byteLength)
}

var uint8ArrayObj js.Value

func uint8Array(buffer js.Value, byteOffset, byteLength int) js.Value {
	if isTypedArrayWritable {
		if uint8ArrayObj.IsUndefined() {
			uint8ArrayObj = js.Global().Get("Uint8Array").New()
		}
		uint8ArrayObj.Set("buffer", buffer)
		uint8ArrayObj.Set("byteOffset", byteOffset)
		uint8ArrayObj.Set("byteLength", byteLength)
		return uint8ArrayObj
	}
	return js.Global().Get("Uint8Array").New(buffer, byteOffset, byteLength)
}
<commit_msg>jsutil: Use Equal for Go 1.13<commit_after>// Copyright 2019 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package jsutil

import (
	"syscall/js"
)

// isTypedArrayWritable represents whether TypedArray is writable or not.
// TypedArray's properties are not writable in the Web standard, but are writable with go2cpp.
// This enables to avoid unnecessary allocations of js.Value.
var isTypedArrayWritable = js.Global().Get("go2cpp").Truthy()

// temporaryBuffer is a temporary buffer used at gl.readPixels or gl.texSubImage2D.
// The read data is converted to Go's byte slice as soon as possible.
// To avoid often allocating ArrayBuffer, reuse the buffer whenever possible.
var temporaryBuffer = js.Global().Get("ArrayBuffer").New(16)

func TemporaryUint8Array(byteLength int) js.Value {
	if bufl := temporaryBuffer.Get("byteLength").Int(); bufl < byteLength {
		for bufl < byteLength {
			bufl *= 2
		}
		temporaryBuffer = js.Global().Get("ArrayBuffer").New(bufl)
	}
	return uint8Array(temporaryBuffer, 0, byteLength)
}

var uint8ArrayObj js.Value

func uint8Array(buffer js.Value, byteOffset, byteLength int) js.Value {
	if isTypedArrayWritable {
		if Equal(uint8ArrayObj, js.Undefined()) {
			uint8ArrayObj = js.Global().Get("Uint8Array").New()
		}
		uint8ArrayObj.Set("buffer", buffer)
		uint8ArrayObj.Set("byteOffset", byteOffset)
		uint8ArrayObj.Set("byteLength", byteLength)
		return uint8ArrayObj
	}
	return js.Global().Get("Uint8Array").New(buffer, byteOffset, byteLength)
}
<|endoftext|>


<commit_before>// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cmd

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"path/filepath"
	"strings"
	"time"

	"golang.org/x/tools/internal/jsonrpc2"
	"golang.org/x/tools/internal/lsp"
	"golang.org/x/tools/internal/lsp/debug"
	"golang.org/x/tools/internal/lsp/telemetry"
	"golang.org/x/tools/internal/lsp/telemetry/tag"
	"golang.org/x/tools/internal/lsp/telemetry/trace"
	"golang.org/x/tools/internal/tool"
)

// Serve is a struct that exposes the configurable parts of the LSP server as
// flags, in the right form for tool.Main to consume.
type Serve struct {
	Logfile string `flag:"logfile" help:"filename to log to. if value is \"auto\", then logging to a default output file is enabled"`
	Mode    string `flag:"mode" help:"no effect"`
	Port    int    `flag:"port" help:"port on which to run gopls for debugging purposes"`
	Address string `flag:"listen" help:"address on which to listen for remote connections"`
	Trace   bool   `flag:"rpc.trace" help:"Print the full rpc trace in lsp inspector format"`
	Debug   string `flag:"debug" help:"Serve debug information on the supplied address"`

	app *Application
}

func (s *Serve) Name() string  { return "serve" }
func (s *Serve) Usage() string { return "" }
func (s *Serve) ShortHelp() string {
	return "run a server for Go code using the Language Server Protocol"
}
func (s *Serve) DetailedHelp(f *flag.FlagSet) {
	fmt.Fprint(f.Output(), `
The server communicates using JSONRPC2 on stdin and stdout, and is intended to be run directly as
a child of an editor process.

gopls server flags are:
`)
	f.PrintDefaults()
}

// Run configures a server based on the flags, and then runs it.
// It blocks until the server shuts down.
func (s *Serve) Run(ctx context.Context, args ...string) error {
	if len(args) > 0 {
		return tool.CommandLineErrorf("server does not take arguments, got %v", args)
	}
	out := os.Stderr
	if s.Logfile != "" {
		filename := s.Logfile
		if filename == "auto" {
			filename = filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.log", os.Getpid()))
		}
		f, err := os.Create(filename)
		if err != nil {
			return fmt.Errorf("Unable to create log file: %v", err)
		}
		defer f.Close()
		log.SetOutput(io.MultiWriter(os.Stderr, f))
		out = f
	}

	debug.Serve(ctx, s.Debug)

	if s.app.Remote != "" {
		return s.forward()
	}

	// For debugging purposes only.
	run := func(ctx context.Context, srv *lsp.Server) {
		srv.Conn.AddHandler(&handler{loggingRPCs: s.Trace, out: out})
		go srv.Run(ctx)
	}
	if s.Address != "" {
		return lsp.RunServerOnAddress(ctx, s.app.cache, s.Address, run)
	}
	if s.Port != 0 {
		return lsp.RunServerOnPort(ctx, s.app.cache, s.Port, run)
	}
	stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout)
	ctx, srv := lsp.NewServer(ctx, s.app.cache, stream)
	srv.Conn.AddHandler(&handler{loggingRPCs: s.Trace, out: out})
	return srv.Run(ctx)
}

func (s *Serve) forward() error {
	conn, err := net.Dial("tcp", s.app.Remote)
	if err != nil {
		return err
	}
	errc := make(chan error)

	go func(conn net.Conn) {
		_, err := io.Copy(conn, os.Stdin)
		errc <- err
	}(conn)

	go func(conn net.Conn) {
		_, err := io.Copy(os.Stdout, conn)
		errc <- err
	}(conn)

	return <-errc
}

type handler struct {
	loggingRPCs bool
	out         io.Writer
}

type rpcStats struct {
	method    string
	direction jsonrpc2.Direction
	id        *jsonrpc2.ID
	payload   *json.RawMessage
	start     time.Time
	close     func()
}

type statsKeyType int

const statsKey = statsKeyType(0)

func (h *handler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
	return false
}

func (h *handler) Cancel(ctx context.Context, conn *jsonrpc2.Conn, id jsonrpc2.ID, cancelled bool) bool {
	return false
}

func (h *handler) Request(ctx context.Context, direction jsonrpc2.Direction, r *jsonrpc2.WireRequest) context.Context {
	if r.Method == "" {
		panic("no method in rpc stats")
	}
	s := &rpcStats{
		method:    r.Method,
		start:     time.Now(),
		direction: direction,
		payload:   r.Params,
	}
	mode := telemetry.Outbound
	if direction == jsonrpc2.Receive {
		mode = telemetry.Inbound
	}
	ctx, s.close = trace.StartSpan(ctx, r.Method,
		tag.Tag{Key: telemetry.Method, Value: r.Method},
		tag.Tag{Key: telemetry.RPCDirection, Value: mode},
		tag.Tag{Key: telemetry.RPCID, Value: r.ID},
	)
	telemetry.Started.Record(ctx, 1)
	return ctx
}

func (h *handler) Response(ctx context.Context, direction jsonrpc2.Direction, r *jsonrpc2.WireResponse) context.Context {
	stats := h.getStats(ctx)
	h.logRPC(direction, r.ID, 0, stats.method, r.Result, nil)
	return ctx
}

func (h *handler) Done(ctx context.Context, err error) {
	stats := h.getStats(ctx)
	h.logRPC(stats.direction, stats.id, time.Since(stats.start), stats.method, stats.payload, err)
	if err != nil {
		ctx = telemetry.StatusCode.With(ctx, "ERROR")
	} else {
		ctx = telemetry.StatusCode.With(ctx, "OK")
	}
	elapsedTime := time.Since(stats.start)
	latencyMillis := float64(elapsedTime) / float64(time.Millisecond)
	telemetry.Latency.Record(ctx, latencyMillis)
	stats.close()
}

func (h *handler) Read(ctx context.Context, bytes int64) context.Context {
	telemetry.SentBytes.Record(ctx, bytes)
	return ctx
}

func (h *handler) Wrote(ctx context.Context, bytes int64) context.Context {
	telemetry.ReceivedBytes.Record(ctx, bytes)
	return ctx
}

const eol = "\r\n\r\n\r\n"

func (h *handler) Error(ctx context.Context, err error) {
	stats := h.getStats(ctx)
	h.logRPC(stats.direction, stats.id, 0, stats.method, nil, err)
}

func (h *handler) getStats(ctx context.Context) *rpcStats {
	stats, ok := ctx.Value(statsKey).(*rpcStats)
	if !ok || stats == nil {
		stats = &rpcStats{
			method: "???",
			close:  func() {},
		}
	}
	return stats
}

func (h *handler) logRPC(direction jsonrpc2.Direction, id *jsonrpc2.ID, elapsed time.Duration, method string, payload *json.RawMessage, err error) {
	if !h.loggingRPCs {
		return
	}
	const eol = "\r\n\r\n\r\n"
	if err != nil {
		fmt.Fprintf(h.out, "[Error - %v] %s %s%s %v%s", time.Now().Format("3:04:05 PM"),
			direction, method, id, err, eol)
		return
	}
	outx := new(strings.Builder)
	fmt.Fprintf(outx, "[Trace - %v] ", time.Now().Format("3:04:05 PM"))
	switch direction {
	case jsonrpc2.Send:
		fmt.Fprint(outx, "Received ")
	case jsonrpc2.Receive:
		fmt.Fprint(outx, "Sending ")
	}
	switch {
	case id == nil:
		fmt.Fprint(outx, "notification ")
	case elapsed >= 0:
		fmt.Fprint(outx, "response ")
	default:
		fmt.Fprint(outx, "request ")
	}
	fmt.Fprintf(outx, "'%s", method)
	switch {
	case id == nil:
		// do nothing
	case id.Name != "":
		fmt.Fprintf(outx, " - (%s)", id.Name)
	default:
		fmt.Fprintf(outx, " - (%d)", id.Number)
	}
	fmt.Fprint(outx, "'")
	if elapsed >= 0 {
		msec := int(elapsed.Round(time.Millisecond) / time.Millisecond)
		fmt.Fprintf(outx, " in %dms", msec)
	}
	params := "null"
	if payload != nil {
		params = string(*payload)
	}
	if params == "null" {
		params = "{}"
	}
	fmt.Fprintf(outx, ".\r\nParams: %s%s", params, eol)
	fmt.Fprintf(h.out, "%s", outx.String())
}
<commit_msg>internal/lsp: fixed broken tracing<commit_after>// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cmd

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"path/filepath"
	"strings"
	"time"

	"golang.org/x/tools/internal/jsonrpc2"
	"golang.org/x/tools/internal/lsp"
	"golang.org/x/tools/internal/lsp/debug"
	"golang.org/x/tools/internal/lsp/telemetry"
	"golang.org/x/tools/internal/lsp/telemetry/tag"
	"golang.org/x/tools/internal/lsp/telemetry/trace"
	"golang.org/x/tools/internal/tool"
)

// Serve is a struct that exposes the configurable parts of the LSP server as
// flags, in the right form for tool.Main to consume.
type Serve struct {
	Logfile string `flag:"logfile" help:"filename to log to. if value is \"auto\", then logging to a default output file is enabled"`
	Mode    string `flag:"mode" help:"no effect"`
	Port    int    `flag:"port" help:"port on which to run gopls for debugging purposes"`
	Address string `flag:"listen" help:"address on which to listen for remote connections"`
	Trace   bool   `flag:"rpc.trace" help:"Print the full rpc trace in lsp inspector format"`
	Debug   string `flag:"debug" help:"Serve debug information on the supplied address"`

	app *Application
}

func (s *Serve) Name() string  { return "serve" }
func (s *Serve) Usage() string { return "" }
func (s *Serve) ShortHelp() string {
	return "run a server for Go code using the Language Server Protocol"
}
func (s *Serve) DetailedHelp(f *flag.FlagSet) {
	fmt.Fprint(f.Output(), `
The server communicates using JSONRPC2 on stdin and stdout, and is intended to be run directly as
a child of an editor process.

gopls server flags are:
`)
	f.PrintDefaults()
}

// Run configures a server based on the flags, and then runs it.
// It blocks until the server shuts down.
func (s *Serve) Run(ctx context.Context, args ...string) error {
	if len(args) > 0 {
		return tool.CommandLineErrorf("server does not take arguments, got %v", args)
	}
	out := os.Stderr
	if s.Logfile != "" {
		filename := s.Logfile
		if filename == "auto" {
			filename = filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.log", os.Getpid()))
		}
		f, err := os.Create(filename)
		if err != nil {
			return fmt.Errorf("Unable to create log file: %v", err)
		}
		defer f.Close()
		log.SetOutput(io.MultiWriter(os.Stderr, f))
		out = f
	}

	debug.Serve(ctx, s.Debug)

	if s.app.Remote != "" {
		return s.forward()
	}

	// For debugging purposes only.
	run := func(ctx context.Context, srv *lsp.Server) {
		srv.Conn.AddHandler(&handler{loggingRPCs: s.Trace, out: out})
		go srv.Run(ctx)
	}
	if s.Address != "" {
		return lsp.RunServerOnAddress(ctx, s.app.cache, s.Address, run)
	}
	if s.Port != 0 {
		return lsp.RunServerOnPort(ctx, s.app.cache, s.Port, run)
	}
	stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout)
	ctx, srv := lsp.NewServer(ctx, s.app.cache, stream)
	srv.Conn.AddHandler(&handler{loggingRPCs: s.Trace, out: out})
	return srv.Run(ctx)
}

func (s *Serve) forward() error {
	conn, err := net.Dial("tcp", s.app.Remote)
	if err != nil {
		return err
	}
	errc := make(chan error)

	go func(conn net.Conn) {
		_, err := io.Copy(conn, os.Stdin)
		errc <- err
	}(conn)

	go func(conn net.Conn) {
		_, err := io.Copy(os.Stdout, conn)
		errc <- err
	}(conn)

	return <-errc
}

type handler struct {
	loggingRPCs bool
	out         io.Writer
}

type rpcStats struct {
	method    string
	direction jsonrpc2.Direction
	id        *jsonrpc2.ID
	payload   *json.RawMessage
	start     time.Time
	close     func()
}

type statsKeyType int

const statsKey = statsKeyType(0)

func (h *handler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
	return false
}

func (h *handler) Cancel(ctx context.Context, conn *jsonrpc2.Conn, id jsonrpc2.ID, cancelled bool) bool {
	return false
}

func (h *handler) Request(ctx context.Context, direction jsonrpc2.Direction, r *jsonrpc2.WireRequest) context.Context {
	if r.Method == "" {
		panic("no method in rpc stats")
	}
	stats := &rpcStats{
		method:    r.Method,
		start:     time.Now(),
		direction: direction,
		payload:   r.Params,
	}
	ctx = context.WithValue(ctx, statsKey, stats)
	mode := telemetry.Outbound
	if direction == jsonrpc2.Receive {
		mode = telemetry.Inbound
	}
	ctx, stats.close = trace.StartSpan(ctx, r.Method,
		tag.Tag{Key: telemetry.Method, Value: r.Method},
		tag.Tag{Key: telemetry.RPCDirection, Value: mode},
		tag.Tag{Key: telemetry.RPCID, Value: r.ID},
	)
	telemetry.Started.Record(ctx, 1)
	return ctx
}

func (h *handler) Response(ctx context.Context, direction jsonrpc2.Direction, r *jsonrpc2.WireResponse) context.Context {
	stats := h.getStats(ctx)
	h.logRPC(direction, r.ID, 0, stats.method, r.Result, nil)
	return ctx
}

func (h *handler) Done(ctx context.Context, err error) {
	stats := h.getStats(ctx)
	h.logRPC(stats.direction, stats.id, time.Since(stats.start), stats.method, stats.payload, err)
	if err != nil {
		ctx = telemetry.StatusCode.With(ctx, "ERROR")
	} else {
		ctx = telemetry.StatusCode.With(ctx, "OK")
	}
	elapsedTime := time.Since(stats.start)
	latencyMillis := float64(elapsedTime) / float64(time.Millisecond)
	telemetry.Latency.Record(ctx, latencyMillis)
	stats.close()
}

func (h *handler) Read(ctx context.Context, bytes int64) context.Context {
	telemetry.SentBytes.Record(ctx, bytes)
	return ctx
}

func (h *handler) Wrote(ctx context.Context, bytes int64) context.Context {
	telemetry.ReceivedBytes.Record(ctx, bytes)
	return ctx
}

const eol = "\r\n\r\n\r\n"

func (h *handler) Error(ctx context.Context, err error) {
	stats := h.getStats(ctx)
	h.logRPC(stats.direction, stats.id, 0, stats.method, nil, err)
}

func (h *handler) getStats(ctx context.Context) *rpcStats {
	stats, ok := ctx.Value(statsKey).(*rpcStats)
	if !ok || stats == nil {
		method, ok := ctx.Value(telemetry.Method).(string)
		if !ok {
			method = "???"
		}
		stats = &rpcStats{
			method: method,
			close:  func() {},
		}
	}
	return stats
}

func (h *handler) logRPC(direction jsonrpc2.Direction, id *jsonrpc2.ID, elapsed time.Duration, method string, payload *json.RawMessage, err error) {
	if !h.loggingRPCs {
		return
	}
	const eol = "\r\n\r\n\r\n"
	if err != nil {
		fmt.Fprintf(h.out, "[Error - %v] %s %s%s %v%s", time.Now().Format("3:04:05 PM"),
			direction, method, id, err, eol)
		return
	}
	outx := new(strings.Builder)
	fmt.Fprintf(outx, "[Trace - %v] ", time.Now().Format("3:04:05 PM"))
	switch direction {
	case jsonrpc2.Send:
		fmt.Fprint(outx, "Received ")
	case jsonrpc2.Receive:
		fmt.Fprint(outx, "Sending ")
	}
	switch {
	case id == nil:
		fmt.Fprint(outx, "notification ")
	case elapsed >= 0:
		fmt.Fprint(outx, "response ")
	default:
		fmt.Fprint(outx, "request ")
	}
	fmt.Fprintf(outx, "'%s", method)
	switch {
	case id == nil:
		// do nothing
	case id.Name != "":
		fmt.Fprintf(outx, " - (%s)", id.Name)
	default:
		fmt.Fprintf(outx, " - (%d)", id.Number)
	}
	fmt.Fprint(outx, "'")
	if elapsed >= 0 {
		msec := int(elapsed.Round(time.Millisecond) / time.Millisecond)
		fmt.Fprintf(outx, " in %dms", msec)
	}
	params := "null"
	if payload != nil {
		params = string(*payload)
	}
	if params == "null" {
		params = "{}"
	}
	fmt.Fprintf(outx, ".\r\nParams: %s%s", params, eol)
	fmt.Fprintf(h.out, "%s", outx.String())
}
<|endoftext|>


<commit_before>package core

import (
	"context"
	"sync"
	"time"

	"chain/core/account"
	"chain/core/txbuilder"
	"chain/database/pg"
	"chain/errors"
	"chain/net/http/reqid"
	"chain/protocol"
	"chain/protocol/bc"
)

const (
	defaultTxTTL = 5 * time.Minute
)

func buildSingle(ctx context.Context, req *buildRequest) (*txbuilder.Template, error) {
	dbtx, ctx, err := pg.Begin(ctx)
	if err != nil {
		return nil, err
	}
	defer dbtx.Rollback(ctx)

	ttl := req.TTL.Duration
	if ttl == 0 {
		ttl = defaultTxTTL
	}
	maxTime := time.Now().Add(ttl)
	tpl, err := txbuilder.Build(ctx, req.Tx, req.actions(), req.ReferenceData, maxTime)
	if err != nil {
		return nil, err
	}

	err = dbtx.Commit(ctx)
	if err != nil {
		return nil, err
	}

	// ensure null is never returned for signing instructions
	if tpl.SigningInstructions == nil {
		tpl.SigningInstructions = []*txbuilder.SigningInstruction{}
	}

	return tpl, nil
}

// POST /build-transaction
func build(ctx context.Context, buildReqs []*buildRequest) (interface{}, error) {
	responses := make([]interface{}, len(buildReqs))
	var wg sync.WaitGroup
	wg.Add(len(responses))

	for i := 0; i < len(responses); i++ {
		go func(i int) {
			defer wg.Done()

			err := filterAliases(ctx, buildReqs[i])
			if err != nil {
				logHTTPError(ctx, err)
				responses[i], _ = errInfo(err)
				return
			}

			resp, err := buildSingle(reqid.NewSubContext(ctx, reqid.New()), buildReqs[i])
			if err != nil {
				logHTTPError(ctx, err)
				responses[i], _ = errInfo(err)
			} else {
				responses[i] = resp
			}
		}(i)
	}

	wg.Wait()
	return responses, nil
}

type submitSingleArg struct {
	tpl  *txbuilder.Template
	wait time.Duration
}

func submitSingle(ctx context.Context, c *protocol.Chain, x submitSingleArg) (interface{}, error) {
	// TODO(bobg): Set up an expiring context object outside this
	// function, perhaps in handler.ServeHTTPContext, and perhaps
	// initialize the timeout from the HTTP Timeout field.  (Or just
	// switch to gRPC.)
	timeout := x.wait
	if timeout <= 0 {
		timeout = 30 * time.Second
	}
	ctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()

	err := finalizeTxWait(ctx, c, x.tpl)
	if err != nil {
		return nil, err
	}

	return map[string]string{"id": x.tpl.Transaction.Hash().String()}, nil
}

// finalizeTxWait calls FinalizeTx and then waits for confirmation of
// the transaction.  A nil error return means the transaction is
// confirmed on the blockchain.  ErrRejected means a conflicting tx is
// on the blockchain.  context.DeadlineExceeded means ctx is an
// expiring context that timed out.
func finalizeTxWait(ctx context.Context, c *protocol.Chain, txTemplate *txbuilder.Template) error {
	// Avoid a race condition.  Calling c.Height() here ensures that
	// when we start waiting for blocks below, we don't begin waiting at
	// block N+1 when the tx we want is in block N.
	height := c.Height()

	if txTemplate.Transaction == nil {
		return errors.Wrap(txbuilder.ErrMissingRawTx)
	}

	tx := bc.NewTx(*txTemplate.Transaction)
	err := txbuilder.FinalizeTx(ctx, c, tx)
	if err != nil {
		return err
	}

	// As a rule we only index confirmed blockchain data to prevent dirty
	// reads, but here we're explicitly breaking that rule iff all of the
	// inputs to the transaction are from locally-controlled keys. In that
	// case, we're confident that this tx will be confirmed, so we relax
	// that constraint to allow use of unconfirmed change, etc.
	if txTemplate.Local {
		err := account.IndexUnconfirmedUTXOs(ctx, tx)
		if err != nil {
			return errors.Wrap(err, "indexing unconfirmed account utxos")
		}
	}

	for {
		height++
		select {
		case <-ctx.Done():
			return ctx.Err()

		case <-waitBlock(ctx, c, height):
			b, err := c.GetBlock(ctx, height)
			if err != nil {
				return errors.Wrap(err, "getting block that just landed")
			}
			for _, confirmed := range b.Transactions {
				if confirmed.Hash == tx.Hash {
					// confirmed
					return nil
				}
			}

			if tx.MaxTime > 0 && tx.MaxTime < b.TimestampMS {
				return txbuilder.ErrRejected
			}

			// might still be in pool or might be rejected; we can't
			// tell definitively until its max time elapses.

			// TODO(jackson): Re-insert into the pool.

			// TODO(jackson): Do simple rejection checks like checking if
			// the tx's blockchain prevouts still exist in the state tree.
		}
	}
}

func waitBlock(ctx context.Context, c *protocol.Chain, height uint64) <-chan struct{} {
	done := make(chan struct{}, 1)
	go func() {
		c.WaitForBlock(height)
		done <- struct{}{}
	}()
	return done
}

// TODO(bobg): allow caller to specify reservation by (encrypted) id?
// POST /v3/assets/cancel-reservation
// Idempotent
func cancelReservation(ctx context.Context, x struct{ Transaction bc.Tx }) error {
	var outpoints []bc.Outpoint
	for _, input := range x.Transaction.Inputs {
		if !input.IsIssuance() {
			outpoints = append(outpoints, input.Outpoint())
		}
	}
	return account.CancelReservations(ctx, outpoints)
}

type submitArg struct {
	Transactions []*txbuilder.Template
	wait         time.Duration
}

// POST /v3/transact/submit
// Idempotent
func (a *api) submit(ctx context.Context, x submitArg) interface{} {
	responses := make([]interface{}, len(x.Transactions))
	var wg sync.WaitGroup
	wg.Add(len(responses))
	for i := range responses {
		go func(i int) {
			resp, err := submitSingle(reqid.NewSubContext(ctx, reqid.New()), a.c, submitSingleArg{tpl: x.Transactions[i], wait: x.wait})
			if err != nil {
				logHTTPError(ctx, err)
				responses[i], _ = errInfo(err)
			} else {
				responses[i] = resp
			}
			wg.Done()
		}(i)
	}

	wg.Wait()
	return responses
}
<commit_msg>core: re-insert tx into pool, if not confirmed<commit_after>package core

import (
	"context"
	"sync"
	"time"

	"chain/core/account"
	"chain/core/txbuilder"
	"chain/database/pg"
	"chain/errors"
	"chain/net/http/reqid"
	"chain/protocol"
	"chain/protocol/bc"
)

const (
	defaultTxTTL = 5 * time.Minute
)

func buildSingle(ctx context.Context, req *buildRequest) (*txbuilder.Template, error) {
	dbtx, ctx, err := pg.Begin(ctx)
	if err != nil {
		return nil, err
	}
	defer dbtx.Rollback(ctx)

	ttl := req.TTL.Duration
	if ttl == 0 {
		ttl = defaultTxTTL
	}
	maxTime := time.Now().Add(ttl)
	tpl, err := txbuilder.Build(ctx, req.Tx, req.actions(), req.ReferenceData, maxTime)
	if err != nil {
		return nil, err
	}

	err = dbtx.Commit(ctx)
	if err != nil {
		return nil, err
	}

	// ensure null is never returned for signing instructions
	if tpl.SigningInstructions == nil {
		tpl.SigningInstructions = []*txbuilder.SigningInstruction{}
	}

	return tpl, nil
}

// POST /build-transaction
func build(ctx context.Context, buildReqs []*buildRequest) (interface{}, error) {
	responses := make([]interface{}, len(buildReqs))
	var wg sync.WaitGroup
	wg.Add(len(responses))

	for i := 0; i < len(responses); i++ {
		go func(i int) {
			defer wg.Done()

			err := filterAliases(ctx, buildReqs[i])
			if err != nil {
				logHTTPError(ctx, err)
				responses[i], _ = errInfo(err)
				return
			}

			resp, err := buildSingle(reqid.NewSubContext(ctx, reqid.New()), buildReqs[i])
			if err != nil {
				logHTTPError(ctx, err)
				responses[i], _ = errInfo(err)
			} else {
				responses[i] = resp
			}
		}(i)
	}

	wg.Wait()
	return responses, nil
}

type submitSingleArg struct {
	tpl  *txbuilder.Template
	wait time.Duration
}

func submitSingle(ctx context.Context, c *protocol.Chain, x submitSingleArg) (interface{}, error) {
	// TODO(bobg): Set up an expiring context object outside this
	// function, perhaps in handler.ServeHTTPContext, and perhaps
	// initialize the timeout from the HTTP Timeout field.  (Or just
	// switch to gRPC.)
	timeout := x.wait
	if timeout <= 0 {
		timeout = 30 * time.Second
	}
	ctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()

	err := finalizeTxWait(ctx, c, x.tpl)
	if err != nil {
		return nil, err
	}

	return map[string]string{"id": x.tpl.Transaction.Hash().String()}, nil
}

// finalizeTxWait calls FinalizeTx and then waits for confirmation of
// the transaction.  A nil error return means the transaction is
// confirmed on the blockchain.  ErrRejected means a conflicting tx is
// on the blockchain.  context.DeadlineExceeded means ctx is an
// expiring context that timed out.
func finalizeTxWait(ctx context.Context, c *protocol.Chain, txTemplate *txbuilder.Template) error {
	// Avoid a race condition.  Calling c.Height() here ensures that
	// when we start waiting for blocks below, we don't begin waiting at
	// block N+1 when the tx we want is in block N.
	height := c.Height()

	if txTemplate.Transaction == nil {
		return errors.Wrap(txbuilder.ErrMissingRawTx)
	}

	tx := bc.NewTx(*txTemplate.Transaction)
	err := txbuilder.FinalizeTx(ctx, c, tx)
	if err != nil {
		return err
	}

	// As a rule we only index confirmed blockchain data to prevent dirty
	// reads, but here we're explicitly breaking that rule iff all of the
	// inputs to the transaction are from locally-controlled keys. In that
	// case, we're confident that this tx will be confirmed, so we relax
	// that constraint to allow use of unconfirmed change, etc.
	if txTemplate.Local {
		err := account.IndexUnconfirmedUTXOs(ctx, tx)
		if err != nil {
			return errors.Wrap(err, "indexing unconfirmed account utxos")
		}
	}

	for {
		height++
		select {
		case <-ctx.Done():
			return ctx.Err()

		case <-waitBlock(ctx, c, height):
			b, err := c.GetBlock(ctx, height)
			if err != nil {
				return errors.Wrap(err, "getting block that just landed")
			}
			for _, confirmed := range b.Transactions {
				if confirmed.Hash == tx.Hash {
					// confirmed
					return nil
				}
			}

			if tx.MaxTime > 0 && tx.MaxTime < b.TimestampMS {
				return txbuilder.ErrRejected
			}

			// might still be in pool or might be rejected; we can't
			// tell definitively until its max time elapses.

			// Re-insert into the pool in case it was dropped.
			err = txbuilder.FinalizeTx(ctx, c, tx)
			if err != nil {
				return err
			}

			// TODO(jackson): Do simple rejection checks like checking if
			// the tx's blockchain prevouts still exist in the state tree.
		}
	}
}

func waitBlock(ctx context.Context, c *protocol.Chain, height uint64) <-chan struct{} {
	done := make(chan struct{}, 1)
	go func() {
		c.WaitForBlock(height)
		done <- struct{}{}
	}()
	return done
}

// TODO(bobg): allow caller to specify reservation by (encrypted) id?
// POST /v3/assets/cancel-reservation
// Idempotent
func cancelReservation(ctx context.Context, x struct{ Transaction bc.Tx }) error {
	var outpoints []bc.Outpoint
	for _, input := range x.Transaction.Inputs {
		if !input.IsIssuance() {
			outpoints = append(outpoints, input.Outpoint())
		}
	}
	return account.CancelReservations(ctx, outpoints)
}

type submitArg struct {
	Transactions []*txbuilder.Template
	wait         time.Duration
}

// POST /v3/transact/submit
// Idempotent
func (a *api) submit(ctx context.Context, x submitArg) interface{} {
	responses := make([]interface{}, len(x.Transactions))
	var wg sync.WaitGroup
	wg.Add(len(responses))
	for i := range responses {
		go func(i int) {
			resp, err := submitSingle(reqid.NewSubContext(ctx, reqid.New()), a.c, submitSingleArg{tpl: x.Transactions[i], wait: x.wait})
			if err != nil {
				logHTTPError(ctx, err)
				responses[i], _ = errInfo(err)
			} else {
				responses[i] = resp
			}
			wg.Done()
		}(i)
	}

	wg.Wait()
	return responses
}
<|endoftext|>


<commit_before>package notify

import (
	"log"
	"sync"

	"github.com/axetroy/watchdog"
	"github.com/pkg/errors"
)

type Notifier interface {
	Push(msg string) error
}

type Notify struct {
	reporter []watchdog.Reporter
	handler  Handler
}

type HandlerFn = func(message string, reporter watchdog.Reporter) error

type Handler = map[string]HandlerFn

func NewNotifier(ns []watchdog.Reporter) Notifier {
	notify := Notify{
		reporter: ns,
		handler:  map[string]HandlerFn{},
	}

	notify.use("wechat", Wechat)
	notify.use("webhook", Webhook)

	return notify
}

// 注册处理器
func (n *Notify) use(protocol string, handler HandlerFn) {
	n.handler[protocol] = handler
}

func (n Notify) Push(content string) error {
	wg := sync.WaitGroup{}
	wg.Add(len(n.reporter))

	for _, reporter := range n.reporter {
		go func(reporter watchdog.Reporter) {
			var (
				err error
			)

			defer func() {
				if err != nil {
					log.Printf("%+v\n", err)
				}
			}()

			defer wg.Done()

			if handler, ok := n.handler[reporter.Protocol]; ok {
				//do something here
				err = handler(content, reporter)
			} else {
				err = errors.Errorf("invlid protocol '%s'", reporter.Protocol)
			}
		}(reporter)
	}

	wg.Wait()
	return nil
}

func (n Notify) PushEmail(msg string) error {
	return nil
}
<commit_msg>refactor: improve code<commit_after>package notify

import (
	"log"
	"sync"

	"github.com/axetroy/watchdog"
	"github.com/pkg/errors"
)

type Notifier interface {
	Push(msg string) error
}

type notifier struct {
	reporter []watchdog.Reporter
	handler  handler
}

type handlerFn = func(message string, reporter watchdog.Reporter) error

type handler = map[string]handlerFn

// 创建新的消息通知器
func NewNotifier(ns []watchdog.Reporter) Notifier {
	notify := notifier{
		reporter: ns,
		handler:  map[string]handlerFn{},
	}

	notify.use("wechat", Wechat)
	notify.use("webhook", Webhook)

	return notify
}

// 注册处理器
func (n *notifier) use(protocol string, handler handlerFn) {
	n.handler[protocol] = handler
}

// 推送消息
func (n notifier) Push(content string) error {
	wg := sync.WaitGroup{}
	wg.Add(len(n.reporter))

	for _, reporter := range n.reporter {
		go func(reporter watchdog.Reporter) {
			var (
				err error
			)

			defer func() {
				if err != nil {
					log.Printf("%+v\n", err)
				}
			}()

			defer wg.Done()

			if handler, ok := n.handler[reporter.Protocol]; ok {
				//do something here
				err = handler(content, reporter)
			} else {
				err = errors.Errorf("invlid protocol '%s'", reporter.Protocol)
			}
		}(reporter)
	}

	wg.Wait()
	return nil
}
<|endoftext|>


<commit_before>package funtional

type Event chan interface{}
type Predicate func (interface{}) bool
type Mapper func (interface{}) interface{}
type MultiMapper func (...interface{}) interface{}
type Reducer func (memo interface{}, element interface{}) interface{}

type Signal struct {
	event Event
}

func (s *Signal)  Map(fn Mapper) Signal {
	c := make(Event)

	go func () {
		for el := range s.event {
			c <- fn(el)
		}
		close(c)
	}()
	return Signal{c}
}

func (s *Signal) Filter(pred Predicate) Signal {
	c := make(Event)
	go func () {
		for el := range s.event {
			if keep := pred(el); keep {
				c <- el
			}
		}
		close(c)
	}()
	return Signal{c}
}

func  prueba(s Signal) Signal {

	return s.Map(func (interface{})).Filter().Map()
}<commit_msg>fromValues<commit_after>package funtional

type Event chan interface{}
type Predicate func (interface{}) bool
type Mapper func (interface{}) interface{}
type MultiMapper func (...interface{}) interface{}
type Reducer func (memo interface{}, element interface{}) interface{}

type Signal struct {
	event Event
}

func (s *Signal)  Map(fn Mapper) Signal {
	c := make(Event)

	go func () {
		for el := range s.event {
			c <- fn(el)
		}
		close(c)
	}()
	return Signal{c}
}

func (s *Signal) Filter(pred Predicate) Signal {
	c := make(Event)
	go func () {
		for el := range s.event {
			if keep := pred(el); keep {
				c <- el
			}
		}
		close(c)
	}()
	return Signal{c}
}

func (s *Signal) Reduce( red Reducer, memo interface{}) interface{} {
	for el := range s.event {
		memo = red(memo, el)
	}
	return memo
}

func FromValues(els ... interface {}) Signal {
	c := make(Event)
	go func () {
		for _, el := range els {
			c <- el
		}
		close(c)
	}()
	return Signal{c}
}<|endoftext|>


<commit_before>package taskfile

// Tasks represents a group of tasks
type Tasks map[string]*Task

// Task represents a task
type Task struct {
	Task         string
	Cmds         []*Cmd
	Deps         []*Dep
	Desc         string
	Summary      string
	Sources      []string
	Generates    []string
	Status       []string
	Preconditions []*Precondition
	Dir          string
	Vars         Vars
	Env          Vars
	Silent       bool
	Method       string
	Prefix       string
	IgnoreError  bool `yaml:"ignore_error"`
}
<commit_msg>go fmt internal/taskfile/task.go<commit_after>package taskfile

// Tasks represents a group of tasks
type Tasks map[string]*Task

// Task represents a task
type Task struct {
	Task          string
	Cmds          []*Cmd
	Deps          []*Dep
	Desc          string
	Summary       string
	Sources       []string
	Generates     []string
	Status        []string
	Preconditions []*Precondition
	Dir           string
	Vars          Vars
	Env           Vars
	Silent        bool
	Method        string
	Prefix        string
	IgnoreError   bool `yaml:"ignore_error"`
}
<|endoftext|>


<commit_before>// Copyright 2018 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package thread

import (
	"context"
)

// Thread represents an OS thread.
type Thread struct {
	funcs   chan func() error
	results chan error
}

// New creates a new thread.
//
// It is assumed that the OS thread is fixed by runtime.LockOSThread when New is called.
func New() *Thread {
	return &Thread{
		funcs:   make(chan func() error),
		results: make(chan error),
	}
}

// Loop starts the thread loop.
//
// Loop must be called on the thread.
func (t *Thread) Loop(context context.Context) {
loop:
	for {
		select {
		case f := <-t.funcs:
			t.results <- f()
		case <-context.Done():
			break loop
		}
	}
}

// Call calls f on the thread.
//
// Do not call this from the same thread. This would block forever.
//
// Call panics when Loop already ends.
func (t *Thread) Call(f func() error) error {
	t.funcs <- f
	return <-t.results
}
<commit_msg>thread: Fix comments<commit_after>// Copyright 2018 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package thread

import (
	"context"
)

// Thread represents an OS thread.
type Thread struct {
	funcs   chan func() error
	results chan error
}

// New creates a new thread.
//
// It is assumed that the OS thread is fixed by runtime.LockOSThread when New is called.
func New() *Thread {
	return &Thread{
		funcs:   make(chan func() error),
		results: make(chan error),
	}
}

// Loop starts the thread loop.
//
// Loop must be called on the thread.
//
// Loop can be called multiple times.
func (t *Thread) Loop(context context.Context) {
loop:
	for {
		select {
		case f := <-t.funcs:
			t.results <- f()
		case <-context.Done():
			break loop
		}
	}
}

// Call calls f on the thread.
//
// Do not call this from the same thread. This would block forever.
//
// Call blocks if Loop is not called.
func (t *Thread) Call(f func() error) error {
	t.funcs <- f
	return <-t.results
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"
	"os"
	"time"

	"github.com/asamy/steam"
)

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)

	timeTip, err := steam.GetTimeTip()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Time tip: %#v\n", timeTip)

	timeDiff := time.Duration(timeTip.Time - time.Now().Unix())
	session := steam.NewSession(&http.Client{}, "")
	if err := session.Login(os.Getenv("steamAccount"), os.Getenv("steamPassword"), os.Getenv("steamSharedSecret"), timeDiff); err != nil {
		log.Fatal(err)
	}
	log.Print("Login successful")

	profileURL, err := session.GetProfileURL()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Profile URL: %s\n", profileURL)

	profileSetting := uint8( /*Profile*/ steam.PrivacyStatePublic | /*Inventory*/ steam.PrivacyStatePublic<<2 | /*Gifts*/ steam.PrivacyStatePublic<<4)
	if err = session.SetProfilePrivacy(profileURL, steam.CommentSettingSelf, profileSetting); err != nil {
		log.Fatal(err)
	}
	log.Printf("Done editing profile: %d", profileSetting)

	profileInfo := map[string][]string{
		"personaName": {"MasterOfTests"},
		"summary":     {"i am just a test, go away"},
		"customURL":   {"therealtesterOFDOOM"},
	}
	if err = session.SetProfileInfo(profileURL, &profileInfo); err != nil {
		log.Fatal(err)
	}
	log.Print("Done editing profile info")

	myToken, err := session.GetMyTradeToken()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Trade offer token: %s\n", myToken)

	key, err := session.GetWebAPIKey()
	if err != nil {
		log.Fatal(err)
	}
	log.Print("Key: ", key)

	summaries, err := session.GetPlayerSummaries("76561198078821986")
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Profile summaries: %#v\n", summaries[0])

	sid := steam.SteamID(76561198078821986)
	inven, err := session.GetInventory(sid, 730, 2, false)
	if err != nil {
		log.Fatal(err)
	}

	for _, item := range inven {
		log.Printf("Item: %s = %d\n", item.Desc.MarketHashName, item.AssetID)
	}

	marketPrices, err := session.GetMarketItemPriceHistory(730, "P90 | Asiimov (Factory New)")
	if err != nil {
		log.Fatal(err)
	}

	for _, v := range marketPrices {
		log.Printf("%s -> %.2f (%s of same price)\n", v.Date, v.Price, v.Count)
	}

	overview, err := session.GetMarketItemPriceOverview(730, "DE", "3", "P90 | Asiimov (Factory New)")
	if err != nil {
		log.Fatal(err)
	}

	if overview.Success {
		log.Println("Price overfiew for P90 Asiimov FN:")
		log.Printf("Volume: %s\n", overview.Volume)
		log.Printf("Lowest price: %s Median Price: %s", overview.LowestPrice, overview.MedianPrice)
	}

	resp, err := session.GetTradeOffers(steam.TradeFilterSentOffers, time.Now())
	if err != nil {
		log.Fatal(err)
	}

	var receiptID uint64
	for _, offer := range resp.SentOffers {
		var sid steam.SteamID
		sid.Parse(offer.Partner, steam.AccountInstanceDesktop, steam.AccountTypeIndividual, steam.UniversePublic)

		if receiptID == 0 && len(offer.RecvItems) != 0 && offer.State == steam.TradeStateAccepted {
			receiptID = offer.ReceiptID
		}

		log.Printf("Offer id: %d, Receipt ID: %d", offer.ID, offer.ReceiptID)
		log.Printf("Offer partner SteamID 64: %d", uint64(sid))
	}

	items, err := session.GetTradeReceivedItems(receiptID)
	if err != nil {
		log.Fatal(err)
	}

	for _, item := range items {
		log.Printf("New asset id: %d", item.AssetID)
	}

	identity := os.Getenv("steamIdentitySecret")
	confirmations, err := session.GetConfirmations(identity, time.Now().Add(timeDiff).Unix())
	if err != nil {
		log.Fatal(err)
	}

	for i := range confirmations {
		c := confirmations[i]
		log.Printf("Confirmation ID: %d, Key: %d\n", c.ID, c.Key)
		log.Printf("-> Title %s\n", c.Title)
		log.Printf("-> Receiving %s\n", c.Receiving)
		log.Printf("-> Since %s\n", c.Since)

		tid, err := session.GetConfirmationOfferID(key, c.ID, time.Now().Add(timeDiff).Unix())
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("-> OfferID %d\n", tid)

		err = session.AnswerConfirmation(c, key, "allow", time.Now().Add(timeDiff).Unix())
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("Accepted %d\n", c.ID)
	}

	log.Println("Bye!")
}
<commit_msg>examples/all: fix inventory loading<commit_after>package main

import (
	"log"
	"net/http"
	"os"
	"time"

	"github.com/asamy/steam"
)

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)

	timeTip, err := steam.GetTimeTip()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Time tip: %#v\n", timeTip)

	timeDiff := time.Duration(timeTip.Time - time.Now().Unix())
	session := steam.NewSession(&http.Client{}, "")
	if err := session.Login(os.Getenv("steamAccount"), os.Getenv("steamPassword"), os.Getenv("steamSharedSecret"), timeDiff); err != nil {
		log.Fatal(err)
	}
	log.Print("Login successful")

	profileURL, err := session.GetProfileURL()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Profile URL: %s\n", profileURL)

	profileSetting := uint8( /*Profile*/ steam.PrivacyStatePublic | /*Inventory*/ steam.PrivacyStatePublic<<2 | /*Gifts*/ steam.PrivacyStatePublic<<4)
	if err = session.SetProfilePrivacy(profileURL, steam.CommentSettingSelf, profileSetting); err != nil {
		log.Fatal(err)
	}
	log.Printf("Done editing profile: %d", profileSetting)

	profileInfo := map[string][]string{
		"personaName": {"MasterOfTests"},
		"summary":     {"i am just a test, go away"},
		"customURL":   {"therealtesterOFDOOM"},
	}
	if err = session.SetProfileInfo(profileURL, &profileInfo); err != nil {
		log.Fatal(err)
	}
	log.Print("Done editing profile info")

	myToken, err := session.GetMyTradeToken()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Trade offer token: %s\n", myToken)

	key, err := session.GetWebAPIKey()
	if err != nil {
		log.Fatal(err)
	}
	log.Print("Key: ", key)

	summaries, err := session.GetPlayerSummaries("76561198078821986")
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Profile summaries: %#v\n", summaries[0])

	sid := steam.SteamID(76561198078821986)
	inven, err := session.GetInventory(sid, 730, 2, steam.LangEng)
	if err != nil {
		log.Fatal(err)
	}

	for _, item := range inven {
		log.Printf("Item: %s = %d\n", item.Name.MarketHash, item.AssetID)
	}

	marketPrices, err := session.GetMarketItemPriceHistory(730, "P90 | Asiimov (Factory New)")
	if err != nil {
		log.Fatal(err)
	}

	for _, v := range marketPrices {
		log.Printf("%s -> %.2f (%s of same price)\n", v.Date, v.Price, v.Count)
	}

	overview, err := session.GetMarketItemPriceOverview(730, "DE", "3", "P90 | Asiimov (Factory New)")
	if err != nil {
		log.Fatal(err)
	}

	if overview.Success {
		log.Println("Price overfiew for P90 Asiimov FN:")
		log.Printf("Volume: %s\n", overview.Volume)
		log.Printf("Lowest price: %s Median Price: %s", overview.LowestPrice, overview.MedianPrice)
	}

	resp, err := session.GetTradeOffers(steam.TradeFilterSentOffers, time.Now())
	if err != nil {
		log.Fatal(err)
	}

	var receiptID uint64
	for _, offer := range resp.SentOffers {
		var sid steam.SteamID
		sid.Parse(offer.Partner, steam.AccountInstanceDesktop, steam.AccountTypeIndividual, steam.UniversePublic)

		if receiptID == 0 && len(offer.RecvItems) != 0 && offer.State == steam.TradeStateAccepted {
			receiptID = offer.ReceiptID
		}

		log.Printf("Offer id: %d, Receipt ID: %d", offer.ID, offer.ReceiptID)
		log.Printf("Offer partner SteamID 64: %d", uint64(sid))
	}

	items, err := session.GetTradeReceivedItems(receiptID)
	if err != nil {
		log.Fatal(err)
	}

	for _, item := range items {
		log.Printf("New asset id: %d", item.AssetID)
	}

	identity := os.Getenv("steamIdentitySecret")
	confirmations, err := session.GetConfirmations(identity, time.Now().Add(timeDiff).Unix())
	if err != nil {
		log.Fatal(err)
	}

	for i := range confirmations {
		c := confirmations[i]
		log.Printf("Confirmation ID: %d, Key: %d\n", c.ID, c.Key)
		log.Printf("-> Title %s\n", c.Title)
		log.Printf("-> Receiving %s\n", c.Receiving)
		log.Printf("-> Since %s\n", c.Since)

		tid, err := session.GetConfirmationOfferID(key, c.ID, time.Now().Add(timeDiff).Unix())
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("-> OfferID %d\n", tid)

		err = session.AnswerConfirmation(c, key, "allow", time.Now().Add(timeDiff).Unix())
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("Accepted %d\n", c.ID)
	}

	log.Println("Bye!")
}
<|endoftext|>