Files
sam-manage/app/Services/FlowTester/HttpClient.php
hskwon 793f4a8319 fix: FlowTester 스텝 헤더가 기본 Bearer 토큰보다 우선하도록 수정
- 문제: .env의 FLOW_TESTER_API_TOKEN이 스텝에서 설정한 Authorization 헤더를 덮어씀
- 원인: withToken()이 항상 호출되어 스텝 헤더를 무시함
- 해결: 스텝 헤더에 Authorization이 있으면 기본 Bearer 토큰 사용 안함
2025-12-03 20:14:03 +09:00

261 lines
6.5 KiB
PHP

<?php
namespace App\Services\FlowTester;
use Exception;
use Illuminate\Support\Facades\Http;
/**
* HTTP 클라이언트 래퍼
*
* Laravel HTTP Client를 래핑하여 플로우 테스터에 맞게 제공합니다.
*/
class HttpClient
{
private string $baseUrl = '';
private int $timeout = 30;
private array $defaultHeaders = [];
private ?string $apiKey = null;
private ?string $bearerToken = null;
private bool $verifySsl = true;
private ?string $hostHeader = null;
/**
* SSL 검증 비활성화 (Docker 내부 통신용)
*/
public function withoutVerifying(): self
{
$this->verifySsl = false;
return $this;
}
/**
* Host 헤더 설정 (Docker 내부 통신용)
*/
public function setHostHeader(string $host): self
{
$this->hostHeader = $host;
return $this;
}
/**
* 기본 URL 설정
*/
public function setBaseUrl(string $url): self
{
$this->baseUrl = rtrim($url, '/');
return $this;
}
/**
* 타임아웃 설정 (초)
*/
public function setTimeout(int $seconds): self
{
$this->timeout = $seconds;
return $this;
}
/**
* 기본 헤더 설정
*/
public function setDefaultHeaders(array $headers): self
{
$this->defaultHeaders = $headers;
return $this;
}
/**
* API 키 설정 (X-API-KEY 헤더)
*/
public function setApiKey(string $apiKey): self
{
$this->apiKey = $apiKey;
return $this;
}
/**
* Bearer 토큰 설정
*/
public function setBearerToken(string $token): self
{
$this->bearerToken = $token;
return $this;
}
/**
* HTTP 요청 실행
*
* @param string $method HTTP 메서드
* @param string $endpoint 엔드포인트
* @param array $options 옵션 ['headers' => [], 'body' => [], 'query' => []]
* @return array ['status' => int, 'headers' => array, 'body' => array, 'duration' => int]
*/
public function request(string $method, string $endpoint, array $options = []): array
{
$startTime = microtime(true);
$url = $this->buildUrl($endpoint);
$headers = $this->buildHeaders($options['headers'] ?? []);
try {
$request = Http::timeout($this->timeout)
->withHeaders($headers);
// SSL 검증 비활성화 (Docker 내부 통신용)
if (! $this->verifySsl) {
$request = $request->withoutVerifying();
}
// Host 헤더 추가 (Docker 내부 통신용)
if ($this->hostHeader) {
$request = $request->withHeaders(['Host' => $this->hostHeader]);
}
// API 키 추가
if ($this->apiKey) {
$request = $request->withHeaders(['X-API-KEY' => $this->apiKey]);
}
// Bearer 토큰 추가 (스텝 헤더에 Authorization이 없는 경우에만)
if ($this->bearerToken && ! isset($headers['Authorization'])) {
$request = $request->withToken($this->bearerToken);
}
// 쿼리 파라미터
if (! empty($options['query'])) {
$url .= '?'.http_build_query($options['query']);
}
// 요청 실행
$response = match (strtoupper($method)) {
'GET' => $request->get($url),
'POST' => $request->post($url, $options['body'] ?? []),
'PUT' => $request->put($url, $options['body'] ?? []),
'PATCH' => $request->patch($url, $options['body'] ?? []),
'DELETE' => $request->delete($url, $options['body'] ?? []),
default => throw new Exception("Unsupported HTTP method: {$method}"),
};
$duration = (int) ((microtime(true) - $startTime) * 1000);
return [
'status' => $response->status(),
'headers' => $response->headers(),
'body' => $response->json() ?? [],
'duration' => $duration,
'success' => $response->successful(),
'error' => null,
];
} catch (Exception $e) {
$duration = (int) ((microtime(true) - $startTime) * 1000);
return [
'status' => 0,
'headers' => [],
'body' => [],
'duration' => $duration,
'success' => false,
'error' => $e->getMessage(),
];
}
}
/**
* GET 요청
*/
public function get(string $endpoint, array $query = [], array $headers = []): array
{
return $this->request('GET', $endpoint, [
'query' => $query,
'headers' => $headers,
]);
}
/**
* POST 요청
*/
public function post(string $endpoint, array $body = [], array $headers = []): array
{
return $this->request('POST', $endpoint, [
'body' => $body,
'headers' => $headers,
]);
}
/**
* PUT 요청
*/
public function put(string $endpoint, array $body = [], array $headers = []): array
{
return $this->request('PUT', $endpoint, [
'body' => $body,
'headers' => $headers,
]);
}
/**
* PATCH 요청
*/
public function patch(string $endpoint, array $body = [], array $headers = []): array
{
return $this->request('PATCH', $endpoint, [
'body' => $body,
'headers' => $headers,
]);
}
/**
* DELETE 요청
*/
public function delete(string $endpoint, array $body = [], array $headers = []): array
{
return $this->request('DELETE', $endpoint, [
'body' => $body,
'headers' => $headers,
]);
}
/**
* 전체 URL 구성
*/
private function buildUrl(string $endpoint): string
{
$endpoint = ltrim($endpoint, '/');
if (empty($this->baseUrl)) {
return $endpoint;
}
return $this->baseUrl.'/'.$endpoint;
}
/**
* 헤더 병합
*/
private function buildHeaders(array $additionalHeaders): array
{
return array_merge(
[
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
$this->defaultHeaders,
$additionalHeaders
);
}
}