diff --git a/include/http/message.h b/include/http/message.h index fa89b34..a70abb7 100644 --- a/include/http/message.h +++ b/include/http/message.h @@ -21,7 +21,9 @@ CLASS(HttpMessage) { int nbody; }; -char httpMessageHasKeepAlive(HttpMessage); +char httpMessageHasKeepAlive(HttpMessage); +size_t httpMessageHeaderSizeGet(HttpMessage); +char * httpMessageHeaderToString(HttpMessage, char *); #endif // __HTTP_MESSAGE__ diff --git a/include/http/response.h b/include/http/response.h index fadb539..422a124 100644 --- a/include/http/response.h +++ b/include/http/response.h @@ -15,10 +15,10 @@ CLASS(HttpResponse) { }; HttpResponse httpResponse404(); +HttpResponse httpResponseMe(); +HttpResponse httpResponseImage(); -void httpResponseHeaderSet(HttpResponse, const char *, const char *); -size_t httpResponseSizeGet(HttpResponse); -char * httpResponseToString(HttpResponse, char *); +//void httpResponseHeaderSet(HttpResponse, const char *, const char *); #endif /* __HTTP_RESPONSE_H__ */ diff --git a/include/http/response/writer.h b/include/http/response/writer.h index cfec228..c1813f2 100644 --- a/include/http/response/writer.h +++ b/include/http/response/writer.h @@ -6,8 +6,8 @@ #include "http/message/queue.h" typedef enum e_HttpResponseState { - HTTP_RESPONSE_NO=0, - HTTP_RESPONSE_START, + HTTP_RESPONSE_GET=0, + HTTP_RESPONSE_HEADER, HTTP_RESPONSE_PIPE, HTTP_RESPONSE_DONE } HttpResponseState; @@ -17,9 +17,9 @@ CLASS(HttpResponseWriter) { char pipe[1024]; size_t nbuffer; - size_t rpipe; - size_t wpipe; - char pipe_flip; + size_t written; + size_t pstart; + size_t pend; HttpMessageQueue response_queue; HttpResponse cur_response; diff --git a/src/Makefile.am b/src/Makefile.am index 0275541..8327aeb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,15 +2,18 @@ ACLOCAL_AMFLAGS = -I m4 AUTOMAKE_OPTIONS = subdir-objects IFACE = interface/class.c interface/stream_reader.c interface/logger.c \ - interface/stream_writer.c + interface/stream_writer.c interface/http_intro.c CLASS = class.c interface.c SOCKET = socket.c socket/accept.c socket/connect.c socket/listen.c SERVER = server.c server/run.c server/close_conn.c LOGGER = logger.c logger/stderr.c logger/syslog.c -MSG = http/message.c http/message/queue.c http/message/has_keep_alive.c +MSG = http/message.c http/message/queue.c http/message/has_keep_alive.c \ + http/message/header_size_get.c http/message/header_to_string.c REQ = http/request.c -RESP = http/response.c http/response/404.c http/response/size_get.c \ - http/response/to_string.c +RESP = http/response.c \ + http/response/404.c \ + http/response/image.c \ + http/response/me.c WRITER = http/response/writer.c http/response/writer/write.c HEADER = http/header.c http/header/get.c http/header/add.c \ http/header/size_get.c http/header/to_string.c diff --git a/src/http/response/size_get.c b/src/http/message/header_size_get.c similarity index 61% rename from src/http/response/size_get.c rename to src/http/message/header_size_get.c index 5f3cfaa..1eaefd6 100644 --- a/src/http/response/size_get.c +++ b/src/http/message/header_size_get.c @@ -5,6 +5,7 @@ #include "http/message.h" #include "http/response.h" #include "http/header.h" +#include "interface/http_intro.h" static size_t size; @@ -19,24 +20,13 @@ addHeaderSize(const void * node, const VISIT which, const int depth) } size_t -httpResponseSizeGet(HttpResponse response) +httpMessageHeaderSizeGet(HttpMessage message) { - HttpMessage message = (HttpMessage)response; - - size = 0; - - size += strlen(message->version) + 1; - size += 4; // for status - size += strlen(response->reason) + 2; + size = httpIntroSizeGet(message); twalk(message->header, addHeaderSize); - size += 2; - if (HTTP_MESSAGE_BUFFERED == message->type) { - size += message->nbody; - } - return size; } diff --git a/src/http/response/to_string.c b/src/http/message/header_to_string.c similarity index 51% rename from src/http/response/to_string.c rename to src/http/message/header_to_string.c index 2f4571d..b3a7979 100644 --- a/src/http/response/to_string.c +++ b/src/http/message/header_to_string.c @@ -4,6 +4,7 @@ #include "http/response.h" #include "http/header.h" +#include "interface/http_intro.h" static char * string; @@ -18,40 +19,17 @@ addHeaderString(const void * node, const VISIT which, const int depth) } char * -httpResponseToString(HttpResponse response, char * _string) +httpMessageHeaderToString(HttpMessage response, char * _string) { HttpMessage message = (HttpMessage)response; - char status[4]; - string = _string; - - snprintf(status, 4, "%d", response->status); - - strcpy(string, message->version); - string += strlen(string); - - *string++ = ' '; - - strcpy(string, status); - string += strlen(string); - - *string++ = ' '; - - strcpy(string, response->reason); - string += strlen(string); - - *string++ = '\r'; - *string++ = '\n'; + string = httpIntroToString(response, _string); twalk(message->header, addHeaderString); *string++ = '\r'; *string++ = '\n'; - if (HTTP_MESSAGE_BUFFERED == message->type) { - memcpy(string, message->body, message->nbody); - } - return string; } diff --git a/src/http/request.c b/src/http/request.c index 57a44fa..c7f93f1 100644 --- a/src/http/request.c +++ b/src/http/request.c @@ -1,8 +1,11 @@ #include #include +#include +#include #include "class.h" #include "interface/class.h" +#include "interface/http_intro.h" #include "http/request.h" #include "message/helper.c" @@ -24,7 +27,47 @@ dtor(void * _this) PARENTCALL(_this, Class, dtor); } +static +size_t +sizeGet(void * _this) +{ + HttpRequest this = _this; + size_t size = 0; + + size += strlen(this->method) + 1; + size += strlen(this->uri) + 1; + size += strlen(((HttpMessage)this)->version) + 2; + + return size; +} + +static +char * +toString(void * _this, char * string) +{ + HttpRequest this = _this; + + strcpy(string, this->method); + string += strlen(string); + *string++ = ' '; + + strcpy(string, this->uri); + string += strlen(string); + *string++ = ' '; + + strcpy(string, ((HttpMessage)this)->version); + string += strlen(string); + *string++ = '\r'; + *string++ = '\n'; + + return string; +} + INIT_IFACE(Class, ctor, dtor, NULL); -CREATE_CLASS(HttpRequest, HttpMessage, IFACE(Class)); +INIT_IFACE(HttpIntro, sizeGet, toString); +CREATE_CLASS(HttpRequest, + HttpMessage, + IFACE(Class), + IFACE(HttpIntro)); // vim: set ts=4 sw=4: diff --git a/src/http/response.c b/src/http/response.c index a972dcd..6f9e8d9 100644 --- a/src/http/response.c +++ b/src/http/response.c @@ -1,8 +1,12 @@ #include #include +#include +#include +#include #include "class.h" #include "interface/class.h" +#include "interface/http_intro.h" #include "http/response.h" #include "message/helper.c" @@ -35,7 +39,48 @@ dtor(void * _this) PARENTCALL(_this, Class, dtor); } +static +size_t +sizeGet(void * _this) +{ + HttpResponse this = _this; + size_t size = 0; + + size += strlen(((HttpMessage)this)->version) + 1; + size += 3 + 1; // for status + size += strlen(this->reason) + 2; + + return size; +} + +static +char * +toString(void * _this, char * string) +{ + HttpResponse this = _this; + + strcpy(string, ((HttpMessage)this)->version); + string += strlen(string); + *string++ = ' '; + + snprintf(string, 4, "%d", this->status); + string += strlen(string); + *string++ = ' '; + + strcpy(string, this->reason); + string += strlen(string); + *string++ = '\r'; + *string++ = '\n'; + + return string; +} + INIT_IFACE(Class, ctor, dtor, NULL); -CREATE_CLASS(HttpResponse, HttpMessage, IFACE(Class)); +INIT_IFACE(HttpIntro, sizeGet, toString); +CREATE_CLASS( + HttpResponse, + HttpMessage, + IFACE(Class), + IFACE(HttpIntro)); // vim: set ts=4 sw=4: diff --git a/src/http/response/image.c b/src/http/response/image.c new file mode 100644 index 0000000..9d2b1d8 --- /dev/null +++ b/src/http/response/image.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include + +#include "class.h" +#include "interface/class.h" + +#include "http/response.h" +#include "http/message.h" +#include "http/header.h" + + +HttpResponse +httpResponseImage() +{ + time_t t; + struct tm * tmp; + char buffer[200]; + struct stat st; + HttpResponse response; + HttpMessage message; + + response = new(HttpResponse, "HTTP/1.1", 200, "OK"); + message = (HttpMessage)response; + + httpHeaderAdd(&(message->header), + new(HttpHeader, "Content-Type", "image/jpeg")); + httpHeaderAdd(&(message->header), + new(HttpHeader, "Server", "testserver")); + + message->type = HTTP_MESSAGE_PIPED; + message->handle = open("./assets/waldschrat.jpg", O_RDONLY); + fstat(message->handle, &st); + message->nbody = st.st_size; + + sprintf(buffer, "%d", message->nbody); + httpHeaderAdd(&(message->header), + new(HttpHeader, "Content-Length", buffer)); + + t = time(NULL); + tmp = localtime(&t); + strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %Z", tmp); + httpHeaderAdd(&(message->header), + new(HttpHeader, "Date", buffer)); + + return response; +} + +// vim: set ts=4 sw=4: diff --git a/src/http/response/me.c b/src/http/response/me.c new file mode 100644 index 0000000..109008a --- /dev/null +++ b/src/http/response/me.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +#include "class.h" +#include "interface/class.h" + +#include "http/response.h" +#include "http/message.h" +#include "http/header.h" + + +#define RESP_DATA "\n" \ + "\n" \ + "\n" \ + "200 - OK" \ + "

