github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/syz-cluster/pkg/api/http.go (about) 1 // Copyright 2025 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package api 5 6 import ( 7 "bytes" 8 "context" 9 "encoding/json" 10 "fmt" 11 "io" 12 "mime/multipart" 13 "net/http" 14 ) 15 16 func getJSON[Resp any](ctx context.Context, url string) (*Resp, error) { 17 httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) 18 if err != nil { 19 return nil, err 20 } 21 return finishRequest[Resp](httpReq) 22 } 23 24 func postJSON[Req any, Resp any](ctx context.Context, url string, req *Req) (*Resp, error) { 25 jsonBody, err := json.Marshal(req) 26 if err != nil { 27 return nil, err 28 } 29 httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(jsonBody)) 30 if err != nil { 31 return nil, err 32 } 33 httpReq.Header.Set("Content-Type", "application/json") 34 return finishRequest[Resp](httpReq) 35 } 36 37 func postMultiPartFile[Resp any](ctx context.Context, url string, reader io.Reader) (*Resp, error) { 38 // TODO: this will work well only up to some size of the file. We need a pipe and a goroutine. 39 body := &bytes.Buffer{} 40 writer := multipart.NewWriter(body) 41 part, err := writer.CreateFormFile("content", "content") 42 if err != nil { 43 return nil, fmt.Errorf("failed to create a form file part: %w", err) 44 } 45 if _, err := io.Copy(part, reader); err != nil { 46 return nil, err 47 } 48 if err := writer.Close(); err != nil { 49 return nil, err 50 } 51 httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body) 52 if err != nil { 53 return nil, err 54 } 55 httpReq.Header.Set("Content-Type", writer.FormDataContentType()) 56 return finishRequest[Resp](httpReq) 57 } 58 59 func ReplyJSON[T any](w http.ResponseWriter, resp T) { 60 w.Header().Set("Content-Type", "application/json") 61 err := json.NewEncoder(w).Encode(resp) 62 if err != nil { 63 http.Error(w, "failed to serialize the response", http.StatusInternalServerError) 64 return 65 } 66 } 67 68 func ParseJSON[T any](w http.ResponseWriter, r *http.Request) *T { 69 if r.Method != http.MethodPost { 70 http.Error(w, "must be called via POST", http.StatusMethodNotAllowed) 71 return nil 72 } 73 body, err := io.ReadAll(r.Body) 74 if err != nil { 75 http.Error(w, "failed to read body", http.StatusBadRequest) 76 return nil 77 } 78 var data T 79 err = json.Unmarshal(body, &data) 80 if err != nil { 81 http.Error(w, "invalid body", http.StatusBadRequest) 82 return nil 83 } 84 return &data 85 }