cmd/tailscale/main: use localapi for generating bug report
Fix logIDPublic and make localapiclient a package with a generic function that can be reused for all featuresoxtocart/bugreportvialocalapi_codereview_suggestion
parent
813ca8adea
commit
171f6a4971
@ -0,0 +1,87 @@
|
|||||||
|
package localapiclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tailscale.com/ipn/localapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LocalAPIResponseWriter substitutes for http.ResponseWriter in order to write byte streams directly
|
||||||
|
// to a receiver function in the application.
|
||||||
|
type LocalApiResponseWriter struct {
|
||||||
|
headers http.Header
|
||||||
|
body bytes.Buffer
|
||||||
|
status int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLocalApiResponseWriter() *LocalApiResponseWriter {
|
||||||
|
return &LocalApiResponseWriter{headers: http.Header{}, status: http.StatusOK}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *LocalApiResponseWriter) Header() http.Header {
|
||||||
|
return w.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes the data to the response body, which will be sent to Java. If WriteHeader is not called
|
||||||
|
// explicitly, the first call to Write will trigger an implicit WriteHeader(http.StatusOK).
|
||||||
|
func (w *LocalApiResponseWriter) Write(data []byte) (int, error) {
|
||||||
|
if w.status == 0 {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
return w.body.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *LocalApiResponseWriter) WriteHeader(statusCode int) {
|
||||||
|
w.status = statusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *LocalApiResponseWriter) Body() []byte {
|
||||||
|
return w.body.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *LocalApiResponseWriter) StatusCode() int {
|
||||||
|
return w.status
|
||||||
|
}
|
||||||
|
|
||||||
|
type LocalApiClient struct {
|
||||||
|
h *localapi.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLocalApiClient(h *localapi.Handler) LocalApiClient {
|
||||||
|
return LocalApiClient{h: h}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrBadHttpStatus = errors.New("bad http status for localapi response")
|
||||||
|
|
||||||
|
func CallLocalApi(h *localapi.Handler, method string, endpoint string) (*LocalApiResponseWriter, error) {
|
||||||
|
done := make(chan *LocalApiResponseWriter, 1)
|
||||||
|
var responseError error
|
||||||
|
go func() {
|
||||||
|
req, err := http.NewRequest(method, "/localapi/v0/"+endpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error creating new request for %s: %v", endpoint, err)
|
||||||
|
responseError = err
|
||||||
|
close(done)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w := newLocalApiResponseWriter()
|
||||||
|
h.ServeHTTP(w, req)
|
||||||
|
if w.StatusCode() > 300 {
|
||||||
|
log.Printf("%s bad http status: %v", endpoint, w.StatusCode())
|
||||||
|
responseError = ErrBadHttpStatus
|
||||||
|
}
|
||||||
|
done <- w
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case w := <-done:
|
||||||
|
return w, responseError
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
return nil, fmt.Errorf("request to %s timed out", endpoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
module github.com/tailscale/tailscale-android/localapiclient
|
||||||
|
|
||||||
|
go 1.21.6
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
package localapiclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tailscale.com/ipn/localapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LocalApiResponseWriter is our custom implementation of http.ResponseWriter
|
||||||
|
type LocalApiResponseWriter struct {
|
||||||
|
headers http.Header
|
||||||
|
body bytes.Buffer
|
||||||
|
status int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLocalApiResponseWriter() *LocalApiResponseWriter {
|
||||||
|
return &LocalApiResponseWriter{headers: http.Header{}, status: http.StatusOK}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *LocalApiResponseWriter) Header() http.Header {
|
||||||
|
return w.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes the data to the response body and will send the data to Java.
|
||||||
|
func (w *LocalApiResponseWriter) Write(data []byte) (int, error) {
|
||||||
|
if w.status == 0 {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
return w.body.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *LocalApiResponseWriter) WriteHeader(statusCode int) {
|
||||||
|
w.status = statusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *LocalApiResponseWriter) Body() []byte {
|
||||||
|
return w.body.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *LocalApiResponseWriter) StatusCode() int {
|
||||||
|
return w.status
|
||||||
|
}
|
||||||
|
|
||||||
|
type LocalApi interface {
|
||||||
|
GetBugReportID(result chan<- string, h *localapi.Handler, fallbackLog string) (LocalApiResponseWriter, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BugReportLocalApi struct{}
|
||||||
|
|
||||||
|
func (b BugReportLocalApi) GetBugReportID(bugReportChan chan<- string, h *localapi.Handler, fallbackLog string) (*LocalApiResponseWriter, error) {
|
||||||
|
w := newLocalApiResponseWriter()
|
||||||
|
req, err := http.NewRequest("POST", "/localapi/v0/bugreport", nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error creating new request for bug report: %v", err)
|
||||||
|
return w, err
|
||||||
|
}
|
||||||
|
h.ServeHTTP(w, req)
|
||||||
|
if w.StatusCode() > 300 {
|
||||||
|
err := fmt.Errorf("bug report bad http status: %v", w.StatusCode())
|
||||||
|
log.Printf("%s", err)
|
||||||
|
bugReportChan <- fallbackLog
|
||||||
|
return w, err
|
||||||
|
}
|
||||||
|
report := string(w.Body())
|
||||||
|
select {
|
||||||
|
case bugReportChan <- report:
|
||||||
|
err := fmt.Errorf("bug report was successfully retrieved: %s", report)
|
||||||
|
log.Printf("%s", err)
|
||||||
|
return w, err
|
||||||
|
// timeout, send fallback log
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
err := fmt.Errorf("bug report retrieval timed out, sending fallback log: %s", fallbackLog)
|
||||||
|
bugReportChan <- fallbackLog
|
||||||
|
log.Printf("%s", err)
|
||||||
|
return w, err
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue