# 73-network-http-rpc-client
Status: ACTIVE
AppliesTo: v10
Type: Policy / Requirements
---
## Purpose
Devian 런타임 모듈(단일 모듈)의 Network 기능에 **binary→base64 단일 파라미터 POST RPC 클라이언트**를 추가한다.
이 SKILL은 정책/요구사항/설계 의도를 정의한다. 구현 및 공개 API는 코드가 정답이며, 예시는 참고용이다.
> **Namespace 정책:** 모든 타입은 `namespace Devian` 단일을 사용한다.
> 네트워크 계열 public API는 `Net` 접두사로 명확화한다.
---
## Scope
### 목표 (In Scope)
- `NetHttpRpcClient`: sync API HTTP RPC 클라이언트
- base64 single-param POST RPC 규약
- `application/x-www-form-urlencoded` 전송
### 비목표 (Out of Scope)
- REST 규격 지원 X
- TypeScript 변경 X
- WebSocket과의 통합/공유 큐는 다음 단계
- JSON 응답 파싱 (기본 미지원)
---
## Module Structure
> Devian C# 런타임은 단일 모듈(`Devian.csproj`)로 통합되어 있다.
> **모든 타입은 `namespace Devian`에 위치한다.** (분리된 하위 네임스페이스 금지)
```
framework-cs/module/Devian/
└── src/
└── Net/
└── Transports/
└── NetHttpRpcClient.cs
```
---
## RPC Wire Protocol
### Request
| 항목 | 값 |
|------|-----|
| Method | `POST` |
| Content-Type | `application/x-www-form-urlencoded; charset=utf-8` |
| Body | `{paramName}={urlEncoded(base64(requestBinary))}` |
| Default paramName | `"p"` |
**Flow:**
```
requestBinary (byte[])
↓ Convert.ToBase64String()
base64 string
↓ Uri.EscapeDataString()
urlEncoded string
↓ "{paramName}={value}"
form-urlencoded body
```
### Response
| 항목 | 값 |
|------|-----|
| Expected | Plain text base64 (no JSON wrapper) |
| Decoding | `Convert.FromBase64String(text.Trim())` |
**Flow:**
```
HTTP Response Body (text)
↓ Trim()
trimmed base64 string
↓ Convert.FromBase64String()
responseBinary (byte[])
```
> **Note:** JSON 응답 `{"p":"..."}` 형태는 기본 미지원. 필요시 옵션 확장 가능.
---
## API Signatures (Reference)
> **Note:** 아래는 이해를 돕기 위한 참고 예시이며, 최종 시그니처/공개 API는 코드가 정답이다.
> 코드 변경 시 이 문서를 'SSOT'로 맞추지 않는다. 필요하면 문서를 참고 수준으로 갱신한다.
```csharp
namespace Devian
{
///
/// HTTP RPC client with binary→base64 single-param POST protocol.
/// All public APIs are synchronous.
///
public sealed class NetHttpRpcClient : IDisposable
{
///
/// Creates a new NetHttpRpcClient.
///
/// RPC endpoint URI.
/// Form parameter name (default: "p").
/// Optional HttpClient (if null, creates internal one).
public NetHttpRpcClient(Uri endpoint, string paramName = "p", HttpClient? http = null);
///
/// Synchronously call the RPC endpoint.
/// Throws on HTTP error or decode failure.
///
/// Request payload.
/// Response payload (decoded from base64).
public byte[] Call(ReadOnlySpan requestBinary);
///
/// Try to call the RPC endpoint without throwing.
///
/// Request payload.
/// Response payload if successful.
/// True if successful, false otherwise.
public bool TryCall(ReadOnlySpan requestBinary, out byte[] responseBinary);
///
/// Dispose the client (releases HttpClient if owned).
///
public void Dispose();
}
}
```
---
## Hard Rules
### MUST
1. **Public API는 sync만 제공**: `Call()`, `TryCall()`, `Dispose()`
2. **async 노출 금지**: `Task`, `ValueTask`, `async` 키워드 금지 (public API)
3. **내부에서만 async 사용**: `SendAsync(...).GetAwaiter().GetResult()` 패턴
4. **HttpClient 주입 지원**: 외부 주입 시 Dispose하지 않음
### MUST NOT
1. public API에 `async` 도입
2. REST 규격이나 JSON 파싱 기본 구현
3. WebSocket 코드와 결합
---
## Error Policy
| 메서드 | 실패 시 |
|--------|---------|
| `Call()` | 예외 throw (`HttpRequestException`, `FormatException` 등) |
| `TryCall()` | `false` 반환, `responseBinary = Array.Empty()` |
---
## Usage Example
```csharp
using Devian;
// Create client
using var rpc = new NetHttpRpcClient(new Uri("https://api.example.com/rpc"));
// Prepare request
byte[] request = /* serialized protobuf or other binary */;
// Call (throws on error)
byte[] response = rpc.Call(request);
// Or try-call (no throw)
if (rpc.TryCall(request, out var resp))
{
// handle resp
}
```
---
## Build Target
- `netstandard2.1` 유지
- 추가 NuGet 패키지 불필요 (`System.Net.Http`는 기본 포함)
---
## Reference
- Parent Module: `Devian` (단일 런타임 모듈, `namespace Devian`)
- Related: `skills/devian-core/72-network-ws-client/SKILL.md`