mirror of
https://github.com/clearlinux/uwsgi.git
synced 2026-08-29 20:54:04 +00:00
308 lines
7.7 KiB
C
308 lines
7.7 KiB
C
/* async fastcgi protocol parser */
|
|
|
|
#include "../uwsgi.h"
|
|
|
|
extern struct uwsgi_server uwsgi;
|
|
|
|
#define PROTO_STATUS_RECV_HDR 0
|
|
#define PROTO_STATUS_RECV_BODY 1
|
|
|
|
|
|
int uwsgi_proto_fastcgi_parser(struct wsgi_request *wsgi_req) {
|
|
|
|
ssize_t len;
|
|
struct fcgi_record *fr;
|
|
uint16_t rs;
|
|
int j;
|
|
uint8_t octet;
|
|
uint32_t keylen, vallen;
|
|
|
|
// allocate space for a fastcgi record
|
|
if (!wsgi_req->proto_parser_buf) {
|
|
wsgi_req->proto_parser_buf = uwsgi_malloc(8 + 65536);
|
|
}
|
|
|
|
if (wsgi_req->proto_parser_status == PROTO_STATUS_RECV_HDR) {
|
|
len = read(wsgi_req->poll.fd, wsgi_req->proto_parser_buf + wsgi_req->proto_parser_pos, 8 - wsgi_req->proto_parser_pos);
|
|
if (len <= 0) {
|
|
free(wsgi_req->proto_parser_buf);
|
|
uwsgi_error("read()");
|
|
return -1;
|
|
}
|
|
wsgi_req->proto_parser_pos += len;
|
|
|
|
if (wsgi_req->proto_parser_pos >= 8) {
|
|
fr = (struct fcgi_record *) wsgi_req->proto_parser_buf;
|
|
rs = ntohs(fr->cl);
|
|
#ifdef UWSGI_DEBUG
|
|
uwsgi_log("fastcgi rs %d type %d\n", rs, fr->type);
|
|
#endif
|
|
// empty STDIN ?
|
|
if (fr->type == 5 && rs == 0) {
|
|
wsgi_req->proto_parser_status = 0;
|
|
if (wsgi_req->async_post) {
|
|
rewind(wsgi_req->async_post);
|
|
wsgi_req->body_as_file = 1;
|
|
}
|
|
free(wsgi_req->proto_parser_buf);
|
|
return UWSGI_OK;
|
|
}
|
|
wsgi_req->proto_parser_pos = 0;
|
|
if (rs == 0) {
|
|
return UWSGI_AGAIN;
|
|
}
|
|
wsgi_req->proto_parser_status = PROTO_STATUS_RECV_BODY;
|
|
}
|
|
return UWSGI_AGAIN;
|
|
}
|
|
else if (wsgi_req->proto_parser_status == PROTO_STATUS_RECV_BODY) {
|
|
|
|
fr = (struct fcgi_record *) wsgi_req->proto_parser_buf;
|
|
rs = ntohs(fr->cl);
|
|
|
|
len = read(wsgi_req->poll.fd, wsgi_req->proto_parser_buf + 8 + wsgi_req->proto_parser_pos, (rs + fr->pad) - wsgi_req->proto_parser_pos);
|
|
if (len <= 0) {
|
|
free(wsgi_req->proto_parser_buf);
|
|
uwsgi_error("read()");
|
|
return -1;
|
|
}
|
|
wsgi_req->proto_parser_pos += len;
|
|
|
|
if (wsgi_req->proto_parser_pos >= rs + fr->pad) {
|
|
#ifdef UWSGI_DEBUG
|
|
uwsgi_log("record parsed = type: %d size: %d padding: %d\n", fr->type, rs, fr->pad);
|
|
#endif
|
|
|
|
// params
|
|
if (fr->type == 4) {
|
|
for (j = 0; j < rs; j++) {
|
|
octet = ((uint8_t *) wsgi_req->proto_parser_buf)[8 + j];
|
|
if (octet > 127) {
|
|
if (j + 4 >= rs) {
|
|
free(wsgi_req->proto_parser_buf);
|
|
return -1;
|
|
}
|
|
memcpy(&keylen, wsgi_req->proto_parser_buf + 8 + j, 4);
|
|
keylen = ntohl(keylen) ^ 0x80000000;
|
|
j += 4;
|
|
}
|
|
else {
|
|
if (j + 1 >= rs) {
|
|
free(wsgi_req->proto_parser_buf);
|
|
return -1;
|
|
}
|
|
keylen = octet;
|
|
j++;
|
|
}
|
|
octet = ((uint8_t *) wsgi_req->proto_parser_buf)[8 + j];
|
|
if (octet > 127) {
|
|
if (j + 4 > rs) {
|
|
free(wsgi_req->proto_parser_buf);
|
|
return -1;
|
|
}
|
|
memcpy(&vallen, wsgi_req->proto_parser_buf + 8 + j, 4);
|
|
vallen = ntohl(vallen) ^ 0x80000000;
|
|
j += 4;
|
|
}
|
|
else {
|
|
if (j + 1 > rs) {
|
|
free(wsgi_req->proto_parser_buf);
|
|
return -1;
|
|
}
|
|
vallen = octet;
|
|
j++;
|
|
}
|
|
|
|
if (j + (keylen + vallen) > rs) {
|
|
free(wsgi_req->proto_parser_buf);
|
|
return -1;
|
|
}
|
|
if (keylen <= 0xffff && vallen <= 0xffff) {
|
|
#ifdef UWSGI_DEBUG
|
|
uwsgi_log("keylen %d %.*s vallen %d %.*s\n", keylen, keylen, wsgi_req->proto_parser_buf + 8 + j, vallen, vallen, wsgi_req->proto_parser_buf + 8 + j + keylen);
|
|
#endif
|
|
wsgi_req->uh.pktsize += proto_base_add_uwsgi_var(wsgi_req, wsgi_req->proto_parser_buf + 8 + j, keylen, wsgi_req->proto_parser_buf + 8 + j + keylen, vallen);
|
|
}
|
|
j += (keylen + vallen) - 1;
|
|
}
|
|
}
|
|
// stdin
|
|
else if (fr->type == 5) {
|
|
if (!wsgi_req->async_post) {
|
|
wsgi_req->async_post = tmpfile();
|
|
if (!wsgi_req->async_post) {
|
|
free(wsgi_req->proto_parser_buf);
|
|
uwsgi_error("tmpfile()");
|
|
return -1;
|
|
}
|
|
}
|
|
if (!fwrite(wsgi_req->proto_parser_buf + 8, rs, 1, wsgi_req->async_post)) {
|
|
free(wsgi_req->proto_parser_buf);
|
|
uwsgi_error("fwrite()");
|
|
return -1;
|
|
}
|
|
}
|
|
wsgi_req->proto_parser_status = PROTO_STATUS_RECV_HDR;
|
|
wsgi_req->proto_parser_pos = 0;
|
|
}
|
|
}
|
|
|
|
return UWSGI_AGAIN;
|
|
}
|
|
|
|
ssize_t uwsgi_proto_fastcgi_writev_header(struct wsgi_request * wsgi_req, struct iovec * iovec, size_t iov_len) {
|
|
int i;
|
|
ssize_t len;
|
|
ssize_t ret = 0;
|
|
|
|
for (i = 0; i < (int) iov_len; i++) {
|
|
len = uwsgi_proto_fastcgi_write(wsgi_req, iovec[i].iov_base, iovec[i].iov_len);
|
|
if (len <= 0) {
|
|
return len;
|
|
}
|
|
ret += len;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
ssize_t uwsgi_proto_fastcgi_writev(struct wsgi_request * wsgi_req, struct iovec * iovec, size_t iov_len) {
|
|
return uwsgi_proto_fastcgi_writev_header(wsgi_req, iovec, iov_len);
|
|
}
|
|
|
|
ssize_t uwsgi_proto_fastcgi_write(struct wsgi_request * wsgi_req, char *buf, size_t len) {
|
|
struct fcgi_record fr;
|
|
ssize_t rlen;
|
|
size_t chunk_len;
|
|
char *ptr = buf;
|
|
|
|
// in fastcgi we need to not send 0 size frames
|
|
if (!len)
|
|
return 0;
|
|
|
|
fr.version = 1;
|
|
fr.type = 6;
|
|
fr.req1 = 0;
|
|
fr.req0 = 1;
|
|
fr.pad = 0;
|
|
fr.reserved = 0;
|
|
fr.cl = htons(len);
|
|
|
|
// split response in 64k chunks...
|
|
|
|
if (len <= 65535) {
|
|
rlen = write(wsgi_req->poll.fd, &fr, 8);
|
|
if (rlen <= 0) {
|
|
if (!uwsgi.ignore_write_errors) {
|
|
uwsgi_req_error("write()");
|
|
}
|
|
wsgi_req->write_errors++;
|
|
return 0;
|
|
}
|
|
rlen = write(wsgi_req->poll.fd, buf, len);
|
|
if (rlen <= 0) {
|
|
if (!uwsgi.ignore_write_errors) {
|
|
uwsgi_req_error("write()");
|
|
}
|
|
wsgi_req->write_errors++;
|
|
return 0;
|
|
}
|
|
return rlen;
|
|
}
|
|
else {
|
|
while(len > 0) {
|
|
chunk_len = UMIN(65535, len);
|
|
fr.cl = htons(chunk_len);
|
|
rlen = write(wsgi_req->poll.fd, &fr, 8);
|
|
if (rlen != 8) {
|
|
if (!uwsgi.ignore_write_errors) {
|
|
uwsgi_req_error("write()");
|
|
}
|
|
wsgi_req->write_errors++;
|
|
return 0;
|
|
}
|
|
rlen = write(wsgi_req->poll.fd, ptr, chunk_len);
|
|
if (rlen <= 0) {
|
|
if (!uwsgi.ignore_write_errors) {
|
|
uwsgi_req_error("write()");
|
|
}
|
|
wsgi_req->write_errors++;
|
|
return 0;
|
|
}
|
|
ptr += rlen;
|
|
len -= rlen;
|
|
}
|
|
return ptr-buf;
|
|
}
|
|
|
|
}
|
|
|
|
ssize_t uwsgi_proto_fastcgi_write_header(struct wsgi_request * wsgi_req, char *buf, size_t len) {
|
|
return uwsgi_proto_fastcgi_write(wsgi_req, buf, len);
|
|
}
|
|
|
|
void uwsgi_proto_fastcgi_close(struct wsgi_request *wsgi_req) {
|
|
|
|
if (write(wsgi_req->poll.fd, FCGI_END_REQUEST, 24) <= 0) {
|
|
uwsgi_req_error("write()");
|
|
}
|
|
|
|
uwsgi_proto_base_close(wsgi_req);
|
|
}
|
|
|
|
ssize_t uwsgi_proto_fastcgi_sendfile(struct wsgi_request *wsgi_req) {
|
|
|
|
ssize_t len;
|
|
struct fcgi_record fr;
|
|
char buf[65536];
|
|
size_t remains = wsgi_req->sendfile_fd_size - wsgi_req->sendfile_fd_pos;
|
|
|
|
wsgi_req->sendfile_fd_chunk = 65536;
|
|
|
|
fr.version = 1;
|
|
fr.type = 6;
|
|
fr.req1 = 0;
|
|
fr.req0 = 1;
|
|
fr.pad = 0;
|
|
fr.reserved = 0;
|
|
|
|
if (uwsgi.async > 1) {
|
|
fr.cl = htons(UMIN(remains, wsgi_req->sendfile_fd_chunk));
|
|
len = write(wsgi_req->poll.fd, &fr, 8);
|
|
if (len != 8) {
|
|
uwsgi_error("write()");
|
|
return -1;
|
|
}
|
|
len = read(wsgi_req->sendfile_fd, buf, ntohs(fr.cl));
|
|
if (len != (ssize_t) ntohs(fr.cl)) {
|
|
uwsgi_error("read()");
|
|
return -1;
|
|
}
|
|
wsgi_req->sendfile_fd_pos += len;
|
|
return write(wsgi_req->poll.fd, buf, len);
|
|
}
|
|
|
|
while (remains) {
|
|
fr.cl = htons(UMIN(remains, wsgi_req->sendfile_fd_chunk));
|
|
len = write(wsgi_req->poll.fd, &fr, 8);
|
|
if (len != 8) {
|
|
uwsgi_error("write()");
|
|
return -1;
|
|
}
|
|
len = read(wsgi_req->sendfile_fd, buf, ntohs(fr.cl));
|
|
if (len != (ssize_t) ntohs(fr.cl)) {
|
|
uwsgi_error("read()");
|
|
return -1;
|
|
}
|
|
wsgi_req->sendfile_fd_pos += len;
|
|
if (write(wsgi_req->poll.fd, buf, len) != len) {
|
|
uwsgi_error("write()");
|
|
return -1;
|
|
}
|
|
remains = wsgi_req->sendfile_fd_size - wsgi_req->sendfile_fd_pos;
|
|
}
|
|
|
|
return wsgi_req->sendfile_fd_pos;
|
|
|
|
}
|