| |
| Posted by Eungkyu Song | PermalinkReply |
|
Eungkyu Song
| I wrote a very simple web server for testing d language. (in linux) It seems to work well. but a chunk of memory leeks per connection especially in thread mode.
please help me.
i need review for the code style too.
below is the code
Makefile:
--------------------------------------------------------
TARGET = httpd httpd-inetd httpd-thread
all: $(TARGET)
httpd: httpd.d
dmd -ofhttpd httpd.d
httpd-inetd: httpd.d
dmd -ofhttpd-inetd -version=inetd httpd.d
httpd-thread: httpd.d
dmd -ofhttpd-thread -version=thread httpd.d
clean:
rm -f *.o $(TARGET)
--------------------------------------------------------
httpd.d:
--------------------------------------------------------
import std.socket;
import std.socketstream;
import std.stream;
import std.string;
import std.file;
import std.thread;
alias char[][char[]] map;
const char[] SERVER = "Dhttpd";
const char[] SERVER_ROOT = "/home/eungkyu/public_html";
const char[] DEFAULT = "index.html";
const char[] MIME_DEFAULT = "text/plain";
const int PORT = 8888;
char[][int] code_string;
char[][int] error;
map mime_type;
extern (C) {
alias void function(int) sighandler_t;
const int SIGPIPE = 13;
const sighandler_t SIG_IGN = cast(sighandler_t) 2;
sighandler_t signal(int signum, sighandler_t handler);
void handle_signal(int signo)
{
/* do nothing */
}
alias long time_t;
time_t time(time_t* t);
size_t strftime(char* s, size_t max, in char* format, in void* tm);
void* localtime(time_t* timep);
}
void init()
{
code_string[200] = "OK";
code_string[400] = "Bad Request";
code_string[403] = "Forbidden";
code_string[404] = "Not Found";
code_string[501] = "Not Implemented";
error[400] = "error/400.html";
error[403] = "error/403.html";
error[404] = "error/404.html";
error[501] = "error/501.html";
mime_type["html"] = "text/html";
mime_type["htm"] = "text/html";
mime_type["gif"] = "image/gif";
mime_type["jpg"] = "image/jpeg";
mime_type["jpeg"] = "image/jpeg";
mime_type["png"] = "image/png";
mime_type["txt"] = "text/plain";
/* why ignoring or not touching the SIGPIPE cause segfault */
//signal(SIGPIPE, &SIG_IGN);
signal(SIGPIPE, &handle_signal);
chdir(SERVER_ROOT);
}
Socket init_listener(int port)
{
Socket listener = new TcpSocket;
listener.bind(new InternetAddress(port));
listener.listen(10);
return listener;
}
map parse_header(Stream input)
{
map header;
char[] line, name, value;
while ((line = input.readLine()) != null) {
if (find(" \t", line[0]) == 0 && name != null) {
header[name] ~= stripl(line);
continue;
}
int delim = find(line, ':');
if (delim == -1) {
name = null;
continue;
}
name = line[0 .. delim];
value = stripl(line[delim + 1 .. line.length]);
header[name] = value;
}
return header;
}
void print_header(Stream output, map header)
{
foreach(char[] name; header.keys)
output.writeLine(name ~ ": " ~ header[name]);
}
char[] get_path(char[] location)
{
char[] path = "." ~ location;
while (path.length > 0 && path[path.length - 1] == '/')
path.length = path.length - 1;
return path;
}
char[] get_mime_type(char[] path)
{
if (path == null)
return MIME_DEFAULT;
int extloc = rfind(path, '.');
if (extloc == -1)
return MIME_DEFAULT;
char[] ext = path[extloc + 1 .. path.length];
if (mime_type[ext] == null)
return MIME_DEFAULT;
return mime_type[ext];
}
char[] get_date_string()
{
char[50] tmp;
time_t t;
time(&t);
strftime(tmp, 50, "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
/* return toString(tmp) doesn't work */
return toString(tmp).dup;
}
void response(Stream output, int code, char[] path = null)
{
map header;
header["Server"] = SERVER;
header["Connection"] = "closed";
header["Date"] = get_date_string();
if (code >= 400 && code < 600 && path == null)
path = error[code];
if (path != null) {
header["Content-Type"] = get_mime_type(path);
header["Content-Length"] = toString(getSize(path));
}
try {
output.printf("HTTP/1.0 %i %.*s\r\n", code, code_string[code]);
print_header(output, header);
output.writeString("\r\n");
if (path != null) {
File file = new File(path, FileMode.In);
output.copyFrom(file, file.size);
file.close();
}
}
catch (WriteError) {
/* client closed the connection. just ignore it */
}
}
void process(Stream output, char[][] method, map header)
{
if (method.length == 0)
return response(output, 400);
if (method[0] != "GET")
return response(output, 501);
if (method[1][0] != '/')
return response(output, 400);
char[] path = get_path(method[1]);
while (exists(path) == 1) {
if (isdir(path) == 0)
return response(output, 200, path);
if (exists(path ~ "/" ~ DEFAULT) == 0) {
return response(output, 403);
}
path ~= "/" ~ DEFAULT;
}
return response(output, 404);
}
int run(void* data)
{
Stream[] stream = *(cast(Stream[]*) data);
char[][] method = split(stream[0].readLine());
map header = parse_header(stream[0]);
process(stream[1], method, header);
stream[0].close();
stream[1].close();
return 0;
}
int main(char[][] args)
{
init();
version (inetd) {
Stream[] stream;
stream.length = 2;
stream[0] = stdin;
stream[1] = stdout;
run(&stream);
}
else {
Socket listener = init_listener(PORT);
while (1) {
Stream[] stream;
stream.length = 2;
SocketStream sockstream = new SocketStream(listener.accept());
stream[0] = sockstream;
stream[1] = sockstream;
version (thread) {
Thread runner = new Thread(&run, &stream);
runner.start();
}
else {
run(&stream);
}
}
}
return 0;
}
--------------------------------------------------------
|