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