executeHTTPHook sends the event as a POST request.
(ctx context.Context, hook HookConfig, event HookEvent, timeout time.Duration)
| 235 | |
| 236 | // executeHTTPHook sends the event as a POST request. |
| 237 | func (m *Manager) executeHTTPHook(ctx context.Context, hook HookConfig, event HookEvent, timeout time.Duration) *HookResult { |
| 238 | eventJSON, _ := json.Marshal(event) |
| 239 | |
| 240 | ctx, cancel := context.WithTimeout(ctx, timeout) |
| 241 | defer cancel() |
| 242 | |
| 243 | req, err := http.NewRequestWithContext(ctx, "POST", hook.URL, bytes.NewReader(eventJSON)) |
| 244 | if err != nil { |
| 245 | return &HookResult{ExitCode: -1, Error: fmt.Sprintf("creating request: %v", err)} |
| 246 | } |
| 247 | req.Header.Set("Content-Type", "application/json") |
| 248 | req.Header.Set("User-Agent", "chatcli-hooks/1.0") |
| 249 | |
| 250 | resp, err := http.DefaultClient.Do(req) |
| 251 | if err != nil { |
| 252 | m.logger.Debug("HTTP hook failed", zap.String("name", hook.Name), zap.Error(err)) |
| 253 | return &HookResult{ExitCode: -1, Error: err.Error()} |
| 254 | } |
| 255 | defer resp.Body.Close() |
| 256 | |
| 257 | result := &HookResult{ExitCode: 0} |
| 258 | |
| 259 | // HTTP 403 = block (similar to exit code 2) |
| 260 | if resp.StatusCode == http.StatusForbidden { |
| 261 | result.ExitCode = 2 |
| 262 | result.Blocked = true |
| 263 | result.BlockReason = "blocked by HTTP hook: " + hook.Name |
| 264 | } else if resp.StatusCode >= 400 { |
| 265 | result.ExitCode = 1 |
| 266 | result.Error = fmt.Sprintf("HTTP %d", resp.StatusCode) |
| 267 | } |
| 268 | |
| 269 | return result |
| 270 | } |
| 271 | |
| 272 | // matchToolPattern checks if a tool name matches a glob-like pattern. |
| 273 | // Supports * as wildcard (e.g., "mcp_*", "@coder", "*"). |
no test coverage detected