200 - OK

" \ + "" + + +HttpResponse +httpResponseMe() +{ + time_t t; + struct tm * tmp; + char buffer[200]; + HttpResponse response; + HttpMessage message; + + response = new(HttpResponse, "HTTP/1.1", 200, "OK"); + message = (HttpMessage)response; + + httpHeaderAdd(&(message->header), + new(HttpHeader, "Content-Type", "text/html")); + httpHeaderAdd(&(message->header), + new(HttpHeader, "Server", "testserver")); + + message->type = HTTP_MESSAGE_BUFFERED; + message->nbody = sizeof(RESP_DATA) - 1; + message->body = calloc(1, sizeof(RESP_DATA)); + strcpy(message->body, RESP_DATA); + + sprintf(buffer, "%d", message->nbody); + httpHeaderAdd(&(message->header), + new(HttpHeader, "Content-Length", buffer)); + + t = time(NULL); + tmp = localtime(&t); + strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %Z", tmp); + httpHeaderAdd(&(message->header), + new(HttpHeader, "Date", buffer)); + + return response; +} + +// vim: set ts=4 sw=4: diff --git a/src/http/response/writer/write.c b/src/http/response/writer/write.c index bf4fd8d..7aeafcc 100644 --- a/src/http/response/writer/write.c +++ b/src/http/response/writer/write.c @@ -8,6 +8,7 @@ #include "class.h" #include "interface/class.h" +#include "http/message.h" #include "http/response.h" #include "http/response/writer.h" @@ -15,98 +16,124 @@ HttpResponse httpResponseWriterWrite(HttpResponseWriter this, int fd) { HttpMessageQueue respq = this->response_queue; + HttpResponse retval = this->cur_response; + HttpMessage message = (HttpMessage)this->cur_response; int cont = 1; - HttpResponse retval = NULL; while (cont) { switch (this->state) { - case HTTP_RESPONSE_NO: + case HTTP_RESPONSE_GET: if (NULL == this->cur_response && 0 < respq->nmsgs) { - retval = this->cur_response = (HttpResponse)respq->msgs[0]; + message = respq->msgs[0]; + retval = this->cur_response = (HttpResponse)message; + memmove(respq->msgs, &(respq->msgs[1]), sizeof(void*) * (--respq->nmsgs + 1)); - this->state = HTTP_RESPONSE_START; + + this->nbuffer = httpMessageHeaderSizeGet(message); + this->buffer = malloc(this->nbuffer); + this->written = 0; + + httpMessageHeaderToString(message, this->buffer); + + this->state = HTTP_RESPONSE_HEADER; } else { cont = 0; } break; - case HTTP_RESPONSE_START: - if (HTTP_MESSAGE_PIPED == - ((HttpMessage)(this->cur_response))->type) { - struct stat st; - HttpMessage message = (HttpMessage)this->cur_response; - char buffer[200]; - - message->handle = - open("./assets/waldschrat.jpg", O_RDONLY); - fstat(message->handle, &st); - message->nbody = st.st_size; - - sprintf(buffer, "%d", message->nbody); - httpHeaderAdd(&(message->header), - new(HttpHeader, "Content-Length", buffer)); - + case HTTP_RESPONSE_HEADER: + this->written += write( + fd, + &(this->buffer[this->written]), + this->nbuffer - this->written); + + if (this->written == this->nbuffer) { + free(this->buffer); + this->buffer = NULL; + this->nbuffer = 0; + this->written = 0; + this->pstart = 0; + this->pend = 0; + + this->state = HTTP_RESPONSE_PIPE; + } + else { + cont = 0; } - - this->state = HTTP_RESPONSE_PIPE; break; case HTTP_RESPONSE_PIPE: - { - HttpMessage message = (HttpMessage)(this->cur_response); - size_t headsize = httpResponseSizeGet(this->cur_response); + switch (message->type) { + case HTTP_MESSAGE_BUFFERED: + this->written += write( + fd, + &(message->body[this->written]), + message->nbody - this->written); + break; + + case HTTP_MESSAGE_PIPED: + /** + * read + */ + if (this->nbuffer < message->nbody) { + size_t rsize; + size_t temp; + + this->pend = (1024 == this->pend)? + 0 : this->pend; + + rsize = (this->pstart <= this->pend)? + 1024 - this->pend : this->pstart - 1; + + temp = read( + message->handle, + &(this->pipe[this->pend]), + rsize); + + this->nbuffer += temp; + this->pend += temp; + } - this->buffer = malloc(headsize + message->nbody); - httpResponseToString(this->cur_response, this->buffer); - this->rpipe = headsize; + /** + * write + */ + { + size_t wsize; + size_t temp; - if (HTTP_MESSAGE_PIPED == message->type && - 0 != message->handle) { - char * data = &(this->buffer[headsize]); - size_t togo = message->nbody; + wsize = (this->pstart <= this->pend)? + this->pend - this->pstart : 1024 - this->pstart; - size_t rsize = read(message->handle, data, togo); + temp = write(fd, &(this->pipe[this->pstart]), wsize); - while (rsize < togo) { - data += rsize; - togo -= rsize; - rsize = read(message->handle, data, togo); + this->written += temp; + this->pstart += temp; } + break; - this->wpipe = 0; - this->rpipe += message->nbody; - close(message->handle); - message->handle = 0; - } - - { - char * data = &(this->buffer[this->wpipe]); - size_t written; - - written = write(fd, data, this->rpipe - this->wpipe); - data += written; - this->wpipe += written; - - if (this->rpipe == this->wpipe) { - this->rpipe = 0; - this->wpipe = 0; - free (this->buffer); - this->buffer = NULL; - this->state = HTTP_RESPONSE_DONE; - } - else { - cont = 0; - } - } + default: + break; + } + + if (this->written == message->nbody) { + this->nbuffer = 0; + this->written = 0; + this->pstart = 0; + this->pend = 0; + + this->state = HTTP_RESPONSE_DONE; + } + else { + cont = 0; } break; case HTTP_RESPONSE_DONE: - this->state = HTTP_RESPONSE_NO; this->cur_response = NULL; + this->state = HTTP_RESPONSE_GET; cont = 0; break; diff --git a/src/interface/http_intro.c b/src/interface/http_intro.c new file mode 100644 index 0000000..fc13842 --- /dev/null +++ b/src/interface/http_intro.c @@ -0,0 +1,29 @@ +#include "class.h" +#include "interface/http_intro.h" + +const struct interface i_HttpIntro = { + "httpIntro", + 2 +}; + +size_t +httpIntroSizeGet(void * object) +{ + size_t ret; + + RETCALL(object, HttpIntro, sizeGet, ret); + + return ret; +} + +char * +httpIntroToString(void * object, char * string) +{ + char * ret; + + RETCALL(object, HttpIntro, toString, ret, string); + + return ret; +} + +// vim: set ts=4 sw=4: diff --git a/src/server/run.c b/src/server/run.c index 9870719..09dcfe8 100644 --- a/src/server/run.c +++ b/src/server/run.c @@ -122,62 +122,11 @@ serverRun(Server this) if (0 == strcmp("GET", request->method) && 0 == strcmp("/me/", request->uri)) { - const char foo[] = - "\n" - "\n" - "\n" - "200 - OK" - "

