Add Demo/Network/HtServ

v5
minexew 2 years ago
parent 085ded14ac
commit cf49dd90f9
  1. 2
      Adam/Net/Tcp.HC
  2. 7
      CHANGELOG.md
  3. 179
      Demo/Network/HtServ.HC

@ -449,7 +449,7 @@ I64 TcpSocketAccept(CTcpSocket* s, sockaddr* addr, I64 addrlen) {
// TODO: Thread safe?
if (s->backlog_first) {
CTcpSocket* new_socket = s->backlog_first;
"Retr %p\n", new_socket;
// "Retr %p\n", new_socket;
s->backlog_first = s->backlog_first->backlog_next;
if (!s->backlog_first)

@ -5,14 +5,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Added
- functions `inet_ntop`, `inet_pton`
- function `sendall`
- World's worst HTTP server (in `Demo/Network/HtServ`)
- Functions `inet_ntop`, `inet_pton`
- Function `sendall`
### Changed
- `send` will no longer block until all data has been sent. The new behavior is consistend with blocking Berkeley sockets (the new function `sendall` can be used to get the previous behavior)
### Removed
- functions `inet_aton`, `inet_ntoa`
- Functions `inet_aton`, `inet_ntoa`
### Fixed
- TCP server mode is now usable (`close` doesn't abruptly terminate the connection)

@ -0,0 +1,179 @@
// See https://www.w3.org/Protocols/HTTP/1.0/spec.html
#include "::/Adam/Net/Socket"
#define PORT 80
#define BASE_DIR "/Www"
// Set to 0 if you don't want to print anything
#define SERVER_STRING "Server: Shrine HtServ\r\n"
#define STATE_TERM 0
#define STATE_NEW 1
#define STATE_HEADERS 2
#define METHOD_GET 0
class CHtServSession {
CTcpSocket* sock;
U8 state;
U8 method;
U8* protocol;
U8* resource;
};
U0 HtServProcess(CHtServSession* sess) {
// look for resource
// TODO: prevent access to parent
U8* path = MStrPrint("%s/%s", BASE_DIR, sess->resource);
I64 size;
U8* data = FileRead(path, &size);
U8 line[256];
if (data) {
// Send "200 OK"
StrPrint(line, "%s 200 OK\r\n", sess->protocol); // FIXME possible overflow
sendString(sess->sock, line, 0);
StrPrint(line, "Content-Length: %d\r\n", size);
sendString(sess->sock, line, 0);
if (SERVER_STRING) {
sendString(sess->sock, SERVER_STRING, 0);
}
sendString(sess->sock, "\r\n", 0);
sendall(sess->sock, data, size, 0);
sess->state = STATE_TERM;
}
else {
// Send "404 Not Found"
StrPrint(line, "%s 404 Not Found\r\n", sess->protocol); // FIXME possible overflow
sendString(sess->sock, line, 0);
sendString(sess->sock, "Content-Length: 0\r\n", 0);
if (SERVER_STRING) {
sendString(sess->sock, SERVER_STRING, 0);
}
sendString(sess->sock, "\r\n", 0);
sess->state = STATE_TERM;
}
}
U0 HtServSession(CTcpSocket* sock) {
DocNew;
CHtServSession sess;
sess.sock = sock;
sess.state = STATE_NEW;
"HtServ: session begin\n";
// TODO: log peer addr
while (sess.state != STATE_TERM) {
U8 line[256];
I64 error = recvLine(sock, line, sizeof(line), 0);
if (error < 0) {
break;
}
"REQUEST: %s\n", line;
U8* delim;
switch (sess.state) {
case STATE_NEW:
// expect "GET <resource> HTTP/1.x"
delim = StrFirstOcc(line, " ");
if (!delim) {
// Malformed request, ignore
sess.state = STATE_TERM; break;
}
*delim = 0;
if (!StrCmp(line, "GET")) {
sess.method = METHOD_GET;
U8* resource = delim + 1;
delim = StrFirstOcc(resource, " ");
if (!delim) {
// "HTTP 0.9", currently not supported
sess.state = STATE_TERM; break;
}
*delim = 0;
U8* protocol = delim + 1;
if (StrCmp(protocol, "HTTP/1.0") != 0 && StrCmp(protocol, "HTTP/1.1")) {
"HtServ: Bad protocol\n";
// TODO: sent 400 Bad Request
sess.state = STATE_TERM; break;
}
sess.protocol = StrNew(protocol);
// TODO: should urldecode
sess.resource = StrNew(resource);
sess.state = STATE_HEADERS;
}
else {
"HtServ: Invalid method %s\n", line;
// TODO: sent 400 Bad Request
}
break;
case STATE_HEADERS:
if (*line == 0) {
"HtServ: headers done\n";
HtServProcess(&sess);
sess.state = STATE_TERM;
break;
}
else {
// Ignore all headers for now
}
break;
}
// send(client, buffer, count, 0);
}
close(sock);
}
I8 HtServ() {
SocketInit();
I64 sock = socket(AF_INET, SOCK_STREAM);
if (sock < 0)
return -1;
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, &addr, sizeof(addr)) < 0) {
close(sock);
"$FG,4$TcpEchoServer: failed to bind to port %d\n$FG$", PORT;
return -1;
}
I64 error = listen(sock, 1);
if (error < 0) {
"$FG,6$listen: error %d\n$FG$", error;
return -1;
}
"$FG,2$Serving %s on port %d\n$FG$", BASE_DIR, PORT;
while (1) {
I64 client = accept(sock, 0, 0);
if (client)
Spawn(&HtServSession, client);
else
break;
Yield; // loop unconditionally
}
close(sock);
return 0;
}
Loading…
Cancel
Save