write.c 3.69 KB
/**
 * \file
 *
 * \author	Georg Hopp
 *
 * \copyright
 * Copyright (C) 2012  Georg Hopp
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <unistd.h>
#include <sys/stat.h>

#include "class.h"
#include "interface/class.h"
#include "http/message.h"
#include "http/response.h"
#include "http/response/writer.h"
#include "cbuf.h"

#define MIN(x,y)	((x) < (y) ? (x) : (y))
#define MAX(x,y)	((x) > (y) ? (x) : (y))
#define _PSIZE(x)	(MAX((x),RESPONSE_WRITER_MAX_BUF))
#define PSIZE		_PSIZE(this->nheader+message->nbody)

ssize_t
httpResponseWriterWrite(HttpResponseWriter this, int fd)
{
	HttpMessageQueue respq     = this->response_queue;
	HttpMessage      message   = (HttpMessage)this->cur_response;
	int              cont      = 1;

	if (cbufIsLocked(this->buffer)) {
		if (FALSE == this->ourLock)
			return 0;
	}
	else {
		cbufLock(this->buffer);
		this->ourLock = TRUE;
	}

	while (cont) {
		switch (this->state) {
			case HTTP_RESPONSE_GET:
				if (NULL == this->cur_response && 0 < respq->nmsgs) {
					message            = respq->msgs[0];
					this->cur_response = (HttpResponse)message;

					this->written = 0;
					this->nbody   = 0;
					this->nheader = httpMessageHeaderSizeGet(message);

					httpMessageHeaderToString(message, cbufGetWrite(this->buffer));
					cbufIncWrite(this->buffer, this->nheader);

					this->state = HTTP_RESPONSE_WRITE;
				}
				else {
					cbufRelease(this->buffer);
					this->ourLock = FALSE;
					cont          = 0;
				}
				break;

			case HTTP_RESPONSE_WRITE:
				/**
				 * read
				 */
				if (this->nbody < message->nbody) {
					size_t size = MIN(
							message->nbody - this->nbody,
							cbufGetFree(this->buffer));

					switch (message->type) {
						case HTTP_MESSAGE_BUFFERED:
							cbufSetData(this->buffer,
									message->body + this->nbody,
									size);
							break;

						case HTTP_MESSAGE_PIPED:
							size = cbufRead(this->buffer, message->handle);
							break;

						default:
							return -1;
					}

					this->nbody += size;
				}

				/**
				 * write
				 */
				{
					ssize_t written = cbufWrite(this->buffer, fd);

					if (0 <= written) {
						this->written += written;
					}
					else {
						return -1;
					}
				}

				if (this->written == message->nbody + this->nheader) {
					this->state = HTTP_RESPONSE_DONE;
				}
				else {
					cont = 0;
				}
				break;

			case HTTP_RESPONSE_DONE:
				if (HTTP_MESSAGE_PIPED == message->type) {
					close(message->handle);
				}

	 			this->state = HTTP_RESPONSE_GET;

				memmove(respq->msgs,
						&(respq->msgs[1]),
						sizeof(void*) * (--respq->nmsgs + 1));

				if (! httpMessageHasKeepAlive(message)) {
					/**
					 * if the message did not have the keep-alive feature
					 * we don't care about further pipelined messages and
					 * return to the caller with a -1 indicating that the
					 * underlying connection should be closed.
					 */
					cbufRelease(this->buffer);
					this->ourLock = FALSE;
					delete(this->cur_response);
					return -1;
				}

				cbufRelease(this->buffer);
				this->ourLock = FALSE;
				delete(this->cur_response);

				break;
		}
	}

	return respq->nmsgs;
}

// vim: set ts=4 sw=4: