콘텐츠로 이동

Spec -- ZLink HTTP Client For .NET

사용법 중심 문서는 사용자 가이드를 본다. 언어 중립 공통 계약은 공통 spec이 정본이며, 이 문서는 공통 계약에 대한 .NET 고유 편차와 구현 매핑만 기술한다. 공개 계약의 기준은 공통 spec과 이 언어별 spec이다. src/Zlink.HttpClient/**의 공개 타입과 Zlink.HttpClient.UnitTests 회귀 테스트는 구현이 그 계약을 지키는지 검증한다.

1. 목적

Zlink.HttpClient는 .NET에서 HTTP request를 보내기 위한 별도 client-side 산출물이다. JSON 전용 client가 아니라 일반 HTTP client이며 zlink fluent builder 스타일로 System.Net.Http의 낮은 수준 설정을 흡수한다. typed 경로 (Body(dto)/Async<T>()/Fetch<T>())는 그 위에 얹은 편의 계층이다.

이 client는 Zlink.Framework.Contracts의 오류·codec 계약을 참조하며 Zlink.Framework server runtime assembly를 참조하지 않는다. 이는 .NET의 package 분할이며, 공통 의존 방향은 01 범위와 아키텍처 §1.3가 소유한다.

HTTP codec registry는 serializer 등록만 처리한다. 등록할 contentType에는 parameter가 없는 ASCII type/subtype을 사용한다. Registry는 앞뒤 SP와 TAB을 제거하고 ASCII 대문자를 소문자로 바꾼 canonical media type을 key로 사용한다.

HTTP response의 Content-Type은 registration 입력과 경계가 다르다. HTTP 규칙에 따라 charset 같은 parameter를 먼저 분리한 뒤, parameter가 없는 media type을 소문자로 바꾸어 canonical key를 찾는다. 따라서 application/json; charset=utf-8처럼 정상적인 parameter가 있는 response도 application/json serializer를 사용한다.

같은 extension이 IZlinkStreamCodecRegistration도 구현하더라도 STREAM descriptor는 무시한다. HTTP client package는 Stream Connector runtime이나 compression package에 의존하지 않는다.

2. 산출물 경계

역할 위치 공개 여부
공개 contract src/Zlink.HttpClient/*.cs, Contracts/* public
공유 오류·codec contract src/Zlink.Framework.Contracts/Codecs, Errors public dependency
runtime 구현 src/Zlink.HttpClient/Runtime/* internal
회귀 테스트 tests/Zlink.HttpClient.UnitTests/* private
프로젝트 Zlink.HttpClient public package

공개 표면에는 SocketsHttpHandler, HttpClientHandler, HttpRequestMessage, HttpResponseMessage 같은 System.Net.Http 타입을 노출하지 않는다.

3. 공개 타입

  • ZLinkHttpClient — 정적 팩토리에서 만드는 standalone client. Get/Post/Put/Delete/ Patch/Head/Options, IDisposable을 제공한다.
  • ZLinkHttpServerClient — framework 서버가 DI로 제공하는 client. 각 verb는 ZLinkHttpServerRequestBuilder를 반환한다.
  • ZLinkHttpClientBuilderBaseUrl, Codecs, Timeout, DefaultHeader, BasicAuth, BearerToken, MaxResponseBodySize, TrustCertificateFile, ClientCertificateFile, FollowRedirects, Retry, Cookies, Proxy, ProxyBasicAuth, Compression, Build, BuildServer, 그리고 단발 verb shortcut. (Codecs는 framework codec extension 등록 — .NET 고유 확장점, 공통 spec 2.3장 언어 편차)
  • ZLinkHttpRequestBuilder — standalone 표면. Header, Query, Timeout, Body<T>, Body(content, contentType), BodyStream, Form, Multipart, MultipartFile, AsyncRaw, DownloadAsync, Async<T>, decoded body를 직접 반환하는 Fetch<T>와 callback overload를 제공한다.
  • ZLinkHttpServerRequestBuilder — standalone 표면을 포함하고 one-way ValueTask Async(CancellationToken cancellationToken = default)를 추가한다. 반환된 ValueTask는 비동기 완료와 실패만 전달하며 전송 결과나 admission status를 포함하지 않는다. Shared Spot gate를 반납하고 새 turn에서 이어받는 ValueTask<HttpResponse<T>> Yield<T>(CancellationToken cancellationToken = default)도 추가한다. gate 반납이 허용되는 SpotWide User Spot과 Instance Spot에서만 사용한다.
  • IZLinkHttpExecutionScheduler / IZLinkHttpExecutionTurn — DI 통합이 현재 Spot turn을 캡처하고 callback 완료를 원래 실행 줄의 새 turn에 배치하는 공개 주입점이다.
  • RawHttpResponse { Status, Headers, Body }.
  • HttpResponse<T> { Status, Headers, Body, RawBody }.
  • ZLinkHttpMethod enum.

callback overload가 받는 delegate도 공개 계약에 포함한다. 완료 시 errorresponse 중 정확히 하나만 null이 아니다.

public delegate void ZLinkHttpCallback<T>(
    Exception? error,
    HttpResponse<T>? response);

4. 실행 모델

  • Async<T>ValueTask<HttpResponse<T>>를 반환하며 Spot turn을 유지한다.
  • Fetch<T>ValueTask<T>를 반환한다. status와 header가 필요 없는 application sample은 response를 받은 뒤 .Body를 꺼내지 않고 이 terminal을 사용한다.

public ValueTask<T> Fetch<T>(
    CancellationToken cancellationToken = default);
- HTTP request builder에는 Yield<T>를 제공하지 않는다. Shared Spot gate를 반납하려면 RunIoWorker(...) 안에서 Async<T>를 호출하고 Worker call의 Yield로 기다린다. - callback overload는 awaitable을 반환하지 않는다. 완료 callback은 요청을 만든 Spot turn의 실행 줄에 새 turn으로 배치한다. standalone client에서는 비동기 완료 문맥에서 직접 호출한다. - 완료 값을 동기로 꺼내는 blocking terminator는 제공하지 않는다.

5. 전송 의미론

기본값·redirect·retry·cookie·압축·인증 스크럽·body 소스 배타 의미론은 공통 spec 2~8장을 따른다. .NET 구현 매핑:

  • 네이티브 자동 기능 비활성: SocketsHttpHandler에서 AllowAutoRedirect=false, AutomaticDecompression=None, UseCookies=false — 의미론은 래퍼가 구현.
  • per-attempt timeout은 HttpClient.Timeout 대신 linked CancellationTokenSource.CancelAfter로 강제(호출자 취소와 timeout을 구분).
  • TLS: SslClientAuthenticationOptions(trust 추가 + mTLS). proxy: WebProxy.
  • 압축 해제: 래퍼가 System.IO.Compression으로 수행.

6. 에러 매핑

공통 spec 9장을 따른다. .NET은 ZLinkFrameworkErrorKind를 사용하며 public exception은 재시도 여부를 제공하지 않는다.

  • timeout은 DeadlineExceeded와 inner TimeoutException으로 보고한다. 호출자 취소는 OperationCanceledException 그대로 전파된다.

7. 회귀 테스트 축

Zlink.HttpClient.UnitTestsHttpClientContractTests가 전송 계약 시나리오를 검증한다. chunked 업로드는 managed Linux HttpListener가 chunked 요청 본문을 못 받으므로 raw-socket 서버(RawCaptureServer)로 검증한다.