PUBLIC void httpFinalizeOutput(HttpConn *conn) { HttpTx *tx; tx = conn->tx; if (!tx || tx->finalizedOutput) { return; } tx->responded = 1; tx->finalizedOutput = 1; assert(conn->writeq); if (conn->writeq == tx->queue[HTTP_QUEUE_TX]) { /* Tx Pipeline not yet created */ tx->pendingFinalize = 1; return; } assert(conn->state >= HTTP_STATE_CONNECTED); /* This may be called from httpError when the connection fails. */ if (conn->sock) { httpPutForService(conn->writeq, httpCreateEndPacket(), HTTP_SCHEDULE_QUEUE); httpServiceQueues(conn); } }
/* Write another block of data */ static ssize writeHttpData(Ejs *ejs, EjsHttp *hp) { EjsByteArray *ba; HttpConn *conn; ssize count, nbytes; conn = hp->conn; ba = hp->data; nbytes = 0; if (ba && (count = ejsGetByteArrayAvailableData(ba)) > 0) { if (conn->tx->finalizedOutput) { ejsThrowIOError(ejs, "Cannot write to socket"); return 0; } // MOB - or should this be non-blocking nbytes = httpWriteBlock(conn->writeq, (cchar*) &ba->value[ba->readPosition], count, HTTP_BLOCK); if (nbytes < 0) { ejsThrowIOError(ejs, "Cannot write to socket"); return 0; } ba->readPosition += nbytes; } httpServiceQueues(conn); return nbytes; }
/* Flush write data back to the client */ static void flushOutput(void *server_context) { HttpConn *conn; conn = (HttpConn*) server_context; if (conn) { httpServiceQueues(conn); } }
PUBLIC void httpFinalize(HttpConn *conn) { HttpTx *tx; tx = conn->tx; if (!tx || tx->finalized) { return; } tx->finalized = 1; if (!tx->finalizedOutput) { httpFinalizeOutput(conn); } else { httpServiceQueues(conn); } }
/* Read data. If sync mode, this will block. If async, will never block. Will return what data is available up to the requested size. Returns a count of bytes read. Returns zero if not data. EOF if returns zero and conn->state is > HTTP_STATE_CONTENT. */ PUBLIC ssize httpRead(HttpConn *conn, char *buf, ssize size) { HttpPacket *packet; HttpQueue *q; MprBuf *content; ssize nbytes, len; q = conn->readq; assert(q->count >= 0); assert(size >= 0); VERIFY_QUEUE(q); while (q->count <= 0 && !conn->async && !conn->error && conn->sock && (conn->state <= HTTP_STATE_CONTENT)) { httpServiceQueues(conn); if (conn->sock) { httpWait(conn, 0, MPR_TIMEOUT_NO_BUSY); } } conn->lastActivity = conn->http->now; for (nbytes = 0; size > 0 && q->count > 0; ) { if ((packet = q->first) == 0) { break; } content = packet->content; len = mprGetBufLength(content); len = min(len, size); assert(len <= q->count); if (len > 0) { len = mprGetBlockFromBuf(content, buf, len); assert(len <= q->count); } buf += len; size -= len; q->count -= len; assert(q->count >= 0); nbytes += len; if (mprGetBufLength(content) == 0) { httpGetPacket(q); } } assert(q->count >= 0); if (nbytes < size) { buf[nbytes] = '\0'; } return nbytes; }
int httpProcessHandler(HttpConn *conn) { HttpQueue *q; q = conn->writeq; if (!q->stage->writable) { return 0; } if (!conn->finalized) { q->stage->writable(q); if (q->count > 0) { httpScheduleQueue(q); httpServiceQueues(conn); } } return 1; }
/* Flush queue data by scheduling the queue and servicing all scheduled queues. Return true if there is room for more data. If blocking is requested, the call will block until the queue count falls below the queue max. WARNING: Be very careful when using blocking == true. Should only be used by end applications and not by middleware. */ PUBLIC bool httpFlushQueue(HttpQueue *q, bool blocking) { HttpConn *conn; HttpQueue *next; conn = q->conn; assert(conn->sock); do { httpScheduleQueue(q); next = q->nextQ; if (next->count > 0) { httpScheduleQueue(next); } httpServiceQueues(conn); if (conn->sock == 0) { break; } } while (blocking && q->count > 0 && !conn->tx->finalizedConnector); return (q->count < q->max) ? 1 : 0; }
/* Write a block of data. This is the lowest level write routine for data. This will buffer the data and flush if the queue buffer is full. Flushing is done by calling httpFlushQueue which will service queues as required. This may call the queue outgoing service routine and disable downstream queues if they are overfull. This routine will always accept the data and never return "short". */ PUBLIC ssize httpWriteBlock(HttpQueue *q, cchar *buf, ssize len, int flags) { HttpPacket *packet; HttpConn *conn; HttpTx *tx; ssize totalWritten, packetSize, thisWrite; assert(q == q->conn->writeq); conn = q->conn; tx = conn->tx; if (flags == 0) { flags = HTTP_BUFFER; } if (tx == 0 || tx->finalizedOutput) { return MPR_ERR_CANT_WRITE; } tx->responded = 1; for (totalWritten = 0; len > 0; ) { mprTrace(7, "httpWriteBlock q_count %d, q_max %d", q->count, q->max); if (conn->state >= HTTP_STATE_FINALIZED) { return MPR_ERR_CANT_WRITE; } if (q->last && q->last != q->first && q->last->flags & HTTP_PACKET_DATA && mprGetBufSpace(q->last->content) > 0) { packet = q->last; } else { packetSize = (tx->chunkSize > 0) ? tx->chunkSize : q->packetSize; if ((packet = httpCreateDataPacket(packetSize)) == 0) { return MPR_ERR_MEMORY; } httpPutForService(q, packet, HTTP_DELAY_SERVICE); } assert(mprGetBufSpace(packet->content) > 0); thisWrite = min(len, mprGetBufSpace(packet->content)); if (flags & (HTTP_BLOCK | HTTP_NON_BLOCK)) { thisWrite = min(thisWrite, q->max - q->count); } if (thisWrite > 0) { if ((thisWrite = mprPutBlockToBuf(packet->content, buf, thisWrite)) == 0) { return MPR_ERR_MEMORY; } buf += thisWrite; len -= thisWrite; q->count += thisWrite; totalWritten += thisWrite; } if (q->count >= q->max) { httpFlushQueue(q, 0); if (q->count >= q->max) { if (flags & HTTP_NON_BLOCK) { break; } else if (flags & HTTP_BLOCK) { while (q->count >= q->max && !tx->finalized) { if (!mprWaitForSingleIO((int) conn->sock->fd, MPR_WRITABLE, conn->limits->inactivityTimeout)) { return MPR_ERR_TIMEOUT; } httpResumeQueue(conn->connectorq); httpServiceQueues(conn); } } } } } if (conn->error) { return MPR_ERR_CANT_WRITE; } return totalWritten; }