Browse Source

try to use memory mapped io for asset access, but this one leaks like a rotten barge

release0.1.5
Georg Hopp 12 years ago
parent
commit
d235aaef32
  1. 2
      assets/html/main.html
  2. 1
      configure.ac
  3. 9
      include/asset.h
  4. 3
      include/http/message.h
  5. 2
      include/http/worker.h
  6. 8
      src/Makefile.am
  7. 6
      src/asset/Makefile.am
  8. 19
      src/asset/asset.c
  9. 88
      src/asset/pool.c
  10. 4
      src/cbuf/write.c
  11. 2
      src/hash/delete.c
  12. 4
      src/http/Makefile.am
  13. 16
      src/http/message.c
  14. 65
      src/http/response/500.c
  15. 61
      src/http/response/asset.c
  16. 46
      src/http/worker.c
  17. 32
      src/http/worker/get_asset.c
  18. 42
      src/http/worker/get_mime_type.c
  19. 33
      src/http/worker/process.c
  20. 22
      src/http/writer/write.c
  21. 6
      src/server/read.c
  22. 1
      src/server/run.c
  23. 5
      src/server/server.c
  24. 47
      src/server/write.c
  25. 2
      src/taskrambler.c

2
assets/html/main.html

@ -40,7 +40,7 @@
<div id="main">
<h1>Testpage</h1>
Welcome<span></span>!!!<br />
<img src="/image/waldschrat.jpg" />
<a href="/foo.html"><img src="/image/waldschrat.jpg" /></a>
</div>
<hr />
<div id="msg"></div>

1
configure.ac

