Admin Site Admin
(send private message)
Posts: 933 Topics: 55
Location: OverHertz Studio | [65] Tutorial :: Writing a small webserver - posted: 2011-09-16 22:21:01 In this tutorial I will write a simple web server, and i mean a very simple web server, i will take you step by step as i write it, allowing you to learn some of the language and at the same time end up with your own small simple web server to start out with. It is expected you have experience with winsock and the windows API.
ok first we need a basic ziron project file with a few extra includes, such as the winsock file and console.
Code:program WIN32CUI 'Sample';
//cpu optimization option.
#cpu 486;
#include 'ziron32.zir';
#include 'fileio.zir';
#include 'wsock32.zir';
#include 'console.zir';
ExitProcess(0);
ok our first task is to start listening on port 80, we will create a const for the port
Code:
let us add some variables for winsock also
Code:WSADATA ws;
WSOCKET listensocket;
WSOCKET clientsocket;
sockaddr_in addrinfo;
we will now initialize winsock and begin listening
Code:EAX = WSAStartup(WSOCKVER2_2, @ws);
listensocket = socket(PF_INET, SOCK_STREAM, 0);
addrinfo.sin_family = AF_INET;
addrinfo.sin_addr = INADDR_ANY;
addrinfo.sin_port = htons(DEFAULT_PORT);
bind(listensocket, @addrinfo, sizeof sockaddr_in);
listen(listensocket, SOMAXCONN);
next we need to accept connections, which we will do in a loop
Code:println('started serving...');
while () {
clientsocket = accept(listensocket, nil, nil);
//process something here
//close the client gracefully
shutdown(clientsocket, SD_BOTH);
closesocket(clientsocket);
}
ok now i'll add some variables for the web server side of things
Code://this const will be sent before sending content type
const HTTP_HEADER = 'HTTP/1.1 200 OK\nContent-Type: ';
//this variable will hold the number of bytes received from recv
DWord bytesReceived;
//this will be our buffer for incoming data
char inBuffer[65537];
//a handle for the file being output to the client
DWord hFile;
//this buffer will be for the file that is read from disk
char IndexPage[65535];
//this buffer is for our file name and path.
char fileNameBuf[256];
ok in our loop where the process comment is we will place some more code
Code: //receive some data
bytesReceived = recv(clientsocket, @inBuffer, 65536, 0);
ebx = @inBuffer;
//add a null char at the end for safety
char[ebx+eax] = 0;
//if no bytes were incoming, do nothing
if (bytesReceived != 0) {
//if the incoming data is a GET request we will process it
if (char*[ebx] == ord('GET ')) {
//call a process function we will write in a moment.
processInBuffer();
//try to open the requested file that our process function will get for us.
hFile = fileopen(@fileNameBuf);
//if the file exists and is ok we will output it
if (hFile != INVALID_HANDLE_VALUE) {
//we will reuse the bytesReceived instead of creating a new variable.
ReadFile(hFile, @IndexPage, 65535, bytesReceived, nil);
send(clientsocket, @IndexPage, bytesReceived, 0);
fileclose(hFile);
}
}
char[ebx] = 0;
}
now we will write our process function
Code:function processInBuffer() {
uses ecx ebx;
add ebx, 4 //skip past 'GET '
ecx = xor;
eax = @fileNameBuf;
byte[eax] = ord('.');
inc eax;
while (byte[ebx+ecx] <> ord(' ')) {
dl = [ebx+ecx];
[eax+ecx] = dl;
inc ecx;
}
[eax+ecx] = 0;
if (word[eax] == "/") {
strlcpy(@fileNameBuf, 'index.html', 10);
byte[eax+11] = 0;
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'text/html\n\n', 11, 0);
} else {
eax = getFileExt(eax);
if (char*[eax] == "html") {
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'text/html\n\n', 11, 0);
} elseif (char*[eax] == "css") {
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'text/css\n\n', 10, 0);
} elseif (char*[eax] == "gif") {
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'image/gif\n\n', 11, 0);
} elseif (char*[eax] == "png") {
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'image/png\n\n', 11, 0);
} elseif ((char*[eax] == "jpg") or (char*[eax] == "jpeg")) {
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'image/jpeg\n\n', 12, 0);
}
}
}
i will leave this function for you to study, if you have questions, please do reply.
well, that is it, we have build a tiny web server, bare in mind it has no security and many other features are missing that you can add, maybe in later tutorials i can extend this web server.
the full code for this tutorial:
Code:program WIN32CUI 'Sample';
//cpu optimization option.
#cpu 486;
#include 'ziron32.zir';
#include 'fileio.zir';
#include 'wsock32.zir';
#include 'console.zir';
/////////////////////////////////////////////////
const DEFAULT_PORT = 80;
WSADATA ws;
WSOCKET listensocket;
WSOCKET clientsocket;
sockaddr_in addrinfo;
EAX = WSAStartup(WSOCKVER2_2, @ws);
listensocket = socket(PF_INET, SOCK_STREAM, 0);
addrinfo.sin_family = AF_INET;
addrinfo.sin_addr = INADDR_ANY;
addrinfo.sin_port = htons(DEFAULT_PORT);
bind(listensocket, @addrinfo, sizeof sockaddr_in);
const HTTP_HEADER = 'HTTP/1.1 200 OK\nContent-Type: ';
DWord bytesReceived;
char inBuffer[65537];
DWord hFile;
char IndexPage[65535];
char fileNameBuf[256];
listen(listensocket, SOMAXCONN);
/////////////////////////////////////////////////
function processInBuffer() {
uses ecx ebx;
add ebx, 4 //skip past 'GET '
ecx = xor;
eax = @fileNameBuf;
byte[eax] = ord('.');
inc eax;
while (byte[ebx+ecx] <> ord(' ')) {
dl = [ebx+ecx];
[eax+ecx] = dl;
inc ecx;
}
[eax+ecx] = 0;
if (word[eax] == "/") {
strlcpy(@fileNameBuf, 'index.html', 10);
byte[eax+11] = 0;
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'text/html\n\n', 11, 0);
} else {
eax = getFileExt(eax);
if (char*[eax] == "html") {
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'text/html\n\n', 11, 0);
} elseif (char*[eax] == "css") {
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'text/css\n\n', 10, 0);
} elseif (char*[eax] == "gif") {
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'image/gif\n\n', 11, 0);
} elseif (char*[eax] == "png") {
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'image/png\n\n', 11, 0);
} elseif ((char*[eax] == "jpg") or (char*[eax] == "jpeg")) {
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'image/jpeg\n\n', 12, 0);
}
}
}
println('started serving...');
while () {
clientsocket = accept(listensocket, nil, nil);
bytesReceived = recv(clientsocket, @inBuffer, 65536, 0);
ebx = @inBuffer;
char[ebx+eax] = 0;
if (bytesReceived != 0) {
if (char*[ebx] == ord('GET ')) {
processInBuffer();
hFile = fileopen(@fileNameBuf);
if (hFile != INVALID_HANDLE_VALUE) {
//we will reuse the bytesReceived instead of creating a new variable.
ReadFile(hFile, @IndexPage, 65535, bytesReceived, nil);
send(clientsocket, @IndexPage, bytesReceived, 0);
fileclose(hFile);
}
}
char[ebx] = 0;
}
shutdown(clientsocket, SD_BOTH);
closesocket(clientsocket);
}
ExitProcess(0);
note you need to place an index.html inside the same dir as the executable, or you can put the address to a specified file.
let me know what you think.
Download Ziron
Get free hosting for Ziron related fan-sites and Ziron projects, contact me in private message. |
Admin Site Admin
(send private message)
Posts: 933 Topics: 55
Location: OverHertz Studio | [68] - posted: 2011-09-17 18:27:36 made a few extra updates to the code, if you have questions about it, just ask, it still has some major things needed, for example multitasking...right now only 1 user actually downloads a file at a time.
Code:program WIN32CUI 'Sample';
//cpu optimization option.
#cpu 486;
#include 'ziron32.zir';
#include 'fileio.zir';
#include 'wsock32.zir';
#include 'console.zir';
/////////////////////////////////////////////////
const DEFAULT_PORT = 80;
WSADATA ws;
WSOCKET listensocket;
WSOCKET clientsocket;
sockaddr_in addrinfo;
EAX = WSAStartup(WSOCKVER2_2, @ws);
listensocket = socket(PF_INET, SOCK_STREAM, 0);
addrinfo.sin_family = AF_INET;
addrinfo.sin_addr = INADDR_ANY;
addrinfo.sin_port = htons(DEFAULT_PORT);
bind(listensocket, @addrinfo, sizeof sockaddr_in);
const HTTP_HEADER = 'HTTP/1.1 200 OK\nContent-Type: ';
const HTTP_NOTFOUND = 'HTTP/1.1 404 Not Found';
DWord bytesReceived;
char inBuffer[65537];
DWord hFile;
char IndexPage[65535];
char fileNameBuf[256];
//WSOCKET clientsocks[65535];
listen(listensocket, SOMAXCONN);
/////////////////////////////////////////////////
function processInBuffer() {
uses ecx ebx;
add ebx, 4 //skip past 'GET '
ecx = xor;
eax = @fileNameBuf;
byte[eax] = ".";
inc eax;
while (byte[ebx+ecx] <> " ") {
dl = [ebx+ecx];
[eax+ecx] = dl;
inc ecx;
}
[eax+ecx] = 0;
}
println('started serving...');
while () {
clientsocket = accept(listensocket, nil, nil);
bytesReceived = recv(clientsocket, @inBuffer, 65536, 0);
ebx = @inBuffer;
char[ebx+eax] = 0;
if (bytesReceived != 0) {
if (char*[ebx] == "GET ") {
processInBuffer();
eax = getFileExt(eax);
if (char[eax] == "") {
eax--;
if (char[eax] == "/") {
strlcpy(eax, '/index.html', 11);
byte[eax+11] = 0;
add eax, 7
}
}
push eax
hFile = fileopen(@fileNameBuf);
if (hFile != INVALID_HANDLE_VALUE) {
pop eax
if (char*[eax] == "html") {
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'text/html\n\n', 11, 0);
} elseif (char*[eax] == "css") {
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'text/css\n\n', 10, 0);
} elseif (char*[eax] == "gif") {
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'image/gif\n\n', 11, 0);
} elseif (char*[eax] == "png") {
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'image/png\n\n', 11, 0);
} elseif ((char*[eax] == "jpg") or (char*[eax] == "jpeg")) {
send(clientsocket, HTTP_HEADER, 30, 0);
send(clientsocket, 'image/jpeg\n\n', 12, 0);
}
//we will reuse the bytesReceived instead of creating a new variable.
repeat {
ReadFile(hFile, @IndexPage, 65535, @bytesReceived, nil);
if (bytesReceived == 0) break;
send(clientsocket, @IndexPage, bytesReceived, 0);
};
fileclose(hFile);
} else {
pop eax
send(clientsocket, HTTP_NOTFOUND, 22, 0);
}
}
char[ebx] = 0;
}
shutdown(clientsocket, SD_BOTH);
closesocket(clientsocket);
}
ExitProcess(0);
Download Ziron
Get free hosting for Ziron related fan-sites and Ziron projects, contact me in private message. |