EV-ClickHouse
view release on metacpan or search on metacpan
xs/proto_http.c view on Meta::CPAN
if (self->session_id) {
plen += (size_t)snprintf(params + plen, params_cap - plen, "&session_id=");
plen += url_encode(self->session_id, strlen(self->session_id), params + plen);
}
if (url_sql) {
plen += (size_t)snprintf(params + plen, params_cap - plen, "&query=");
plen += url_encode(url_sql, url_sql_len, params + plen);
}
plen = append_settings_url_params(params, plen,
defaults, overrides,
&query_id, &query_id_len);
if (query_id) {
size_t need = plen + 10 + query_id_len * 3 + 1;
if (need > params_cap) {
params_cap = need;
Renew(params, params_cap, char);
}
plen += (size_t)snprintf(params + plen, params_cap - plen, "&query_id=");
plen += url_encode(query_id, query_id_len, params + plen);
}
params[plen] = '\0';
/* user/password are quoted as-is in the X-ClickHouse-* form, or
* base64-expanded ((n+2)/3)*4 in the Basic auth form. The 4/3 factor
* is enough either way. */
req_cap = 512 + body_len + plen
+ (self->host ? strlen(self->host) : 0)
+ (self->user ? strlen(self->user) * 2 : 0)
+ (self->password ? strlen(self->password) * 2 : 0);
Newx(req, req_cap, char);
/* request line + headers */
pos += snprintf(req + pos, req_cap - pos,
"POST /%s HTTP/1.1\r\n", params);
Safefree(params);
pos += snprintf(req + pos, req_cap - pos,
"Host: %s:%u\r\n", self->host, self->port);
if (self->http_basic_auth && self->user) {
/* "user:pass" â base64. Single allocation: [cred][b64]. */
size_t ul = strlen(self->user);
size_t pl = self->password ? strlen(self->password) : 0;
size_t cred_len = ul + 1 + pl;
size_t b64_cap = ((cred_len + 2) / 3) * 4 + 1;
char *buf;
Newx(buf, cred_len + b64_cap, char);
memcpy(buf, self->user, ul);
buf[ul] = ':';
if (pl) memcpy(buf + ul + 1, self->password, pl);
base64_encode((const unsigned char *)buf, cred_len, buf + cred_len);
pos += snprintf(req + pos, req_cap - pos,
"Authorization: Basic %s\r\n", buf + cred_len);
Safefree(buf);
} else {
if (self->user)
pos += snprintf(req + pos, req_cap - pos,
"X-ClickHouse-User: %s\r\n", self->user);
if (self->password && self->password[0])
pos += snprintf(req + pos, req_cap - pos,
"X-ClickHouse-Key: %s\r\n", self->password);
}
pos += snprintf(req + pos, req_cap - pos, "Connection: keep-alive\r\n");
if (self->compress)
pos += snprintf(req + pos, req_cap - pos, "Accept-Encoding: gzip\r\n");
if (content_encoding)
pos += snprintf(req + pos, req_cap - pos, "%s", content_encoding);
pos += snprintf(req + pos, req_cap - pos,
"Content-Length: %lu\r\n\r\n", (unsigned long)body_len);
/* body */
if (body_len > 0) {
if (pos + body_len > req_cap) {
req_cap = pos + body_len + 1;
Renew(req, req_cap, char);
}
Copy(body ? body : body_data, req + pos, body_len, char);
pos += body_len;
}
if (body) Safefree(body);
*req_len = pos;
return req;
}
/* Build HTTP GET /ping request */
static char* build_http_ping_request(ev_clickhouse_t *self, size_t *req_len) {
char *req;
size_t req_cap = 128 + (self->host ? strlen(self->host) : 0);
size_t pos = 0;
Newx(req, req_cap, char);
pos = snprintf(req, req_cap,
"GET /ping HTTP/1.1\r\n"
"Host: %s:%u\r\n"
"Connection: keep-alive\r\n\r\n",
self->host, self->port);
if (pos >= req_cap) pos = req_cap - 1;
*req_len = pos;
return req;
}
/* --- HTTP response parsing --- */
/* Length-bounded uint parser. Stops at the first non-digit OR at `len`,
* whichever comes first. Used to parse X-ClickHouse-Summary values out
* of a non-NUL-terminated header buffer without letting strtoull scan
* into bytes past the header on malformed input. */
static uint64_t parse_uint_within(const char *p, size_t len) {
uint64_t n = 0;
size_t i;
for (i = 0; i < len; i++) {
unsigned char c = (unsigned char)p[i];
if (c < '0' || c > '9') break;
n = n * 10 + (c - '0');
}
return n;
}
/* Find \r\n\r\n in recv_buf. Returns offset past it, or 0 if not found. */
static size_t find_header_end(const char *buf, size_t len) {
size_t i;
if (len < 4) return 0;
for (i = 0; i <= len - 4; i++) {
if (buf[i] == '\r' && buf[i+1] == '\n' &&
buf[i+2] == '\r' && buf[i+3] == '\n') {
return i + 4;
}
}
return 0;
}
/* Extract ClickHouse error code from HTTP error body ("Code: NNN. ...") */
static int32_t parse_ch_error_code(const char *body, size_t len) {
if (len > 6 && memcmp(body, "Code: ", 6) == 0)
return (int32_t)atoi(body + 6);
return 0;
}
/* Format an HTTP error response into a Newx-allocated "HTTP NNN: ..." message
* and update self->last_error_code. Body may be gzip-compressed. Caller must
* Safefree the returned pointer. */
static char* format_http_error(ev_clickhouse_t *self, int status,
const char *body, size_t body_len, int is_gzip) {
char *errmsg;
char *err_body = (char *)body;
size_t err_len = body_len;
if (is_gzip && body_len > 0) {
size_t dec_len;
char *dec = gzip_decompress(body, body_len, &dec_len);
if (dec) { err_body = dec; err_len = dec_len; }
}
while (err_len > 0 && (err_body[err_len-1] == '\n' || err_body[err_len-1] == '\r'))
err_len--;
self->last_error_code = parse_ch_error_code(err_body, err_len);
Newx(errmsg, err_len + 64, char);
( run in 1.698 second using v1.01-cache-2.11-cpan-14f38c9f855 )