int connection_proxy_req_parse(Connection *conn) { int rc = 0; Host *target_host = conn->req->target_host; Backend *req_action = conn->req->action; check_debug(!IOBuf_closed(conn->iob), "Client closed, goodbye."); rc = Connection_read_header(conn, conn->req); check_debug(rc > 0, "Failed to read another header."); error_unless(Request_is_http(conn->req), conn, 400, "Someone tried to change the protocol on us from HTTP."); Backend *found = Host_match_backend(target_host, Request_path(conn->req), NULL); error_unless(found, conn, 404, "Handler not found: %s", bdata(Request_path(conn->req))); // break out of PROXY if the actions don't match if(found != req_action) { Request_set_action(conn->req, found); return Connection_backend_event(found, conn); } else { return HTTP_REQ; } error_response(conn, 500, "Invalid code branch, tell Zed."); error: return REMOTE_CLOSE; }
static inline int stream_to_disk(IOBuf *iob, int content_len, int tmpfd) { char *data = NULL; int avail = 0; debug("max content length: %d, content_len: %d", MAX_CONTENT_LENGTH, content_len); IOBuf_resize(iob, MAX_CONTENT_LENGTH); // give us a good buffer size while(content_len > 0) { data = IOBuf_read_some(iob, &avail); check(!IOBuf_closed(iob), "Closed while reading from IOBuf."); content_len -= avail; check(write(tmpfd, data, avail) == avail, "Failed to write requested amount to tempfile: %d", avail); check(IOBuf_read_commit(iob, avail) != -1, "Final commit failed streaming to disk."); } check(content_len == 0, "Failed to write everything to the large upload tmpfile."); return 0; error: return -1; }
char *test_IOBuf_send_operations() { Connection *conn = fake_conn("/dev/null", O_WRONLY); mu_assert(conn != NULL, "Failed to allocate buf."); IOBuf *buf = conn->iob; mu_assert(Register_fd_exists(IOBuf_fd(buf)) != NULL, "Damn fd isn't registered."); int rc = IOBuf_send(buf, "012345789", 10); mu_assert(!IOBuf_closed(buf), "Should not be closed."); mu_assert(rc == 10, "Should have sent 10 bytes."); fdclose(IOBuf_fd(buf)); rc = IOBuf_send(buf, "012345789", 10); mu_assert(IOBuf_closed(buf), "Should be closed."); mu_assert(rc == -1, "Should send nothing."); fake_conn_close(conn); return NULL; }
/** * Does a non-blocking read and either reads in the amount you requested * with "need" or less so you can try again. It sets out_len to what is * available (<= need) so you can decide after that. You can keep attempting * to read more and more (up to buf->len) and you'll get the same start point * each time. * * Once you're done with the data you've been trying to read, you use IOBuf_read_commit * to commit the "need" amount and then start doing new reads. * * Internally this works like a "half open ring buffer" and it tries to greedily * read as much as possible without blocking or copying. * * To just get as much as possible, use the IOBuf_read_some macro. */ char *IOBuf_read(IOBuf *buf, int need, int *out_len) { int rc = 0; assert(buf->cur + buf->avail <= buf->len && "Buffer math off, cur+avail can't be more than > len."); assert(buf->cur >= 0 && "Buffer cur can't be < 0"); assert(buf->avail >= 0 && "Buffer avail can't be < 0"); assert(need <= buf->len && "Request for more than possible in the buffer."); if(IOBuf_closed(buf)) { if(buf->avail > 0) { *out_len = buf->avail; } else { *out_len = 0; return NULL; } } else if(buf->avail < need) { if(buf->cur > 0 && IOBuf_compact_needed(buf, need)) { IOBuf_compact(buf); } rc = buf->recv(buf, IOBuf_read_point(buf), IOBuf_remaining(buf)); if(rc <= 0) { debug("Socket was closed, will return only what's available: %d", buf->avail); buf->closed = 1; } else { buf->avail = buf->avail + rc; } if(buf->avail < need) { // less than expected *out_len = buf->avail; } else { // more than expected *out_len = need; } } else if(buf->avail >= need) { *out_len = need; } else { sentinel("Invalid branch processing buffer, Tell Zed."); } return IOBuf_start(buf); error: return NULL; }
/** * Reads the entire amount requested into the IOBuf (as long as there's * space to hold it) and then commits that read in one shot. */ char *IOBuf_read_all(IOBuf *buf, int len, int retries) { int nread = 0; int attempts = 0; check_debug(!IOBuf_closed(buf), "Closed when attempting to read from buffer."); if(len > buf->len) { // temporarily grow the buffer IOBuf_resize(buf, len); } char *data = IOBuf_read(buf, len, &nread); debug("INITIAL READ: len: %d, nread: %d", len, nread); for(attempts = 0; nread < len; attempts++) { data = IOBuf_read(buf, len, &nread); check_debug(data, "Read error from socket."); assert(nread <= len && "Invalid nread size (too much) on IOBuf read."); if(nread == len) { break; } else { fdwait(buf->fd, 'r'); } } debug("ATTEMPTS: %d, RETRIES: %d", attempts, retries); if(attempts > retries) { log_warn("Read of %d length attempted %d times which is over %d retry limit..", len, attempts, retries); } check(IOBuf_read_commit(buf, len) != -1, "Final commit failed of read_all."); return data; error: buf->closed = 1; return NULL; }
int Connection_read_header(Connection *conn, Request *req) { char *data = IOBuf_start(conn->iob); int avail = IOBuf_avail(conn->iob); int rc = 0; size_t nparsed = 0; int tries = 0; Request_start(req); for(tries = 0; rc == 0 && tries < CLIENT_READ_RETRIES; tries++) { if(avail > 0) { rc = Request_parse(req, data, avail, &nparsed); } if(rc == 0) { data = IOBuf_read_some(conn->iob, &avail); check_debug(!IOBuf_closed(conn->iob), "Client closed during read."); } } error_unless(tries < CLIENT_READ_RETRIES, conn, 400, "Too many small packet read attempts."); error_unless(rc == 1, conn, 400, "Error parsing request."); // add the x-forwarded-for header Request_set(conn->req, bstrcpy(&HTTP_X_FORWARDED_FOR), blk2bstr(conn->remote, IPADDR_SIZE), 1); check_should_close(conn, conn->req); return nparsed; error: return -1; }
int Connection_read_wspacket(Connection *conn) { bstring payload=NULL; uint8_t *dataU=NULL; char *data = IOBuf_start(conn->iob); int avail = IOBuf_avail(conn->iob); int64_t packet_length=-1; int smaller_packet_length; int header_length; char key[4]; int i; int data_length; int tries = 0; int rc=0; int fin; int inprogFlags=0; int isControl; int flags; again: dataU=NULL; data = IOBuf_start(conn->iob); avail = IOBuf_avail(conn->iob); packet_length=-1; smaller_packet_length=0; header_length=0; i=0; data_length=0; tries = 0; rc=0; fin=0; for(tries = 0; packet_length == -1 && tries < 8*CLIENT_READ_RETRIES; tries++) { if(avail > 0) { packet_length = Websocket_packet_length((uint8_t *)data, avail); } if(packet_length == -1) { data = IOBuf_read_some(conn->iob, &avail); check_debug(!IOBuf_closed(conn->iob), "Client closed during read."); } } check(packet_length > 0,"Error receiving websocket packet header.") check_debug(packet_length <= INT_MAX,"Websocket packet longer than MAXINT."); /* TODO properly terminate WS connection */ smaller_packet_length = (int)packet_length; /* TODO check for maximum length */ header_length=Websocket_header_length((uint8_t *) data, avail); data_length=smaller_packet_length-header_length; dataU = (uint8_t *)IOBuf_read_all(conn->iob,header_length,8*CLIENT_READ_RETRIES); memcpy(key,dataU+header_length-4,4); flags=dataU[0]; if (payload==NULL) { inprogFlags=flags; } fin=(WS_fin(dataU)); isControl=(WS_is_control(dataU)); { const char *error=WS_validate_packet(dataU,payload!=NULL); check(error==NULL,"%s",error); } dataU = (uint8_t *)IOBuf_read_all(conn->iob,data_length, 8*CLIENT_READ_RETRIES); check(dataU != NULL, "Client closed the connection during websocket packet."); for(i=0;i<data_length;++i) { dataU[i]^=key[i%4]; } if(isControl) /* Control frames get sent right-away */ { Request_set(conn->req,bfromcstr("FLAGS"),bformat("0x%X",flags|0x80),1); rc = Connection_send_to_handler(conn, conn->handler, (void *)dataU,data_length); check_debug(rc == 0, "Failed to deliver to the handler."); } else { if(fin) { Request_set(conn->req,bfromcstr("FLAGS"),bformat("0x%X",inprogFlags|0x80),1); } if (payload == NULL) { if (fin) { rc = Connection_send_to_handler(conn, conn->handler, (void *)dataU,data_length); check_debug(rc == 0, "Failed to deliver to the handler."); } else { payload = blk2bstr(dataU,data_length); check(payload != NULL,"Allocation failed"); } } else { check(BSTR_OK == bcatblk(payload,dataU,data_length), "Concatenation failed"); if (fin) { rc = Connection_send_to_handler(conn, conn->handler, bdata(payload),blength(payload)); check_debug(rc == 0, "Failed to deliver to the handler."); bdestroy(payload); payload=NULL; } } } if (payload != NULL) { goto again; } return packet_length; error: bdestroy(payload); return -1; }
/** * Reads the entire amount requested into the IOBuf (as long as there's * space to hold it) and then commits that read in one shot. */ char *IOBuf_read_all(IOBuf *buf, int len, int retries) { int nread = 0; int attempts = 0; check_debug(!IOBuf_closed(buf), "Closed when attempting to read from buffer."); if(len > buf->len) { // temporarily grow the buffer IOBuf_resize(buf, len); } char *data = IOBuf_read(buf, len, &nread); debug("INITIAL READ: len: %d, nread: %d", len, nread); for(attempts = 0; nread < len; attempts++) { data = IOBuf_read(buf, len, &nread); check_debug(data, "Read error from socket."); assert(nread <= len && "Invalid nread size (too much) on IOBuf read."); if(nread == len) { log_warn("recive all data"); break; } else if(IOBuf_closed(buf)){//register closed or socket error if(buf->buf) free(buf->buf); buf->buf = strdup("upload=false&info='connection error'"); buf->len = strlen(buf->buf); buf->avail = buf->len; buf->cur = buf->len; log_warn("connection error"); len = buf->len; goto error; /*if(buf->buf) free(buf->buf); buf->buf = NULL; buf->len = 0; buf->avail = 0; buf->cur = 0; log_warn("connection error"); goto error; */ } else { fdwait(buf->fd, 'r'); } } debug("ATTEMPTS: %d, RETRIES: %d", attempts, retries); if(attempts > retries) { log_warn("Read of %d length attempted %d times which is over %d retry limit..", len, attempts, retries); } check(IOBuf_read_commit(buf, len) != -1, "Final commit failed of read_all."); return data; error: buf->closed = 1; return NULL; }
int Upload_stream(Connection *conn, Handler *handler, int content_len) { char *data = NULL; int avail = 0; int offset = 0; int first_chunk = 1; int rc; hash_t *altheaders = NULL; bstring offsetstr; debug("max content length: %d, content_len: %d", MAX_CONTENT_LENGTH, content_len); IOBuf_resize(conn->iob, MAX_CONTENT_LENGTH); // give us a good buffer size while(content_len > 0) { if(first_chunk) { // read whatever's there data = IOBuf_read_some(conn->iob, &avail); } else if(conn->sendCredits > 0) { // read up to credits data = IOBuf_read(conn->iob, conn->sendCredits < content_len ? conn->sendCredits : content_len, &avail); conn->sendCredits -= avail; } else { // sleep until we have credits tasksleep(&conn->uploadRendez); continue; } check(!IOBuf_closed(conn->iob), "Closed while reading from IOBuf."); content_len -= avail; offsetstr = bformat("%d", offset); if(first_chunk) { Request_set(conn->req, &UPLOAD_STREAM, offsetstr, 1); if(content_len == 0) { Request_set(conn->req, &UPLOAD_STREAM_DONE, bfromcstr("1"), 1); } } else { altheaders = hash_create(2, (hash_comp_t)bstrcmp, bstr_hash_fun); add_to_hash(altheaders, &UPLOAD_STREAM, offsetstr); if(content_len == 0) { add_to_hash(altheaders, &UPLOAD_STREAM_DONE, bfromcstr("1")); } } rc = Connection_send_to_handler(conn, handler, data, avail, altheaders); check_debug(rc == 0, "Failed to deliver to the handler."); if(altheaders != NULL) { hash_free_nodes(altheaders); hash_destroy(altheaders); altheaders = NULL; } check(IOBuf_read_commit(conn->iob, avail) != -1, "Commit failed while streaming."); first_chunk = 0; offset += avail; } check(content_len == 0, "Failed to write everything to the large upload tmpfile."); return 0; error: return -1; }