|
1 | 1 | package httpclient |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
4 | 5 | "context" |
5 | 6 | "encoding/base64" |
6 | 7 | "fmt" |
7 | 8 | "io" |
8 | 9 | "mime/multipart" |
9 | 10 | "net/http" |
10 | 11 | "net/textproto" |
| 12 | + "net/url" |
11 | 13 | "os" |
12 | 14 | "path/filepath" |
13 | 15 | "strings" |
@@ -432,109 +434,102 @@ func (c *Client) DoImageMultiPartUpload(method, endpoint string, fileName string |
432 | 434 | zap.String("method", method), |
433 | 435 | zap.String("endpoint", endpoint), |
434 | 436 | zap.String("fileName", fileName), |
435 | | - zap.String("originalBoundary", customBoundary)) |
| 437 | + zap.String("boundary", customBoundary)) |
436 | 438 |
|
437 | | - // Remove any leading hyphens from the boundary when setting in header |
438 | | - cleanBoundary := strings.TrimPrefix(customBoundary, "-----") |
439 | | - c.Sugar.Debugw("Processed boundary", |
440 | | - zap.String("cleanBoundary", cleanBoundary)) |
| 439 | + // URL encode the filename for both the Content-Disposition and data prefix |
| 440 | + encodedFileName := url.QueryEscape(fileName) |
| 441 | + c.Sugar.Debugw("URL encoded filename", zap.String("encodedFileName", encodedFileName)) |
441 | 442 |
|
442 | | - // Format the multipart payload with the specified boundary |
| 443 | + // Construct payload exactly like the example |
443 | 444 | payload := fmt.Sprintf("%s\r\n"+ |
444 | 445 | "Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n"+ |
445 | 446 | "Content-Type: image/png\r\n\r\n"+ |
446 | 447 | "data:image/png;name=%s;base64,%s\r\n"+ |
447 | | - "%s--", |
| 448 | + "%s-", |
448 | 449 | customBoundary, |
449 | | - fileName, |
450 | | - fileName, |
| 450 | + encodedFileName, |
| 451 | + encodedFileName, |
451 | 452 | base64Data, |
452 | 453 | customBoundary) |
453 | 454 |
|
454 | | - // Log the payload structure (not the full base64 data) |
455 | | - payloadPreview := fmt.Sprintf("%s\r\n"+ |
456 | | - "Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n"+ |
457 | | - "Content-Type: image/png\r\n\r\n"+ |
458 | | - "data:image/png;name=%s;base64,[BASE64_DATA_LENGTH: %d]\r\n"+ |
459 | | - "%s--", |
460 | | - customBoundary, |
461 | | - fileName, |
462 | | - fileName, |
463 | | - len(base64Data), |
464 | | - customBoundary) |
| 455 | + // Create truncated version of payload for logging |
| 456 | + truncatedPayload := payload |
| 457 | + if len(base64Data) > 100 { |
| 458 | + // Find the position of base64 data in the payload |
| 459 | + base64Start := strings.Index(payload, ";base64,") + 8 |
| 460 | + if base64Start > 0 { |
| 461 | + truncatedPayload = payload[:base64Start] + "[BASE64_DATA_LENGTH: " + |
| 462 | + fmt.Sprintf("%d", len(base64Data)) + "]\r\n" + |
| 463 | + customBoundary + "-" |
| 464 | + } |
| 465 | + } |
465 | 466 |
|
466 | | - c.Sugar.Debugw("Constructed payload", |
467 | | - zap.String("payloadStructure", payloadPreview), |
468 | | - zap.Int("totalPayloadLength", len(payload))) |
| 467 | + c.Sugar.Debugw("Constructed request payload", |
| 468 | + zap.String("payload", truncatedPayload)) |
469 | 469 |
|
470 | 470 | url := (*c.Integration).GetFQDN() + endpoint |
471 | | - c.Sugar.Debugw("Constructed URL", zap.String("fullURL", url)) |
| 471 | + c.Sugar.Debugw("Full request URL", zap.String("url", url)) |
472 | 472 |
|
473 | | - // Create the request with the formatted payload |
| 473 | + // Create request with string payload |
474 | 474 | req, err := http.NewRequest(method, url, strings.NewReader(payload)) |
475 | 475 | if err != nil { |
476 | | - c.Sugar.Errorw("Failed to create request", |
477 | | - zap.Error(err), |
478 | | - zap.String("method", method), |
479 | | - zap.String("url", url)) |
| 476 | + c.Sugar.Errorw("Failed to create request", zap.Error(err)) |
480 | 477 | return nil, fmt.Errorf("failed to create request: %v", err) |
481 | 478 | } |
482 | 479 |
|
483 | | - // Clear existing headers and set only what we need |
484 | | - req.Header = http.Header{} |
485 | | - contentTypeHeader := fmt.Sprintf("multipart/form-data; boundary=---%s", cleanBoundary) |
486 | | - req.Header.Set("Accept", "application/json") |
487 | | - req.Header.Set("Content-Type", contentTypeHeader) |
| 480 | + // Set headers exactly as in example |
| 481 | + req.Header.Set("accept", "application/json") |
| 482 | + req.Header.Set("content-type", fmt.Sprintf("multipart/form-data; boundary=%s", strings.TrimPrefix(customBoundary, "---"))) |
488 | 483 |
|
489 | | - c.Sugar.Debugw("Request headers before auth", |
| 484 | + c.Sugar.Debugw("Initial headers", |
490 | 485 | zap.Any("headers", req.Header), |
491 | | - zap.String("contentType", contentTypeHeader)) |
| 486 | + zap.String("accept", req.Header.Get("accept")), |
| 487 | + zap.String("content-type", req.Header.Get("content-type"))) |
492 | 488 |
|
493 | | - preservedAccept := req.Header.Get("Accept") |
494 | | - preservedContentType := req.Header.Get("Content-Type") |
| 489 | + // Store initial headers |
| 490 | + contentType := req.Header.Get("content-type") |
| 491 | + accept := req.Header.Get("accept") |
495 | 492 |
|
| 493 | + // Apply auth |
496 | 494 | (*c.Integration).PrepRequestParamsAndAuth(req) |
497 | 495 |
|
498 | | - // Restore our specific headers |
499 | | - req.Header.Set("Accept", preservedAccept) |
500 | | - req.Header.Set("Content-Type", preservedContentType) |
501 | | - |
502 | | - c.Sugar.Debugw("Request headers after auth and restoration", |
503 | | - zap.Any("headers", req.Header)) |
| 496 | + // Restore and log final headers |
| 497 | + req.Header.Set("accept", accept) |
| 498 | + req.Header.Set("content-type", contentType) |
504 | 499 |
|
505 | | - c.Sugar.Infow("Sending custom multipart request", |
506 | | - zap.String("method", method), |
507 | | - zap.String("url", url), |
508 | | - zap.String("filename", fileName), |
509 | | - zap.String("contentType", req.Header.Get("Content-Type")), |
510 | | - zap.String("accept", req.Header.Get("Accept"))) |
| 500 | + c.Sugar.Infow("Final request headers", |
| 501 | + zap.Any("headers", req.Header), |
| 502 | + zap.String("accept", req.Header.Get("accept")), |
| 503 | + zap.String("content-type", req.Header.Get("content-type"))) |
511 | 504 |
|
512 | | - startTime := time.Now() |
| 505 | + // Send the request |
513 | 506 | resp, err := c.http.Do(req) |
514 | | - duration := time.Since(startTime) |
515 | | - |
516 | 507 | if err != nil { |
517 | | - c.Sugar.Errorw("Failed to send request", |
518 | | - zap.String("method", method), |
519 | | - zap.String("endpoint", endpoint), |
520 | | - zap.Error(err), |
521 | | - zap.Duration("requestDuration", duration)) |
522 | 508 | return nil, fmt.Errorf("failed to send request: %v", err) |
523 | 509 | } |
524 | 510 |
|
525 | | - c.Sugar.Debugw("Request sent successfully", |
526 | | - zap.String("method", method), |
527 | | - zap.String("endpoint", endpoint), |
528 | | - zap.Int("status_code", resp.StatusCode), |
529 | | - zap.Duration("duration", duration)) |
530 | | - |
531 | | - c.Sugar.Debugw("Response headers", |
532 | | - zap.Any("headers", resp.Header)) |
| 511 | + c.Sugar.Debugw("Response received", |
| 512 | + zap.Int("statusCode", resp.StatusCode), |
| 513 | + zap.Any("responseHeaders", resp.Header)) |
533 | 514 |
|
| 515 | + // Handle response |
534 | 516 | if resp.StatusCode >= 200 && resp.StatusCode < 300 { |
535 | | - c.Sugar.Info("Request succeeded, processing response") |
536 | 517 | return resp, response.HandleAPISuccessResponse(resp, out, c.Sugar) |
537 | 518 | } |
538 | 519 |
|
| 520 | + // For error responses, try to log the response body |
| 521 | + if resp.Body != nil { |
| 522 | + bodyBytes, err := io.ReadAll(resp.Body) |
| 523 | + if err != nil { |
| 524 | + c.Sugar.Warnw("Failed to read error response body", zap.Error(err)) |
| 525 | + } else { |
| 526 | + c.Sugar.Errorw("Request failed", |
| 527 | + zap.Int("statusCode", resp.StatusCode), |
| 528 | + zap.String("responseBody", string(bodyBytes))) |
| 529 | + // Create new reader with same data for error handler |
| 530 | + resp.Body = io.NopCloser(bytes.NewReader(bodyBytes)) |
| 531 | + } |
| 532 | + } |
| 533 | + |
539 | 534 | return resp, response.HandleAPIErrorResponse(resp, c.Sugar) |
540 | 535 | } |
0 commit comments