test: Add HTTP POST and headers E2E test

- Add e2e_http_post_and_headers: Full POST request with headers test
- Verify client POST with body data ('DATA')
- Server reads request body and responds with custom status (201)
- Custom headers (X-Test: V) properly set and retrieved
- Complete request/response cycle validation: '201:V:R' 
- All 4 HTTP plugin tests passing

HTTP POSTとヘッダー操作のE2Eテスト追加
- POSTリクエストのボディ送受信確認
- カスタムステータスコード(201 Created)
- HTTPヘッダーの設定と取得
- 完全なHTTPプロトコル機能の検証

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-08-21 23:11:31 +09:00
parent 26c3d153e2
commit 9c1389edc5
3 changed files with 81 additions and 1 deletions

View File

@ -39,9 +39,12 @@ const M_RESP_SET_STATUS: u32 = 1; // arg: i32
const M_RESP_SET_HEADER: u32 = 2; // args: name, value (string)
const M_RESP_WRITE: u32 = 3; // arg: bytes/string
const M_RESP_READ_BODY: u32 = 4; // -> Bytes
const M_RESP_GET_STATUS: u32 = 5; // -> i32
const M_RESP_GET_HEADER: u32 = 6; // arg: name -> string (or empty)
// Client
const M_CLIENT_GET: u32 = 1; // arg: url -> Handle(Response)
const M_CLIENT_GET: u32 = 1; // arg: url -> Handle(Response)
const M_CLIENT_POST: u32 = 2; // args: url, body(bytes/string) -> Handle(Response)
// Global State
static SERVER_INSTANCES: Lazy<Mutex<HashMap<u32, ServerState>>> = Lazy::new(|| Mutex::new(HashMap::new()));
@ -216,6 +219,18 @@ unsafe fn response_invoke(m: u32, id: u32, args: *const u8, args_len: usize, res
M_RESP_READ_BODY => {
if let Some(rp) = RESPONSES.lock().unwrap().get(&id) { write_tlv_bytes(&rp.body, res, res_len) } else { E_INV_HANDLE }
}
M_RESP_GET_STATUS => {
if let Some(rp) = RESPONSES.lock().unwrap().get(&id) { write_tlv_i32(rp.status, res, res_len) } else { E_INV_HANDLE }
}
M_RESP_GET_HEADER => {
if let Ok(name) = tlv_parse_string(slice(args, args_len)) {
if let Some(rp) = RESPONSES.lock().unwrap().get(&id) {
let v = rp.headers.get(&name).cloned().unwrap_or_default();
return write_tlv_string(&v, res, res_len);
} else { return E_INV_HANDLE; }
}
E_INV_ARGS
}
_ => E_INV_METHOD,
}
}
@ -248,6 +263,33 @@ unsafe fn client_invoke(m: u32, id: u32, args: *const u8, args_len: usize, res:
// Return Handle(Response)
write_tlv_handle(T_RESPONSE, resp_id, res, res_len)
}
M_CLIENT_POST => {
// args: TLV String(url), Bytes body
let data = slice(args, args_len);
let (_, argc, mut pos) = tlv_parse_header(data).map_err(|_| ()).or(Err(())).unwrap_or((1,0,4));
if argc < 2 { return E_INV_ARGS; }
let (_t1, s1, p1) = tlv_parse_entry_hdr(data, pos).map_err(|_| ()).or(Err(())).unwrap_or((0,0,0));
if data[pos] != 6 { return E_INV_ARGS; }
let url = std::str::from_utf8(&data[p1..p1+s1]).map_err(|_| ()).or(Err(())) .unwrap_or("").to_string();
pos = p1 + s1;
let (t2, s2, p2) = tlv_parse_entry_hdr(data, pos).map_err(|_| ()).or(Err(())).unwrap_or((0,0,0));
if t2 != 6 && t2 != 7 { return E_INV_ARGS; }
let body = data[p2..p2+s2].to_vec();
let path = parse_path(&url);
// Create Request
let req_id = REQUEST_ID.fetch_add(1, Ordering::Relaxed);
REQUESTS.lock().unwrap().insert(req_id, RequestState { path, body, response_id: None });
// Enqueue to active server if running
if let Some(sid) = *ACTIVE_SERVER_ID.lock().unwrap() {
if let Some(s) = SERVER_INSTANCES.lock().unwrap().get_mut(&sid) { if s.running { s.pending.push_back(req_id); } }
}
// Create paired client Response
let resp_id = RESPONSE_ID.fetch_add(1, Ordering::Relaxed);
RESPONSES.lock().unwrap().insert(resp_id, ResponseState { status: 200, headers: HashMap::new(), body: vec![] });
if let Some(rq) = REQUESTS.lock().unwrap().get_mut(&req_id) { rq.response_id = Some(resp_id); }
write_tlv_handle(T_RESPONSE, resp_id, res, res_len)
}
_ => E_INV_METHOD,
}
}