Files
2026-07-13 12:31:02 +08:00

136 lines
3.9 KiB
Go

package operations
import (
"fmt"
"io"
"net/http"
"strings"
"time"
"service-operation/types"
)
type HTTPOperation struct {
timeout time.Duration
client *http.Client
}
func NewHTTPOperation(timeout time.Duration) *HTTPOperation {
return &HTTPOperation{
timeout: timeout,
client: &http.Client{
Timeout: timeout,
},
}
}
func (h *HTTPOperation) Execute(url, method string) (*types.OperationResult, error) {
result := &types.OperationResult{
Type: types.OperationHTTP,
StartTime: time.Now(),
HTTPMethod: method,
}
// Default to GET if no method specified
if method == "" {
method = "GET"
result.HTTPMethod = "GET"
}
// Ensure URL has protocol
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
url = "https://" + url
}
start := time.Now()
req, err := http.NewRequest(method, url, nil)
if err != nil {
result.Error = fmt.Sprintf("Failed to create request: %v", err)
result.Success = false
result.EndTime = time.Now()
return result, nil
}
// Set a user agent
req.Header.Set("User-Agent", "ServiceOperation/1.0")
resp, err := h.client.Do(req)
result.ResponseTime = time.Since(start)
result.EndTime = time.Now()
if err != nil {
// More detailed error messages
if strings.Contains(err.Error(), "timeout") {
result.Error = fmt.Sprintf("🕐 Request timeout after %.2fs - Server did not respond within the expected time", h.timeout.Seconds())
} else if strings.Contains(err.Error(), "connection refused") {
result.Error = "🚫 Connection refused - Server is not accepting connections on this port"
} else if strings.Contains(err.Error(), "no such host") {
result.Error = "🌐 DNS resolution failed - Host not found"
} else if strings.Contains(err.Error(), "certificate") {
result.Error = "🔒 SSL/TLS certificate error - Certificate verification failed"
} else {
result.Error = fmt.Sprintf("🔌 Connection error: %v", err)
}
result.Success = false
return result, nil
}
defer resp.Body.Close()
result.HTTPStatusCode = resp.StatusCode
result.ContentLength = resp.ContentLength
result.Success = resp.StatusCode >= 200 && resp.StatusCode < 400
// Capture important headers
result.HTTPHeaders = make(map[string]string)
for key, values := range resp.Header {
if len(values) > 0 {
switch strings.ToLower(key) {
case "content-type", "server", "cache-control", "content-encoding", "x-powered-by":
result.HTTPHeaders[key] = values[0]
}
}
}
// Read response body for keyword checking and additional details
body, err := io.ReadAll(resp.Body)
if err == nil && len(body) > 0 {
result.ResponseBody = string(body)
// Update content length if not set by server
if result.ContentLength <= 0 {
result.ContentLength = int64(len(body))
}
}
// Create detailed status message with emoji
if !result.Success {
switch {
case resp.StatusCode >= 500:
result.Error = fmt.Sprintf("🔥 Server Error (HTTP %d): %s - The server encountered an internal error", resp.StatusCode, resp.Status)
case resp.StatusCode >= 400:
result.Error = fmt.Sprintf("❌ Client Error (HTTP %d): %s - The request was invalid or unauthorized", resp.StatusCode, resp.Status)
case resp.StatusCode >= 300:
result.Error = fmt.Sprintf("↩️ Redirect (HTTP %d): %s - Resource has moved", resp.StatusCode, resp.Status)
default:
result.Error = fmt.Sprintf("⚠️ Unexpected Status (HTTP %d): %s", resp.StatusCode, resp.Status)
}
} else {
// Success message with emoji
switch resp.StatusCode {
case 200:
result.Error = "✅ OK - Request successful"
case 201:
result.Error = "🆕 Created - Resource created successfully"
case 202:
result.Error = "⏳ Accepted - Request accepted for processing"
case 204:
result.Error = "📭 No Content - Request successful, no content returned"
default:
result.Error = fmt.Sprintf("✅ Success (HTTP %d): %s", resp.StatusCode, resp.Status)
}
}
return result, nil
}