200 - OK

" - "" - ""; - char buffer[200]; - time_t t; - struct tm * tmp; - - response = (HttpMessage)new(HttpResponse, "HTTP/1.1", 200, "OK"); - - httpHeaderAdd(&(response->header), - new(HttpHeader, "Content-Type", "text/html")); - httpHeaderAdd(&(response->header), - new(HttpHeader, "Server", "testserver")); - - response->type = HTTP_MESSAGE_BUFFERED; - response->nbody = sizeof(foo) - 1; - response->body = calloc(1, sizeof(foo)); - strcpy(response->body, foo); - - sprintf(buffer, "%d", response->nbody); - httpHeaderAdd(&(response->header), - new(HttpHeader, "Content-Length", buffer)); - - t = time(NULL); - tmp = localtime(&t); - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %Z", tmp); - httpHeaderAdd(&(response->header), - new(HttpHeader, "Date", buffer)); + response = (HttpMessage)httpResponseMe(); } else if (0 == strcmp("GET", request->method) && 0 == strcmp("/image/", request->uri)) { - char buffer[200]; - time_t t; - struct tm * tmp; - - response = (HttpMessage)new( - HttpResponse, "HTTP/1.1", 200, "OK"); - - httpHeaderAdd(&(response->header), - new(HttpHeader, "Content-Type", "image/jpeg")); - httpHeaderAdd(&(response->header), - new(HttpHeader, "Server", "testserver")); - - response->type = HTTP_MESSAGE_PIPED; - - t = time(NULL); - tmp = localtime(&t); - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %Z", tmp); - httpHeaderAdd(&(response->header), - new(HttpHeader, "Date", buffer)); + response = (HttpMessage)httpResponseImage(); } else { response = (HttpMessage)httpResponse404(); @@ -217,25 +166,17 @@ serverRun(Server this) events--; nwrites--; - message = (HttpMessage)streamWriterWrite(writer, fd); + message = streamWriterWrite(writer, fd); - while (NULL != message) { - if (writer->state == HTTP_RESPONSE_NO) { - if (httpMessageHasKeepAlive(message)) { - delete(&message); - if (0 == writer->response_queue->nmsgs) { - (this->fds)[i].events &= ~POLLOUT; - break; - } - } - else { - delete(&message); - serverCloseConn(this, i); - break; - } + if (NULL != message && writer->state == HTTP_RESPONSE_GET) { + if (httpMessageHasKeepAlive(message)) { + (this->fds)[i].events &= ~POLLOUT; + } + else { + serverCloseConn(this, i); } - message = (HttpMessage)streamWriterWrite(writer, fd); + delete(&message); } } }