master
go 69 lines 1.69 KB
Raw
1 package corehttp
2
3 import (
4 "bufio"
5 "fmt"
6 "net"
7 "net/http"
8
9 logging "github.com/ipfs/go-log/v2"
10 core "github.com/ipfs/kubo/core"
11 )
12
13 func LogOption() ServeOption {
14 return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
15 mux.HandleFunc("/logs", func(w http.ResponseWriter, r *http.Request) {
16 // The log data comes from an io.Reader, and we need to constantly
17 // read from it and then write to the HTTP response.
18 pipeReader := logging.NewPipeReader()
19 done := make(chan struct{})
20
21 // Close the pipe reader if the request context is canceled. This
22 // is necessary to avoiding blocking on reading from the pipe
23 // reader when the client terminates the request.
24 go func() {
25 select {
26 case <-r.Context().Done(): // Client canceled request
27 case <-n.Context().Done(): // Node shutdown
28 case <-done: // log reader goroutine exitex
29 }
30 pipeReader.Close()
31 }()
32
33 errs := make(chan error, 1)
34
35 go func() {
36 defer close(errs)
37 defer close(done)
38
39 rdr := bufio.NewReader(pipeReader)
40 for {
41 // Read a line of log data and send it to the client.
42 line, err := rdr.ReadString('\n')
43 if err != nil {
44 errs <- fmt.Errorf("error reading log message: %s", err)
45 return
46 }
47 _, err = w.Write([]byte(line))
48 if err != nil {
49 // Failed to write to client, probably disconnected.
50 return
51 }
52 if f, ok := w.(http.Flusher); ok {
53 f.Flush()
54 }
55 if r.Context().Err() != nil {
56 return
57 }
58 }
59 }()
60 log.Info("log API client connected")
61 err := <-errs
62 if err != nil {
63 http.Error(w, err.Error(), http.StatusInternalServerError)
64 return
65 }
66 })
67 return mux, nil
68 }
69 }