Last active
June 24, 2020 19:12
-
-
Save renthraysk/cf01be2d6b6f9e9cfcce6d5304618291 to your computer and use it in GitHub Desktop.
Cancellation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"context" | |
"errors" | |
"fmt" | |
"log" | |
"net" | |
"net/http" | |
"os" | |
"os/signal" | |
"time" | |
) | |
type contextKey string | |
const ( | |
ShutdownContextKey = contextKey("shutdown") | |
) | |
func main() { | |
errCh := make(chan error) | |
sigCh := make(chan os.Signal) | |
signal.Notify(sigCh, os.Interrupt) | |
ctx := context.Background() | |
shutdownCtx, cancel := context.WithCancel(ctx) | |
ctx = context.WithValue(ctx, ShutdownContextKey, shutdownCtx) | |
s := &http.Server{ | |
Addr: ":8080", | |
Handler: &Long{}, | |
BaseContext: func(_ net.Listener) context.Context { return ctx }, | |
} | |
s.RegisterOnShutdown(cancel) | |
go func() { errCh <- s.ListenAndServe() }() | |
// Main loop | |
var err error | |
for err == nil { | |
select { | |
case s := <-sigCh: | |
switch s { | |
case os.Interrupt: | |
err = errors.New(s.String()) | |
} | |
case err = <-errCh: | |
} | |
} | |
// Shutdown | |
{ | |
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) | |
if err := s.Shutdown(ctx); err != nil { | |
log.Printf("error during shutdown: %v", err) | |
} | |
cancel() | |
} | |
if err != nil { | |
log.Printf("shutdown: %v", err) | |
os.Exit(1) | |
} | |
os.Exit(0) | |
} | |
type Long struct{} | |
func (Long) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
if r.Method != http.MethodGet { | |
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) | |
return | |
} | |
f, ok := w.(http.Flusher) | |
if !ok { | |
http.Error(w, "Streaming events not supported", http.StatusBadRequest) | |
return | |
} | |
w.Header().Set("Content-Type", "text/event-stream") | |
w.Header().Set("Cache-Control", "no-cache") | |
w.Header().Set("Transfer-Encoding", "chunked") | |
ctx := r.Context().Value(ShutdownContextKey).(context.Context) | |
t := time.NewTicker(time.Second) | |
defer t.Stop() | |
for { | |
f.Flush() | |
select { | |
case <-t.C: | |
fmt.Fprintf(w, "type: ping\n\n") | |
case <-r.Context().Done(): // Client gone | |
return | |
case <-ctx.Done(): // Server shutting down | |
fmt.Fprintf(w, "type: shutdown\n\n") | |
f.Flush() | |
return | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment