Repository URL to install this package:
|
Version:
2.0.17 ▾
|
/* async fastcgi protocol parser */
#include <uwsgi.h>
extern struct uwsgi_server uwsgi;
#define FCGI_END_REQUEST "\1\x06\0\1\0\0\0\0\1\3\0\1\0\x08\0\0\0\0\0\0\0\0\0\0"
struct fcgi_record {
uint8_t version;
uint8_t type;
uint8_t req1;
uint8_t req0;
uint8_t cl1;
uint8_t cl0;
uint8_t pad;
uint8_t reserved;
} __attribute__ ((__packed__));
// convert fastcgi params to uwsgi key/val
int fastcgi_to_uwsgi(struct wsgi_request *wsgi_req, char *buf, size_t len) {
size_t j;
uint8_t octet;
uint32_t keylen, vallen;
for (j = 0; j < len; j++) {
octet = (uint8_t) buf[j];
if (octet > 127) {
if (j + 4 >= len)
return -1;
keylen = uwsgi_be32(&buf[j]) ^ 0x80000000;
j += 4;
}
else {
if (j + 1 >= len)
return -1;
keylen = octet;
j++;
}
octet = (uint8_t) buf[j];
if (octet > 127) {
if (j + 4 >= len)
return -1;
vallen = uwsgi_be32(&buf[j]) ^ 0x80000000;
j += 4;
}
else {
if (j + 1 >= len)
return -1;
vallen = octet;
j++;
}
if (j + (keylen + vallen) > len) {
return -1;
}
if (keylen > 0xffff || vallen > 0xffff)
return -1;
uint16_t pktsize = proto_base_add_uwsgi_var(wsgi_req, buf + j, keylen, buf + j + keylen, vallen);
if (pktsize == 0)
return -1;
wsgi_req->uh->pktsize += pktsize;
// -1 here as the for() will increment j again
j += (keylen + vallen) - 1;
}
return 0;
}
/*
each fastcgi packet is composed by a header and a body
the parser rebuild a whole packet until it finds a 0 STDIN
*/
int uwsgi_proto_fastcgi_parser(struct wsgi_request *wsgi_req) {
// allocate space for a fastcgi record
if (!wsgi_req->proto_parser_buf) {
wsgi_req->proto_parser_buf = uwsgi_malloc(uwsgi.buffer_size);
wsgi_req->proto_parser_buf_size = uwsgi.buffer_size;
}
ssize_t len = read(wsgi_req->fd, wsgi_req->proto_parser_buf + wsgi_req->proto_parser_pos, wsgi_req->proto_parser_buf_size - wsgi_req->proto_parser_pos);
if (len > 0) {
goto parse;
}
if (len < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) {
return UWSGI_AGAIN;
}
uwsgi_error("uwsgi_proto_fastcgi_parser()");
return -1;
}
// mute on 0 len...
if (wsgi_req->proto_parser_pos > 0) {
uwsgi_error("uwsgi_proto_fastcgi_parser()");
}
return -1;
parse:
wsgi_req->proto_parser_pos += len;
// ok let's see what we need to do
for (;;) {
if (wsgi_req->proto_parser_pos >= sizeof(struct fcgi_record)) {
struct fcgi_record *fr = (struct fcgi_record *) wsgi_req->proto_parser_buf;
uint16_t fcgi_len = uwsgi_be16((char *) &fr->cl1);
uint32_t fcgi_all_len = sizeof(struct fcgi_record) + fcgi_len + fr->pad;
uint8_t fcgi_type = fr->type;
uint8_t *sid = (uint8_t *) & wsgi_req->stream_id;
sid[0] = fr->req0;
sid[1] = fr->req1;
// if STDIN, end of the loop
if (fcgi_type == 5) {
wsgi_req->uh->modifier1 = uwsgi.fastcgi_modifier1;
wsgi_req->uh->modifier2 = uwsgi.fastcgi_modifier2;
// does the request stream ended ?
if (fcgi_len == 0) wsgi_req->proto_parser_eof = 1;
return UWSGI_OK;
}
// if we have a full packet, parse it and reset the memory
if (wsgi_req->proto_parser_pos >= fcgi_all_len) {
// PARAMS ? (ignore other types)
if (fcgi_type == 4) {
if (fastcgi_to_uwsgi(wsgi_req, wsgi_req->proto_parser_buf + sizeof(struct fcgi_record), fcgi_len)) {
return -1;
}
}
memmove(wsgi_req->proto_parser_buf, wsgi_req->proto_parser_buf + fcgi_all_len, wsgi_req->proto_parser_pos - fcgi_all_len);
wsgi_req->proto_parser_pos -= fcgi_all_len;
}
else if (fcgi_all_len > wsgi_req->proto_parser_buf_size - wsgi_req->proto_parser_pos) {
char *tmp_buf = realloc(wsgi_req->proto_parser_buf, wsgi_req->proto_parser_buf_size + fcgi_all_len - (wsgi_req->proto_parser_buf_size - wsgi_req->proto_parser_pos));
if (!tmp_buf) {
uwsgi_error("uwsgi_proto_fastcgi_parser()/realloc()");
return -1;
}
wsgi_req->proto_parser_buf = tmp_buf;
wsgi_req->proto_parser_buf_size += (fcgi_all_len - (wsgi_req->proto_parser_buf_size - wsgi_req->proto_parser_pos));
break;
}
else {
break;
}
}
else {
break;
}
}
return UWSGI_AGAIN;
}
ssize_t uwsgi_proto_fastcgi_read_body(struct wsgi_request * wsgi_req, char *buf, size_t len) {
int has_read = 0;
if (wsgi_req->proto_parser_remains > 0) {
size_t remains = UMIN(wsgi_req->proto_parser_remains, len);
memcpy(buf, wsgi_req->proto_parser_remains_buf, remains);
wsgi_req->proto_parser_remains -= remains;
wsgi_req->proto_parser_remains_buf += remains;
// we consumed all of the body, we can safely move the memory
if (wsgi_req->proto_parser_remains == 0 && wsgi_req->proto_parser_move) {
memmove(wsgi_req->proto_parser_buf, wsgi_req->proto_parser_buf + wsgi_req->proto_parser_move, wsgi_req->proto_parser_pos);
wsgi_req->proto_parser_move = 0;
}
return remains;
}
// if we already have seen eof, return 0
if (wsgi_req->proto_parser_eof) return 0;
ssize_t rlen;
for (;;) {
if (wsgi_req->proto_parser_pos >= sizeof(struct fcgi_record)) {
struct fcgi_record *fr = (struct fcgi_record *) wsgi_req->proto_parser_buf;
uint16_t fcgi_len = uwsgi_be16((char *) &fr->cl1);
uint32_t fcgi_all_len = sizeof(struct fcgi_record) + fcgi_len + fr->pad;
uint8_t fcgi_type = fr->type;
// if we have a full packet, parse it and reset the memory
if (wsgi_req->proto_parser_pos >= fcgi_all_len) {
// STDIN ? (ignore other types)
if (fcgi_type == 5) {
// EOF ?
if (fcgi_len == 0) {
wsgi_req->proto_parser_eof = 1;
return 0;
}
// copy data to the buf
size_t remains = UMIN(fcgi_len, len);
memcpy(buf, wsgi_req->proto_parser_buf + sizeof(struct fcgi_record), remains);
// copy remaining
wsgi_req->proto_parser_remains = fcgi_len - remains;
wsgi_req->proto_parser_remains_buf = wsgi_req->proto_parser_buf + sizeof(struct fcgi_record) + remains;
// we consumed all of the body, we can safely move the memory
if (wsgi_req->proto_parser_remains == 0) {
memmove(wsgi_req->proto_parser_buf, wsgi_req->proto_parser_buf + fcgi_all_len, wsgi_req->proto_parser_pos - fcgi_all_len);
}
else {
// postpone memory move
wsgi_req->proto_parser_move = fcgi_all_len;
}
wsgi_req->proto_parser_pos -= fcgi_all_len;
return remains;
}
memmove(wsgi_req->proto_parser_buf, wsgi_req->proto_parser_buf + fcgi_all_len, wsgi_req->proto_parser_pos - fcgi_all_len);
wsgi_req->proto_parser_pos -= fcgi_all_len;
}
else if (fcgi_all_len > wsgi_req->proto_parser_buf_size - wsgi_req->proto_parser_pos) {
char *tmp_buf = realloc(wsgi_req->proto_parser_buf, wsgi_req->proto_parser_buf_size + fcgi_all_len - (wsgi_req->proto_parser_buf_size - wsgi_req->proto_parser_pos));
if (!tmp_buf) {
uwsgi_error("uwsgi_proto_fastcgi_read_body()/realloc()");
return -1;
}
wsgi_req->proto_parser_buf = tmp_buf;
wsgi_req->proto_parser_buf_size += fcgi_all_len - (wsgi_req->proto_parser_buf_size - wsgi_req->proto_parser_pos);
}
if (!has_read)
goto gather;
errno = EAGAIN;
return -1;
}
else {
gather:
rlen = read(wsgi_req->fd, wsgi_req->proto_parser_buf + wsgi_req->proto_parser_pos, wsgi_req->proto_parser_buf_size - wsgi_req->proto_parser_pos);
if (rlen > 0) {
has_read = 1;
wsgi_req->proto_parser_pos += rlen;
continue;
}
return rlen;
}
}
return -1;
}
// write a STDOUT packet
int uwsgi_proto_fastcgi_write(struct wsgi_request *wsgi_req, char *buf, size_t len) {
// fastcgi packets are limited to 64k
if (wsgi_req->proto_parser_status == 0) {
uint16_t fcgi_len = UMIN(len - wsgi_req->write_pos, 0xffff);
wsgi_req->proto_parser_status = fcgi_len;
struct fcgi_record fr;
fr.version = 1;
fr.type = 6;
uint8_t *sid = (uint8_t *) & wsgi_req->stream_id;
fr.req1 = sid[1];
fr.req0 = sid[0];
fr.pad = 0;
fr.reserved = 0;
fr.cl0 = (uint8_t) (fcgi_len & 0xff);
fr.cl1 = (uint8_t) ((fcgi_len >> 8) & 0xff);
if (uwsgi_write_true_nb(wsgi_req->fd, (char *) &fr, sizeof(struct fcgi_record), uwsgi.socket_timeout)) {
return -1;
}
}
ssize_t wlen = write(wsgi_req->fd, buf + wsgi_req->write_pos, wsgi_req->proto_parser_status);
if (wlen > 0) {
wsgi_req->write_pos += wlen;
wsgi_req->proto_parser_status -= wlen;
if (wsgi_req->write_pos == len) {
return UWSGI_OK;
}
return UWSGI_AGAIN;
}
if (wlen < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) {
return UWSGI_AGAIN;
}
}
return -1;
}
void uwsgi_proto_fastcgi_close(struct wsgi_request *wsgi_req) {
// before sending the END_REQUEST and closing the connection we need to check for EOF
if (!wsgi_req->proto_parser_eof) {
// we use a custom tiny buffer, all the data will be discarded...
char buf[4096];
for(;;) {
ssize_t rlen = uwsgi_proto_fastcgi_read_body(wsgi_req, buf, 4096);
if (rlen < 0) {
if (uwsgi_is_again()) {
int ret = uwsgi.wait_read_hook(wsgi_req->fd, uwsgi.socket_timeout);
if (ret <= 0) goto end;
continue;
}
goto end;
}
if (rlen == 0) break;
}
}
// special case here, we run in void context, so we need to wait directly here
char end_request[24];
memcpy(end_request, FCGI_END_REQUEST, 24);
char *sid = (char *) &wsgi_req->stream_id;
// update with request id
end_request[2] = sid[1];
end_request[3] = sid[0];
end_request[10] = sid[1];
end_request[11] = sid[0];
(void) uwsgi_write_true_nb(wsgi_req->fd, end_request, 24, uwsgi.socket_timeout);
end:
uwsgi_proto_base_close(wsgi_req);
}
int uwsgi_proto_fastcgi_sendfile(struct wsgi_request *wsgi_req, int fd, size_t pos, size_t len) {
// fastcgi packets are limited to 64k
if (wsgi_req->proto_parser_status == 0) {
uint16_t fcgi_len = (uint16_t) UMIN(len - wsgi_req->write_pos, 0xffff);
wsgi_req->proto_parser_status = fcgi_len;
struct fcgi_record fr;
fr.version = 1;
fr.type = 6;
uint8_t *sid = (uint8_t *) & wsgi_req->stream_id;
fr.req1 = sid[1];
fr.req0 = sid[0];
fr.pad = 0;
fr.reserved = 0;
fr.cl0 = (uint8_t) (fcgi_len & 0xff);
fr.cl1 = (uint8_t) ((fcgi_len >> 8) & 0xff);
if (uwsgi_write_true_nb(wsgi_req->fd, (char *) &fr, sizeof(struct fcgi_record), uwsgi.socket_timeout)) {
return -1;
}
}
ssize_t wlen = uwsgi_sendfile_do(wsgi_req->fd, fd, pos + wsgi_req->write_pos, wsgi_req->proto_parser_status);
if (wlen > 0) {
wsgi_req->write_pos += wlen;
wsgi_req->proto_parser_status -= wlen;
if (wsgi_req->write_pos == len) {
return UWSGI_OK;
}
return UWSGI_AGAIN;
}
if (wlen < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) {
return UWSGI_AGAIN;
}
}
return -1;
}
void uwsgi_proto_fastcgi_setup(struct uwsgi_socket *uwsgi_sock) {
uwsgi_sock->proto = uwsgi_proto_fastcgi_parser;
uwsgi_sock->proto_accept = uwsgi_proto_base_accept;
uwsgi_sock->proto_prepare_headers = uwsgi_proto_base_cgi_prepare_headers;
uwsgi_sock->proto_add_header = uwsgi_proto_base_add_header;
uwsgi_sock->proto_fix_headers = uwsgi_proto_base_fix_headers;
uwsgi_sock->proto_read_body = uwsgi_proto_fastcgi_read_body;
uwsgi_sock->proto_write = uwsgi_proto_fastcgi_write;
uwsgi_sock->proto_write_headers = uwsgi_proto_fastcgi_write;
uwsgi_sock->proto_sendfile = uwsgi_proto_fastcgi_sendfile;
uwsgi_sock->proto_close = uwsgi_proto_fastcgi_close;
}
void uwsgi_proto_fastcgi_nph_setup(struct uwsgi_socket *uwsgi_sock) {
uwsgi_sock->proto = uwsgi_proto_fastcgi_parser;
uwsgi_sock->proto_accept = uwsgi_proto_base_accept;
uwsgi_sock->proto_prepare_headers = uwsgi_proto_base_prepare_headers;
uwsgi_sock->proto_add_header = uwsgi_proto_base_add_header;
uwsgi_sock->proto_fix_headers = uwsgi_proto_base_fix_headers;
uwsgi_sock->proto_read_body = uwsgi_proto_fastcgi_read_body;
uwsgi_sock->proto_write = uwsgi_proto_fastcgi_write;
uwsgi_sock->proto_write_headers = uwsgi_proto_fastcgi_write;
uwsgi_sock->proto_sendfile = uwsgi_proto_fastcgi_sendfile;
uwsgi_sock->proto_close = uwsgi_proto_fastcgi_close;
}