@ -51,6 +51,7 @@ AC_CHECK_FUNCS([memset])
AC_CONFIG_FILES([Makefile
src/Makefile
src/asset/Makefile
src/auth/Makefile
src/cbuf/Makefile
src/class/Makefile

9
include/asset.h

@ -41,13 +41,18 @@ CLASS(Asset) {
size_t nmtime;
char * mime_type;
size_t size;
size_t nmime_type;
int handle;
char * data;
size_t size;
size_t ref_count;
};
char * assetDataAt(size_t);
Asset assetPoolGet(const char *, size_t);
size_t assetPoolRelease(Asset);
void assetPoolCleanup(void);
#endif // __ASSET_H__

3
include/http/message.h

@ -27,13 +27,16 @@
#include "class.h"
#include "hash.h"
#include "stream.h"
#include "asset.h"
CLASS(HttpMessage) {
char * version;
Hash header;
Asset asset;
char * body;
int nbody;
int dbody;
};

2
include/http/worker.h

@ -49,7 +49,7 @@ CLASS(HttpWorker) {
Cbuf pbuf;
Cbuf wbuf;
Hash mime_types;
Hash asset_pool;
HttpParser parser;
HttpWriter writer;

8
src/Makefile.am

@ -2,16 +2,19 @@ ACLOCAL_AMFLAGS = -I m4
IFACE = interface/subject.c \
interface/observer.c
UTILS = utils/hash.c \
utils/memory.c \
utils/http.c \
utils/daemonize.c \
utils/signalHandling.c
utils/signalHandling.c \
utils/mime_type.c
LIBS = ./http/libhttp.a \
./auth/libauth.a \
./cbuf/libcbuf.a \
./class/libclass.a \
./asset/libasset.a \
./hash/libhash.a \
./queue/libqueue.a \
./logger/liblogger.a \
@ -29,4 +32,5 @@ taskrambler_CFLAGS = $(CFLAGS) -Wall -DPWD=\"$(PWD)\" -I ../include/# $(COVERAG
taskrambler_LDADD = $(LIBS) -lrt -lssl -lldap
#taskrambler_LDFLAGS = $(COVERAGE_LDFLAGS)
SUBDIRS = auth cbuf class hash queue http logger server session socket stream
SUBDIRS = asset auth cbuf class hash queue http \
logger server session socket stream

6
src/asset/Makefile.am

@ -0,0 +1,6 @@
ACLOCAL_AMFLAGS = -I m4
noinst_LIBRARIES = libasset.a
libasset_a_SOURCES = asset.c pool.c
libasset_a_CFLAGS = $(CFLAGS) -Wall -I ../../include/

19
src/asset/asset.c

@ -59,7 +59,7 @@ assetCtor(void * _this, va_list * params)
this->fname[2048] = '\0';
if (-1 == access(this->fname, O_RDONLY)) {
this->handle = -1;
return -1;
} else {
this->handle = open(this->fname, O_RDONLY);
fstat(this->handle, &st);
@ -80,17 +80,34 @@ assetCtor(void * _this, va_list * params)
this->mime_type = "application/octet-stream";
}
if (NULL != this->mime_type) {
this->nmime_type = strlen(this->mime_type);
} else {
this->nmime_type = 0;
}
this->data = mmap(
NULL, this->size, PROT_READ, MAP_PRIVATE, this->handle, 0);
if (MAP_FAILED == this->data) {
return -1;
}
this->ref_count = 1;
return 0;
}
static void assetDtor(void * _this) {
Asset this = _this;
if (MAP_FAILED != this->data && NULL != this->data) {
munmap(this->data, this->size);
}
if (0 != this->handle) {
close(this->handle);
}
}
INIT_IFACE(Class, assetCtor, assetDtor, NULL);

88
src/asset/pool.c

@ -0,0 +1,88 @@
/**
* \file
*
* \author Georg Hopp
*
* \copyright
* Copyright © 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/>.
*/
// for size_t
#include <sys/types.h>
// for strlen
#include "class.h"
#include "asset.h"
#include "hash.h"
Hash asset_pool = NULL;
static
inline
void
freeAsset(const void * _node)
{
Asset node = (Asset)_node;
delete(node);
}
Asset
assetPoolGet(const char * path, size_t npath)
{
Asset asset = NULL;
if (NULL == asset_pool) {
asset_pool = new(Hash);
} else {
asset = hashGet(asset_pool, path, npath);
}
if (NULL == asset) {
asset = new(Asset, path, npath);
hashAdd(asset_pool,
new(HashValue, path, npath, asset, sizeof(Asset)));
} else {
asset->ref_count++;
}
return asset;
}
size_t
assetPoolRelease(Asset asset)
{
if (asset->ref_count <= 1) {
hashDelete(asset_pool, asset->fname, asset->nfname);
delete(asset);
return 0;
} else {
asset->ref_count--;
return asset->ref_count;
}
}
void
assetPoolCleanup(void)
{
hashEach(asset_pool, freeAsset);
}
// vim: set ts=4 sw=4:

4
src/cbuf/write.c

@ -38,6 +38,10 @@ cbufWrite(Cbuf this, Stream st)
wwsize = streamWrite(st, cbufGetRead(this), wsize);
switch (wwsize) {
case 0:
wwsize = -2;
// DROP THROUGH
case -1:
break;

2
src/hash/delete.c

@ -38,7 +38,7 @@ void *
hashDelete(Hash this, const char * search, size_t nsearch)
{
unsigned long hash = sdbm((const unsigned char *)search, nsearch);
void * found = tfind(&hash, &(this->root), hashDeleteComp);
void * found = tdelete(&hash, &(this->root), hashDeleteComp);
return (NULL != found)? *(void**)found : NULL;
}

4
src/http/Makefile.am

@ -12,6 +12,7 @@ RESP = response.c \
response/304.c \
response/404.c \
response/403.c \
response/500.c \
response/login_form.c \
response/asset.c \
response/randval.c \
@ -29,8 +30,7 @@ WORKER = worker.c \
worker/process.c \
worker/answer.c \
worker/get_asset.c \
worker/add_common_header.c \
worker/get_mime_type.c
worker/add_common_header.c
HEADER = header.c \
header/to_string.c

16
src/http/message.c

@ -61,20 +61,10 @@ httpMessageDtor(void * _this)
MEM_FREE(this->version);
switch (this->type) {
case HTTP_MESSAGE_BUFFERED:
if (NULL == this->asset) {
MEM_FREE(this->body);
break;
case HTTP_MESSAGE_PIPED:
if (NULL != this->handle && 2 < (this->handle->handle).fd) {
close((this->handle->handle).fd);
}
delete(this->handle);
break;
default:
break;
} else {
assetPoolRelease(this->asset);
}
}

65
src/http/response/500.c

@ -0,0 +1,65 @@
/**
* \file
*
* \author Georg Hopp
*
* \copyright
* Copyright © 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include "class.h"
#include "http/response.h"
#include "http/message.h"
#include "http/header.h"
#include "utils/memory.h"
#include "hash.h"
#define RESP_DATA "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" \
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" \
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" \
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" \
"<head><title>500 - Internal Server Error</title></head>" \
"<body><h1>500 - Internal Server Error</h1></body>" \
"</html>"
HttpResponse
httpResponse500()
{
HttpResponse response;
HttpMessage message;
response = new(HttpResponse, "HTTP/1.1", 500, "Internal Server Error");
message = (HttpMessage)response;
hashAdd(message->header,
new(HttpHeader, CSTRA("Content-Type"), CSTRA("text/html")));
message->nbody = sizeof(RESP_DATA) - 1;
message->body = memMalloc(sizeof(RESP_DATA));
memcpy(message->body, RESP_DATA, sizeof(RESP_DATA));
return response;
}
// vim: set ts=4 sw=4:

61
src/http/response/asset.c

@ -1,5 +1,18 @@
/**
* \file
* A response class that delivers an asset (file on disk).
*
* In future this will use a asset class, get from an asset class
* hash. The asset hash will be a shared resource between all
* workers.
*
* The asset class holds an open file descriptor wich is memory
* mapped and is able to give the correct pointer to neede data.
*
* This change will envolve changes in other parts of the response
* write system, as we no longer need to destinguish between piped
* and bufferd resources...we will allways work with a memory address
* only one time its allocated and one time a memory mapped file.
*
* \author Georg Hopp
*
@ -38,54 +51,30 @@
#include "utils/memory.h"
#include "hash.h"
#include "asset.h"
HttpResponse
httpResponseAsset(
const char * fname,
const char * mime,
size_t nmime,
const char * match,
size_t nmatch)
httpResponseAsset(Asset asset)
{
struct tm * tmp;
char etag[200];
size_t netag;
char mtime[200];
size_t nmtime;
struct stat st;
HttpResponse response;
HttpMessage message;
int handle;
if (-1 == access(fname, O_RDONLY)) {
handle = -1;
} else {
handle = open(fname, O_RDONLY);
fstat(handle, &st);
}
tmp = localtime(&(st.st_mtime));
netag = strftime(etag, sizeof(etag), "%s", tmp);
nmtime = strftime(mtime, sizeof(mtime), "%a, %d %b %Y %T %Z", tmp);
if (netag == nmatch && 0 == memcmp(etag, match, netag)) {
return httpResponse304(mime, nmime, etag, netag, mtime, nmtime);
}
response = new(HttpResponse, "HTTP/1.1", 200, "OK");
message = (HttpMessage)response;
message->type = HTTP_MESSAGE_PIPED;
if (-1 != handle) {
message->handle = new(Stream, STREAM_FD, handle);
message->nbody = st.st_size;
}
message->asset = asset;
message->body = asset->data;
message->nbody = asset->size;
hashAdd(message->header,
new(HttpHeader, CSTRA("Content-Type"), mime, nmime));
new(HttpHeader, CSTRA("Content-Type"),
asset->mime_type, asset->nmime_type));
hashAdd(message->header,
new(HttpHeader, CSTRA("ETag"), etag, netag));
new(HttpHeader, CSTRA("ETag"), asset->etag, asset->netag));
hashAdd(message->header,
new(HttpHeader, CSTRA("Last-Modified"), mtime, nmtime));
new(HttpHeader, CSTRA("Last-Modified"),
asset->mtime, asset->nmtime));
return response;
}

46
src/http/worker.c

@ -52,47 +52,7 @@ httpWorkerCtor(void * _this, va_list * params)
this->val = va_arg(*params, struct randval *);
/* read all mimetypes in a hash */
this->mime_types = new(Hash);
if (0 == access("./config/mime.types", O_RDONLY)) {
FILE * handle = fopen("./config/mime.types", "r");
if (NULL != handle) {
char buffer[512];
buffer[511] = '\0';
while (NULL != fgets(buffer, 511, handle)) {
char * tmp;
char * key = buffer;
char * value;
size_t nkey;
size_t nvalue;
tmp = memchr(key, ' ', 512);
if (NULL != tmp) {
*tmp = '\0';
}
nkey = tmp - buffer;
value = tmp + 1;
for (; *value == ' ' && value < buffer+511; value++);
nvalue = strlen(value);
if ('\n' == value[nvalue-1]) {
nvalue--;
value[nvalue] = '\0';
}
hashAdd(this->mime_types,
new(HashValue, key, nkey, value, nvalue));
}
fclose(handle);
}
}
this->asset_pool = new(Hash);
sprintf(cbuf_id, "%s_%s", "parser", id);
this->pbuf = new(Cbuf, cbuf_id, PARSER_MAX_BUF);
@ -129,7 +89,7 @@ httpWorkerDtor(void * _this)
delete(this->writer);
if (NULL != this->pbuf) {
delete(this->mime_types);
delete(this->asset_pool);
delete(this->pbuf); //!< cloned workers have NULL, so delete won't do anything
delete(this->wbuf); //!< cloned workers have NULL, so delete won't do anything
tdestroy(*(this->sroot), tDelete);
@ -145,7 +105,7 @@ httpWorkerClone(void * _this, void * _base)
this->val = base->val;
this->mime_types = base->mime_types;
this->asset_pool = base->asset_pool;
this->parser = new(HttpParser, base->pbuf);
this->writer = new(HttpWriter, base->wbuf);

32
src/http/worker/get_asset.c

@ -26,21 +26,24 @@
#include "http/message.h"
#include "http/request.h"
#include "http/response.h"
#include "http/worker.h"
#include "utils/memory.h"
#include "hash.h"
HttpMessage
httpWorkerGetAsset(
HttpWorker this,
HttpRequest request,
const char * fname,
const char * mime,
size_t nmime)
const char * fname)
{
char * match;
size_t nmatch;
HttpHeader header;
HttpMessage message;
Asset asset;
size_t nfname = strlen(fname);
header = hashGet(
((HttpMessage)request)->header,
@ -55,11 +58,26 @@ httpWorkerGetAsset(
nmatch = (header->nvalue)[0];
}
message = (HttpMessage)httpResponseAsset(
fname, mime, nmime, match, nmatch);
asset = assetPoolGet(fname, nfname);
if (asset->netag == nmatch
&& 0 == memcmp(asset->etag, match, asset->netag)) {
assetPoolRelease(asset);
return (HttpMessage)httpResponse304(
asset->mime_type, asset->nmime_type,
asset->etag, asset->netag,
asset->mtime, asset->nmtime);
}
message = (HttpMessage)httpResponseAsset(asset);
if (message->type == HTTP_MESSAGE_PIPED && message->handle == NULL) {
delete(message);
if (NULL == message) {
// here we should be somewhat more care about what causes
// the message to be NULL... here this could be also a
// 404 not found....
assetPoolRelease(asset);
message = (HttpMessage)httpResponse500();
}
return message;

42
src/http/worker/get_mime_type.c

@ -1,42 +0,0 @@
/**
* \file
*
* \author Georg Hopp
*
* \copyright
* Copyright © 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 <sys/types.h>
#include "http/worker.h"
#include "utils/memory.h"
#include "hash.h"
char *
httpWorkerGetMimeType(
HttpWorker this,
const char * extension)
{
HashValue mime_type;
mime_type = hashGet(this->mime_types, extension, strlen(extension));
return (char *)mime_type->value;
}
// vim: set ts=4 sw=4:

33
src/http/worker/process.c

@ -42,10 +42,11 @@
#include "http/parser.h"
#include "utils/memory.h"
#include "utils/mime_type.h"
#include "commons.h"
HttpMessage httpWorkerGetAsset(HttpRequest, const char *, const char *, size_t);
HttpMessage httpWorkerGetAsset(HttpWorker, HttpRequest, const char *);
void httpWorkerAddCommonHeader(HttpMessage, HttpMessage);
char * httpWorkerGetMimeType(HttpWorker, const char * extension);
@ -174,23 +175,14 @@ httpWorkerProcess(HttpWorker this, Stream st)
}
}
else if (0 == strcmp("/assets/js/serverval", request->path)) {
response = httpWorkerGetAsset(
request,
"./assets/js/serverval.js",
CSTRA("text/javascript"));
}
else {
char html_asset[2048] = "./assets/html";
char base_asset[2048] = "./assets";
char main_asset[] = "/main.html";
char * mime_type = NULL;
char default_mime[] = "application/octet-stream";
char * asset_path = base_asset;
char * asset;
char * extension;
char * mime_type;
if (0 == strcmp("/", request->path)) {
asset = main_asset;
@ -198,11 +190,10 @@ httpWorkerProcess(HttpWorker this, Stream st)
asset = request->path;
}
extension = strrchr(asset, '.');
if (NULL != extension) {
extension++;
mime_type = httpWorkerGetMimeType(this, extension);
mime_type = strrchr(asset, '.');
if (NULL != mime_type) {
mime_type++;
mime_type = getMimeType(mime_type, strlen(mime_type));
}
if (NULL != mime_type &&
@ -210,16 +201,8 @@ httpWorkerProcess(HttpWorker this, Stream st)
asset_path = html_asset;
}
if (NULL == mime_type) {
mime_type = default_mime;
}
strcat(asset_path, asset);
response = httpWorkerGetAsset(
request,
asset_path,
mime_type,
strlen(mime_type));
response = httpWorkerGetAsset(this, request, asset_path);
}
}

22
src/http/writer/write.c

@ -81,20 +81,9 @@ httpWriterWrite(void * _this, Stream st)
this->current->nbody - this->nbody,
cbufGetFree(this->buffer));
switch (this->current->type) {
case HTTP_MESSAGE_BUFFERED:
cbufSetData(this->buffer,
this->current->body + this->nbody,
size);
break;
case HTTP_MESSAGE_PIPED:
size = cbufRead(this->buffer, this->current->handle);
break;
default:
return -1;
}
this->nbody += size;
}
@ -105,12 +94,11 @@ httpWriterWrite(void * _this, Stream st)
{
ssize_t written = cbufWrite(this->buffer, st);
if (0 <= written) {
this->written += written;
}
else {
return -1;
if (0 > written) {
return written;
}
this->written += written;
}
if (this->written == this->current->nbody + this->nheader) {
@ -138,8 +126,8 @@ httpWriterWrite(void * _this, Stream st)
delete(this->current);
return -1;
}
delete(this->current);
break;
}
}

6
src/server/read.c

@ -76,11 +76,11 @@ serverRead(Server this, unsigned int i)
inet_ntoa((((this->conns)[fd].sock)->addr).sin_addr));
serverCloseConn(this, i);
case 0:
break;
//case 0:
// break;
default:
(this->fds)[i].events |= POLLOUT;
// (this->fds)[i].events |= POLLOUT;
break;
}

1
src/server/run.c

@ -27,7 +27,6 @@
int serverPoll(Server);
int serverHandleAccept(Server, unsigned int);
void serverCloseConn(Server, unsigned int);
ssize_t serverRead(Server, unsigned int);
ssize_t serverWrite(Server, unsigned int);

5
src/server/server.c

@ -61,6 +61,11 @@ serverCtor(void * _this, va_list * params)
port = va_arg(* params, int);
backlog = va_arg(* params, unsigned int);
loggerLog(this->logger,
LOGGER_INFO,
"accept up to %zu connections",
this->max_fds);
this->fds = memCalloc(sizeof(struct pollfd), this->max_fds);
this->conns = memCalloc(sizeof(struct conns), this->max_fds);

47
src/server/write.c

@ -20,6 +20,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include "server.h"
#include "logger.h"
#include "stream.h"
@ -46,15 +48,52 @@ serverWrite(Server this, unsigned int i)
switch(remaining) {
case -1:
serverCloseConn(this, i);
/*
* read failure
*/
if (errno == EAGAIN || errno == EWOULDBLOCK) {
/* on EGAIN just try again later. */
break;
}
// DROP-THROUGH
case 0:
(this->fds)[i].events &= ~POLLOUT;
break;
case -2:
/**
* normal close: this must be mapped to -2 within the
* underlying read call.
*
* \todo make sure all pending writes will be done before
* close.
*/
/*
* close connection if not EAGAIN, this would also
* remove the filedescriptor from the poll list.
* Else just return indicate
*/
loggerLog(this->logger, LOGGER_INFO,
"connection[%d] closed...%s",
fd,
inet_ntoa((((this->conns)[fd].sock)->addr).sin_addr));
serverCloseConn(this, i);
//case 0:
// break;
default:
// (this->fds)[i].events |= POLLOUT;
break;
// case -1:
// serverCloseConn(this, i);
// break;
//
// case 0:
// (this->fds)[i].events &= ~POLLOUT;
// break;
//
// default:
// break;
}
return remaining;

2
src/taskrambler.c

@ -196,6 +196,8 @@ main()
if (NULL != auth) delete(auth);
if (NULL != logger) delete(logger);
clearMimeTypes();
assetPoolCleanup();
memCleanup();
}

Loading…
Cancel
Save