Liggaam Data Hantering vir HTTP Bediener in C: Lees en Verwerk application/x-www-form-urlencoded Data
Inleiding
In die vorige arikel het ons gekyk na lêerbediening. Nou keer ons terug na iets wat nie implementeer was nie, naamlik om HTTP operasies te hanteer waar ‘n mens die liggaam in moet lees en verwerk. Om dit te doen begin ons deur ‘n verandering aan te bring tot die HTTPRequest data struktuur en ‘n paar nuwe nuts funksies by te voeg. Die oorspronklike artikel kan hier besigtig word.
Stappe
Verander HTTPRequest in ./src/util.h om ‘n verwysing na die oorspronklik ingeleesde HTTP kopstuk te stoor.
typedef struct HTTPRequest {
...
char *raw_header; // nuwe veld
} HTTPRequest;Neem kennis dat met veranderings tot koppelvlakke is dit aanbeveel dat al die kompilasie eenhede oor kompileer word. Loop make clean en dan make om seker te maak dat die uitleg van die data struktuur sinkroniseer word in die ander eenhede waar dit ookal gebruik mag word.
Maak ook die volgende verandering in die parse_http_request funksie in ./src/util.c:
int parse_http_request(char *buf, HTTPRequest *req, Server * server) {
// stoor 'n verwysing na die volle HTTP kopstuk
req->raw_header = buf;
...
}Nou kan ons nuts ‘n funksie defineer wat die Content-Length as ‘n numeriese waarde terug stuur. Die funksie sal later gebruik word met die lees van die HTTP liggaam. Redigeer ./src/util.h en voeg die volgende funksie by:
size_t req_get_content_length(HTTPRequest *req);Implementeer req_get_content_length() in ./src/util.c soos volg:
size_t req_get_content_length(HTTPRequest *req) {
char cl_str[32];
// soek die Content-Length waarde in die rou HTTP-opskrif
get_header_value(req->raw_header, "Content-Length", cl_str, sizeof(cl_str));
if (strlen(cl_str) == 0) {
return 0;
}
// Skakel die teks om na 'n getal
return (size_t)atoll(cl_str);
}Volgende moet ons sekere sleutelwaardes in die HTTP versoek kan opsoek, b.v. Content-Type of Content-Length. Definieer get_header_value in in ./src/util.h soos volg:
void get_header_value(const char *buf, const char *header_name, char *dest, size_t dest_size);Implementeer get_header_value() in ./src/util.c soos volg:
void get_header_value(const char *buf, const char *header_name, char *dest, size_t dest_size) {
char search_str[150];
snprintf(search_str, sizeof(search_str), "\n%s:", header_name);
// soek sleutel
char *p = strcasestr(buf, search_str);
if (!p) {
// kyk of dit die heel eerste sleutelwaarde paar is
snprintf(search_str, sizeof(search_str), "%s:", header_name);
if (strncasecmp(buf, search_str, strlen(search_str)) == 0) p = (char*)buf;
else { dest[0] = '\0'; return; }
}
p = strchr(p, ':') + 1;
while (*p == ' ') p++; // ignoreer spasies
char *end = strchr(p, '\r');
if (!end) end = strchr(p, '\n');
size_t len = (end) ? (size_t)(end - p) : strlen(p);
if (len >= dest_size) len = dest_size - 1;
strncpy(dest, p, len);
dest[len] = '\0';
}Nou kan ons ‘n funksie req_get_body() in ./src/util.h definieer om die HTTP liggaam te lees.
char* req_get_body(HTTPRequest *req, int client_fd, char *buffer, size_t buf_size);Implementeer req_get_body() in ./src/util.c soos volg:
char* req_get_body(HTTPRequest *req, int client_fd, char *buffer, size_t buf_size) {
// soek Content-Length om te weet hoeveel om te lees
char cl_str[32];
get_header_value(req->raw_header, "Content-Length", cl_str, sizeof(cl_str));
size_t content_len = atoi(cl_str);
if (content_len <= 0) return ""; // geen liggaam om te lees nie
// lees konneksie
if (content_len >= buf_size) content_len= buf_size - 1;
size_t total_read = 0;
while (total_read < content_len) {
ssize_t n = recv(client_fd, buffer + total_read, content_len - total_read, 0);
if (n <= 0) break;
total_read += n;
}
// voeg terminasie karakter by
buffer[total_read] = '\0';
return buffer;
}Volgende kan ons nog ‘n nuts funksie bysit om teks te enkodeer vir gebruik in JSON waardes. Definieer in nuwe funksie in ./src/util.h:
char* json_escape(const char *input);Implementeer json_escape() in ./src/util.c soos volg:
char* json_escape(const char *input) {
if (!input) return NULL;
// verskaf genoeg spasie vir die ergste geval waar elke liewe karakter kodeer moet word (b.v., \uXXXX)
// vir eenvoudige gevalle is 2 keer die oorspronklike lengte genoeg
size_t len = strlen(input);
char *output = malloc(len * 6 + 3); // allokeer eerder meer
if (!output) return NULL;
char *ptr = output;
*ptr++ = '"'; // begin aanhaling
while (*input) {
switch (*input) {
case '"': *ptr++ = '\\'; *ptr++ = '"'; break;
case '\\': *ptr++ = '\\'; *ptr++ = '\\'; break;
case '\b': *ptr++ = '\\'; *ptr++ = 'b'; break;
case '\f': *ptr++ = '\\'; *ptr++ = 'f'; break;
case '\n': *ptr++ = '\\'; *ptr++ = 'n'; break;
case '\r': *ptr++ = '\\'; *ptr++ = 'r'; break;
case '\t': *ptr++ = '\\'; *ptr++ = 't'; break;
default:
if (*input < 32) { // Control characters
sprintf(ptr, "\\u%04x", *input);
ptr += 6;
} else {
*ptr++ = *input;
}
break;
}
input++;
}
*ptr++ = '"'; // eind aanhaaling
*ptr = '\0';
return output;
}Ons sal hierdie funksie gebruik in die hanteerder om te verseker ons geldige JSON terug stuur oor die konneksie.
Nou is daar genoeg nuwe funksionaliteit in plek om ‘n basiese HTTP POST te hanteer. Defineer handle_test_post toets funksie in ./src/routes.h:
char* handle_test_post(int client_fd, HTTPRequest *req);Definieer handle_test_post() in ./src/routes.c` soos volg:
char* handle_test_post(int client_fd, HTTPRequest *req) {
if (strcmp(req->method, "POST") != 0) return "{\"error\": \"invalid operation\"}";
size_t len = req_get_content_length(req);
char *body = (len > 0) ? malloc(len + 1) : NULL;
// lees liggaam
if (body) req_get_body(req, client_fd, body, len + 1);
char c_type[128], key[128], val[128], res_json[512], response[1024];
get_header_value(req->raw_header, "Content-Type", c_type, sizeof(c_type));
// valideer versoek waardes
if (!body || !strstr(c_type, "application/x-www-form-urlencoded") ||
!get_query_param(body, "k", key, sizeof(key)) ||
!get_query_param(body, "v", val, sizeof(val))) {
free(body);
return "{\"error\": \"invalid request or data\"}";
}
// bou afvoer JSON
char *k_esc = json_escape(key), *v_esc = json_escape(val);
snprintf(res_json, sizeof(res_json), "{\"key\":%s,\"value\":%s,\"len\":%zu}", k_esc, v_esc, len);
// bou HTTP antwoord
build_http_response(response, sizeof(response), 200, "OK", "Content-Type", "application/json", "Connection", "close", NULL, res_json);
// stuur HTTP antwoord
send(client_fd, response, strlen(response), 0);
// stel geheue vry
free(k_esc); free(v_esc); free(body);
return NULL;
}In hierdie funksie toets ons of ons met ‘n POST operasie werk, ons kyk dat die data tipe application/x-www-form-urlencoded is, ons lees die HTTP liggaam in, onttrek sleutelwaardes k en v, kodeer dit na JSON teks toe, bou ‘n JSON liggaam, bou ‘n HTTP antwoord, en stuur dit dan terug na die kliënt toe.
‘n Nuwe roete kan nou in ./src/main.c registreer word soos volg:
route_add(app_server, "/test/post", handle_test_post);Die hanteering van POST oeprasies kan nou getoets word. Die volgende versoek behoort anvaar te word:
curl -X POST http://localhost:8080/test/post \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "k=user&v=john_doe"Dit het die regte HTTP operasie, inhouds tipe, en parameters. Die volgende is ‘n voorbeeld van ‘n versoek wat nie sal werk nie.
curl -X POST http://localhost:8080/test/post \
-H "Content-Type: application/json" \
-d '{"k": "john", "v": "doe"}'Opsomming
In hierdie artikel het ons gekyk na hoe ons ‘n HTTP POST operasie vir application/x-www-form-urlencoded tipe ligame te hanteer. In die volgende artikel sal ons kyk na hoe ons ‘n globale data stoor kan by sit.
(Afrikaanse artikel)


Comments
Post a Comment