MCPcopy Create free account
hub / github.com/diillson/chatcli / perform

Method perform

cli/plugins/builtin_http.go:176–227  ·  view source on GitHub ↗

perform executes the request through the shared hardened client.

(ctx context.Context, in httpArgs)

Source from the content-addressed store, hash-verified

174
175// perform executes the request through the shared hardened client.
176func (p *BuiltinHTTPPlugin) perform(ctx context.Context, in httpArgs) (string, error) {
177 if len(in.Body) > httpMaxRequestBody {
178 return "", fmt.Errorf("@http: request body too large (%d bytes, limit %d)", len(in.Body), httpMaxRequestBody)
179 }
180
181 safeURL, err := validateWebTarget(in.URL)
182 if err != nil {
183 return "", fmt.Errorf("@http: refusing %q: %w", in.URL, err)
184 }
185
186 timeout := httpDefaultTimeout
187 if in.TimeoutSeconds > 0 {
188 timeout = time.Duration(in.TimeoutSeconds) * time.Second
189 if timeout > httpMaxTimeout {
190 timeout = httpMaxTimeout
191 }
192 }
193 reqCtx, cancel := context.WithTimeout(ctx, timeout)
194 defer cancel()
195
196 var bodyReader io.Reader
197 if in.Body != "" {
198 bodyReader = strings.NewReader(in.Body)
199 }
200 req, err := http.NewRequestWithContext(reqCtx, in.Method, safeURL, bodyReader) //#nosec G704 -- URL validated by validateWebTarget + ssrfDialControl (metadata/link-local refused, redirects re-validated)
201 if err != nil {
202 return "", fmt.Errorf("@http: build request: %w", err)
203 }
204 req.Header.Set("User-Agent", fallbackUserAgent)
205 for k, v := range in.Headers {
206 // The proxy credential channel is operator-owned configuration; a tool
207 // argument must never override it.
208 if strings.EqualFold(k, "Proxy-Authorization") {
209 continue
210 }
211 req.Header.Set(k, v)
212 }
213
214 start := time.Now()
215 resp, err := webHTTPClient().Do(req) //#nosec G704 -- see request annotation above
216 if err != nil {
217 return "", fmt.Errorf("@http: %s %s failed after %s: %w", in.Method, in.URL, time.Since(start).Round(time.Millisecond), err)
218 }
219 defer func() { _ = resp.Body.Close() }()
220
221 raw, err := io.ReadAll(io.LimitReader(resp.Body, httpMaxResponseBody+1))
222 if err != nil {
223 return "", fmt.Errorf("@http: read response: %w", err)
224 }
225 elapsed := time.Since(start).Round(time.Millisecond)
226 return renderHTTPResponse(in, resp, raw, elapsed), nil
227}
228
229// renderHTTPResponse formats the model-facing response summary.
230func renderHTTPResponse(in httpArgs, resp *http.Response, raw []byte, elapsed time.Duration) string {

Callers 1

ExecuteWithStreamMethod · 0.95

Calls 7

validateWebTargetFunction · 0.85
webHTTPClientFunction · 0.85
renderHTTPResponseFunction · 0.85
ErrorfMethod · 0.80
DoMethod · 0.80
CloseMethod · 0.65
SetMethod · 0.45

Tested by

no test coverage detected