NetMessage API¶
Oasis 네트워크 메세지는 NetMessage API 통하여 처리됩니다.
NetMessage는 네트워크 컴포넌트 인터페이스의 매개변수로 전달되고, 응답이나 요청을 위해 사용됩니다.
헤더 파일¶
OasisNet.h
함수¶
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- true: Query 집합에 키가 존재합니다.
- false: Query 집합에 키가 존재하지 않습니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- true: 요청 메세지입니다.
- false: 응답 메세지입니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- true: chunked 메세지입니다.
- false: 단일 메세지입니다.
- true: 준비된 상태입니다.
- false: 아직 다운받는 중입니다.
- 0: 성공
- -1: 실패
- true: 연결된 상태입니다.
- false: 연결되지 않은 상태입니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
아래는 웹 서버에서 클라이언트로 부터 파일 전송 요청을 받았을 때 처리하는 예입니다. 오류 상황은 고려하지 않았습니다.
void MyHttpUpStreamDelegate::onRequest(const NetMessageRef &msg)
{
std::string url;
netMessageUriGetPath(msg, url);
NetMessageRef res = netMessageCreate(msg, false);
std::string path = http_root_dir_path + url;
if(access(path.c_str(), F_OK) == 0) {
//get mime type based on extension
const char *mime_type = get_file_mime_type(path.c_str());
if(mime_type == nullptr) {
//not supported
netMessageSetStatusCodeAndReasonString(res, 403, "Forbidden");
netMessagePost(res);
} else {
//파일을 전송합니다.
netMessageSendFile(res, path.c_str(), mime_type);
}
} else {
netMessageSetStatusCodeAndReasonString(res, 400, "Bad Request");
netMessagePost(res);
}
}
- 0: 성공
- -1: 실패
예제¶
Oasis의 각 네트워크 컴포넌트 문서에 메세지 API를 이용한 예제들이 있습니다.
아래는 웹 클라이언트가 웹 서버로 부터 대용량 파일을 전송 받는 예, 웹 서버에 폼 데이터를 전송하는 예, 웹 서버에서 웹 클라이언트가 전송한 폼 데이터 파일을 저장하는 예 입니다.
웹 서버로 부터 대용량 파일 받기¶
웹 서버에 연결합니다.
const int32_t timeout = 60000;
HttpConnectionRef conn = httpOpenConnection(
"https://example.com/data/firmware.img",
timeout,
"/tmp" /* 캐쉬 디렉토리 */
);
연결 객체로 부터 요청 메세지를 얻어와서 Method를 GET으로 설정 후 서버에 요청 메세지를 전송합니다.
NetMessageRef msg = httpConnectionGetRequestMessage(conn);
netMessageSetMethod(msg, "GET");
netMessagePost(msg);
서버로 부터 응답 헤더를 수신합니다. 일정 시간내에 응답이 없으면 실패한 것으로 간주합니다.
NetMessageRef res_msg;
wait_count = 0;
do {
res_msg = httpConnectionGetResponseHeaderMessage(conn, 1000);
wait_count++;
} while(res_msg == nullptr && continuing_ && wait_count*1000 < timeout);
if(res_msg == nullptr || !continuing_) {
return -1;
}
응답 코드를 확인합니다. 200이 아닌 경우, 오류 응답으로 간주합니다.
std::string reason_string;
int32_t status_code = netMessageGetStatusCodeAndReasonString(res_msg, reason_string);
if(status_code != 200) {
return -1;
}
응답 컨텐츠 길이를 확인합니다. 파일 길이가 유효하지 않으면 오류로 간주합니다.
ssize_t content_length = netMessageGetContentLength(res_msg);
if(content_length < 0) {
return -1;
}
아래 루프를 통해서 완전히 파일 데이터를 수신 마칠 때 까지 확인합니다. 수신 도중 연결이 끊기거나 사용자가 중단할 경우, 오류로 간주합니다.
do {
ssize_t current_length = netMessageGetCurrentContentLength(res_msg);
if(current_length >= 0) {
//현재 까지 수신된 데이터 길이를 확인할 수 있습니다.
}
usleep(1000000);
} while(netMessageIsConnected(res_msg) && netMessageIsContentReady(res_msg) == false && continuing_);
if(!netMessageIsContentReady(res_msg) || !continuing_) {
return -1;
}
캐쉬 디렉토리(/tmp)에 저장된 파일 경로를 얻습니다.
std::string filepath;
netMessageGetContentFilePath(res_msg, filepath);
사용 후 파일을 삭제합니다.
unlink(filepath.c_str());
웹 서버에 폼 데이터 보내기¶
웹 서버에 연결합니다.
const int32_t timeout = 60000;
HttpConnectionRef conn = httpOpenConnection(
"https://example.com/api/status/update",
timeout;
연결 객체로 부터 요청 메세지를 얻어와서 Method를 POST으로 설정합니다.
NetMessageRef msg = httpConnectionGetRequestMessage(conn);
netMessageSetMethod(msg, "POST");
폼 필드 데이터를 설정하고, 전송합니다.
netMessageAddFieldToMultipartFormData(msg, "state", "idle");
// 파일 경로를 지정하여, 전송이 파일 데이터도 함께 보냅니다.
netMessageAddFileToMultipartFormData(msg, "image", file_name.c_str(), file_path_.c_str(), get_file_mime_type(file_path_.c_str()));
netMessagePost(msg);
서버로 부터 응답 헤더를 수신합니다. 일정 시간내에 응답이 없으면 실패한 것으로 간주합니다.
NetMessageRef res_msg;
wait_count = 0;
do {
res_msg = httpConnectionGetResponseHeaderMessage(conn, 1000);
wait_count++;
} while(res_msg == nullptr && continuing_ && wait_count*1000 < timeout);
if(res_msg == nullptr || !continuing_) {
return -1;
}
응답 코드를 확인합니다. 200이 아닌 경우, 오류 응답으로 간주합니다.
std::string reason_string;
int32_t status_code = netMessageGetStatusCodeAndReasonString(res_msg, reason_string);
if(status_code != 200) {
return -1;
}
응답 컨텐츠 길이를 확인하고, 응답 컨텐츠를 얻습니다. 보통 서버는 JSON 양식의 응답 데이터를 전송합니다. 메세지의 파일 길이가 유효하지 않으면 오류로 간주합니다.
ssize_t content_length = netMessageGetContentLength(res_msg);
if(content_length < 0) {
return -1;
}
std::vector<char> res_content;
netMessageGetContent(res_msg, res_content);
응답 메세지를 처리합니다.
웹 서버에서 폼 데이터 파일 저장하기¶
웹 서버를 생성하고 시작합니다. 캐쉬 디렉토리를 지정하고, 캐쉬할 컨텐츠 타입에 multipart/form-data를 지정합니다.
예제에서 오류 상황은 고려하지 않았습니다.
parameters["port"] = std::to_string(http_port);
parameters["root-dir"] = "/var/www/htdocs";
parameters["content-length-max"] = std::to_string(200*1024*1024);
parameters["cache-dir"] = "/tmp";
parameters["cached-content-types"] = "multipart/form-data,multipart/mixed";
std::shared_ptr<MyHttpUpStreamDelegate> http_delegate = std::make_shared<MyHttpUpStreamDelegate>();
HttpServerRef http_server = oasis::createHttpServer(parameters, http_delegate);
oasis::startHttpServer(http_server);
웹 클라이언트가 폼 데이터의 "image" 파일 입력 필드에 지정한 파일을 웹 서버로 전송하면, 웹 서버는 캐쉬 폴더에 이 응답 메세지를 저장합니다.
웹 서버는 MyHttpUpStreamDelegate::onRequest 콜백 함수에서 웹 클라이언트가 업로드한 파일을 저장하고, JSON으로 응답 코드를 보냅니다.
virtual void MyHttpUpStreamDelegate::onRequest(const NetMessageRef &msg) {
std::string url;
netMessageUriGetPath(msg, url);
if(url == "/api/status/update") {
std::string content_type, filename;
ssize_t size = netMessageGetFormDataFileData(msg, "image", content_type, filename, nullptr);
if(size > 0) {
char path[PATH_MAX];
sprintf(path, "/tmp/%s", filename.c_str());
ssize_t saved_size = netMessageSaveFormDataFileDataAs(msg, "image", path);
}
NetMessageRef res = netMessageCreate(msg, false);
netMessageSetStatusCodeAndReasonString(res, 200, "OK");
netMessageSetHeaderField(res, "Connection", "close");
std::string json_string = R"({ "result":"OK" })";
netMessageSetContent(res, "application/json", json_string.c_str(), json_string.length());
netMessagePost(res);
}
}