// Copyright 2025 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. package api import ( "bytes" "context" "encoding/json" "fmt" "io" "mime/multipart" "net/http" ) func getJSON[Resp any](ctx context.Context, url string) (*Resp, error) { httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return nil, err } return finishRequest[Resp](httpReq) } func postJSON[Req any, Resp any](ctx context.Context, url string, req *Req) (*Resp, error) { jsonBody, err := json.Marshal(req) if err != nil { return nil, err } httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(jsonBody)) if err != nil { return nil, err } httpReq.Header.Set("Content-Type", "application/json") return finishRequest[Resp](httpReq) } func postMultiPartFile[Resp any](ctx context.Context, url string, reader io.Reader) (*Resp, error) { // TODO: this will work well only up to some size of the file. We need a pipe and a goroutine. body := &bytes.Buffer{} writer := multipart.NewWriter(body) part, err := writer.CreateFormFile("content", "content") if err != nil { return nil, fmt.Errorf("failed to create a form file part: %w", err) } if _, err := io.Copy(part, reader); err != nil { return nil, err } if err := writer.Close(); err != nil { return nil, err } httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body) if err != nil { return nil, err } httpReq.Header.Set("Content-Type", writer.FormDataContentType()) return finishRequest[Resp](httpReq) } func ReplyJSON[T any](w http.ResponseWriter, resp T) { w.Header().Set("Content-Type", "application/json") err := json.NewEncoder(w).Encode(resp) if err != nil { http.Error(w, "failed to serialize the response", http.StatusInternalServerError) return } } func ParseJSON[T any](w http.ResponseWriter, r *http.Request) *T { if r.Method != http.MethodPost { http.Error(w, "must be called via POST", http.StatusMethodNotAllowed) return nil } body, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "failed to read body", http.StatusBadRequest) return nil } var data T err = json.Unmarshal(body, &data) if err != nil { http.Error(w, "invalid body", http.StatusBadRequest) return nil } return &data }