You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tailscale-android/cmd/localapiclient/localapiclient.go

88 lines
2.1 KiB
Go

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)
}
}