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  }