func main() { services := []struct { uri string svc host.Service }{ // Examples { "/core/examples", host.NewFactoryServiceContext(&host.ExampleFactoryService{}), }, // Examples { "/core/ping", host.NewPingService(), }, } var err error flag.Parse() glog.Infof("Started with %s", os.Args[1:]) h := host.NewServiceHost() err = h.Initialize(bindAddress.String()) if err != nil { glog.Fatalf("Error initializing: %s\n", err) } ctx := operation.SetAuthorizationToken(context.Background(), authToken) _, err = host.GetServiceHostManagementState(ctx) if err != nil { glog.Fatalf("Error getting ServiceHostState: %s\n", err) } var ops []*operation.Operation for _, s := range services { op := operation.NewPost(ctx, uri.Extend(uri.Empty(), s.uri), nil) ops = append(ops, op) h.StartService(op, s.svc) } _, err = operation.Join(ops) if err != nil { glog.Fatalf("Error starting services: %s", err) } start(h) }
// ServeHTTP implements the host's core http entry point. func (h *ServiceHost) ServeHTTP(rw http.ResponseWriter, req *http.Request) { ctx := context.Background() op := operation.NewOperation(ctx) op.SetRequest(req) if glog.V(xenon.Debug) { glog.Infof("host: %s %s", req.Method, req.URL.Path) } // Create context for handling this request. The request associated with // this operation might complete long before the workflow that it kicked // off completes. This workflow needs to flow parameters such as those // related to authorization to other operations and workflows. // On the Java side this is solved by using thread locals that are carefully // set when an operation is handled, making sure they can automatically be // flowed to newly created operations. On the Go side there is no equivalent // for thread locals so this context has to be passed around explicitly. if t := req.Header.Get(operation.RequestAuthTokenHeader); t != "" { ctx = operation.SetAuthorizationToken(ctx, t) } // Operations may be marked complete before this function returns. // Handle the request in a separate routine to return to the client ASAP. op.Start() go h.HandleRequest(ctx, op) if err := op.Wait(); err != nil { if handler, ok := err.(http.Handler); ok { if glog.V(xenon.Debug) { glog.Warningf("host: %s %s: custom error handler", req.Method, req.URL.Path) } handler.ServeHTTP(rw, req) return } body := &common.ServiceErrorResponse{ Message: err.Error(), } op.SetStatusCode(http.StatusInternalServerError) op.SetBody(body) } // Default status code to 200 OK code := op.GetStatusCode() if code == 0 { code = http.StatusOK } if glog.V(xenon.Debug) { glog.Infof("host: %s %s: status %d", req.Method, req.URL.Path, code) } if !op.IsStreamResponse() { rw.Header().Set(operation.ContentType, operation.ApplicationJSON) } rw.WriteHeader(code) if err := op.EncodeBody(rw); err != nil && err != operation.ErrBodyNotSet { glog.Warningf("host: failure writing body: %s\n", err) } }