Index: uspace/app/websrv/websrv.c
===================================================================
--- uspace/app/websrv/websrv.c	(revision b5eae307acebbbe9180272fb7c98f315b08758fb)
+++ uspace/app/websrv/websrv.c	(revision 4a4cc150f1b4177264d79d70d62494b685bb24c2)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2010 Jiri Svoboda
+ * Copyright (c) 2011 Jiri Svoboda
  * All rights reserved.
  *
@@ -31,8 +31,13 @@
  */
 /**
- * @file (Less-than-skeleton) web server.
+ * @file Skeletal web server.
  */
 
+#include <bool.h>
+#include <errno.h>
 #include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
 #include <net/in.h>
@@ -44,13 +49,191 @@
 #define PORT_NUMBER 8080
 
+#define WEB_ROOT "/data/web"
+
 /** Buffer for receiving the request. */
 #define BUFFER_SIZE 1024
-static char buf[BUFFER_SIZE];
+static char rbuf[BUFFER_SIZE];
+static size_t rbuf_out, rbuf_in;
+
+static char lbuf[BUFFER_SIZE + 1];
+static size_t lbuf_used;
+
+static char fbuf[BUFFER_SIZE];
 
 /** Response to send to client. */
-static const char *response_msg =
+static const char *ok_msg =
     "HTTP/1.0 200 OK\r\n"
-    "\r\n"
-    "<h1>Hello from HelenOS!</h1>\r\n";
+    "\r\n";
+
+/** Receive one character (with buffering) */
+static int recv_char(int fd, char *c)
+{
+	ssize_t rc;
+
+	if (rbuf_out == rbuf_in) {
+		rbuf_out = 0;
+		rbuf_in = 0;
+
+		rc = recv(fd, rbuf, BUFFER_SIZE, 0);
+		if (rc <= 0) {
+			printf("recv() failed (%zd)\n", rc);
+			return rc;
+		}
+
+		rbuf_in = rc;
+	}
+
+	*c = rbuf[rbuf_out++];
+	return EOK;
+}
+
+/** Receive one line with length limit. */
+static int recv_line(int fd)
+{
+	char c, prev;
+	int rc;
+	char *bp;
+
+	bp = lbuf; c = '\0';
+	while (bp < lbuf + BUFFER_SIZE) {
+		prev = c;
+		rc = recv_char(fd, &c);
+		if (rc != EOK)
+			return rc;
+
+		*bp++ = c;
+		if (prev == '\r' && c == '\n')
+			break;
+	}
+
+	lbuf_used = bp - lbuf;
+	*bp = '\0';
+
+	if (bp == lbuf + BUFFER_SIZE)
+		return ELIMIT;
+
+	return EOK;
+}
+
+static bool uri_is_valid(char *uri)
+{
+	char *cp;
+	char c;
+
+	if (uri[0] != '/')
+		return false;
+	if (uri[1] == '.')
+		return false;
+
+	cp = uri + 1;
+	while (*cp != '\0') {
+		c = *cp++;
+		if (c == '/')
+			return false;
+	}
+
+	return true;
+}
+
+static int send_response(int conn_sd, const char *msg)
+{
+	size_t response_size;
+	ssize_t rc;
+
+	response_size = str_size(msg);
+
+	/* Send a canned response. */
+        printf("Send response...\n");
+	rc = send(conn_sd, (void *) msg, response_size, 0);
+	if (rc < 0) {
+		printf("send() failed.\n");
+		return rc;
+	}
+
+	return EOK;
+}
+
+static int uri_get(const char *uri, int conn_sd)
+{
+	int rc;
+	char *fname;
+	int fd;
+	ssize_t nr;
+
+	if (str_cmp(uri, "/") == 0)
+		uri = "/index.htm";
+
+	rc = asprintf(&fname, "%s%s", WEB_ROOT, uri);
+	if (rc < 0)
+		return ENOMEM;
+
+	fd = open(fname, O_RDONLY);
+	if (fd < 0) {
+		printf("File '%s' not found.\n", fname);
+		return ENOENT;
+	}
+
+	rc = send_response(conn_sd, ok_msg);
+	if (rc != EOK)
+		return rc;
+
+	while (true) {
+		nr = read(fd, fbuf, BUFFER_SIZE);
+		if (nr == 0)
+			break;
+
+		if (nr < 0) {
+			close(fd);
+			return EIO;
+		}
+
+		rc = send(conn_sd, fbuf, nr, 0);
+		if (rc < 0) {
+			printf("send() failed\n");
+			close(fd);
+			return rc;
+		}
+	}
+
+	close(fd);
+
+	return EOK;
+}
+
+static int req_process(int conn_sd)
+{
+	int rc;
+	char *uri, *end_uri;
+
+	rc = recv_line(conn_sd);
+	if (rc != EOK) {
+		printf("recv_line() failed\n");
+		return rc;
+	}
+
+	printf("%s", lbuf);
+
+	if (str_lcmp(lbuf, "GET ", 4) != 0) {
+		printf("Invalid HTTP method.\n");
+		return EINVAL;
+	}
+
+	uri = lbuf + 4;
+	end_uri = str_chr(uri, ' ');
+	if (end_uri == NULL) {
+		end_uri = lbuf + lbuf_used - 2;
+		assert(*end_uri == '\r');
+	}
+
+	*end_uri = '\0';
+	printf("Requested URI '%s'.\n", uri);
+
+	if (!uri_is_valid(uri)) {
+		printf("Invalid request URI.\n");
+		return EINVAL;
+	}
+
+	return uri_get(uri, conn_sd);
+}
 
 int main(int argc, char *argv[])
@@ -64,5 +247,4 @@
 	int rc;
 
-	size_t response_size;
 
 	addr.sin_family = AF_INET;
@@ -94,6 +276,4 @@
 		return 1;
 	}
-
-	response_size = str_size(response_msg);
 
 	printf("Listening for connections at port number %u.\n", PORT_NUMBER);
@@ -105,5 +285,5 @@
 		if (conn_sd < 0) {
 			printf("accept() failed.\n");
-			return 1;
+			continue;
 		}
 
@@ -111,19 +291,9 @@
 
 		printf("Wait for client request\n");
-
-		/* Really we should wait for a blank line. */
-		rc = recv(conn_sd, buf, BUFFER_SIZE, 0);
-		if (rc < 0) {
-			printf("recv() failed\n");
-			return 1;
-		}
-
-		/* Send a canned response. */
-                printf("Send response...\n");
-		rc = send(conn_sd, (void *) response_msg, response_size, 0);
-		if (rc < 0) {
-			printf("send() failed.\n");
-			return 1;
-		}
+		rbuf_out = rbuf_in = 0;
+
+		rc = req_process(conn_sd);
+		if (rc != EOK) 
+			printf("Error processing request.\n");
 
 		rc = closesocket(conn_sd);
Index: uspace/dist/data/web/bar.htm
===================================================================
--- uspace/dist/data/web/bar.htm	(revision 4a4cc150f1b4177264d79d70d62494b685bb24c2)
+++ uspace/dist/data/web/bar.htm	(revision 4a4cc150f1b4177264d79d70d62494b685bb24c2)
@@ -0,0 +1,10 @@
+<html>
+    <head>
+	<title>Bar!</title>
+    </head>
+    <body>
+	<h1>Bar!</h1>
+
+	<a href="/">Back to top</a>
+    </body>
+</html>
Index: uspace/dist/data/web/foo.htm
===================================================================
--- uspace/dist/data/web/foo.htm	(revision 4a4cc150f1b4177264d79d70d62494b685bb24c2)
+++ uspace/dist/data/web/foo.htm	(revision 4a4cc150f1b4177264d79d70d62494b685bb24c2)
@@ -0,0 +1,10 @@
+<html>
+    <head>
+	<title>Foo!</title>
+    </head>
+    <body>
+	<h1>Foo!</h1>
+
+	<a href="/">Back to top</a>
+    </body>
+</html>
Index: uspace/dist/data/web/index.htm
===================================================================
--- uspace/dist/data/web/index.htm	(revision 4a4cc150f1b4177264d79d70d62494b685bb24c2)
+++ uspace/dist/data/web/index.htm	(revision 4a4cc150f1b4177264d79d70d62494b685bb24c2)
@@ -0,0 +1,18 @@
+<html>
+    <head>
+        <title>
+    	    Hello from HelenOS!
+        </title>
+    </head>
+    <body>
+        <h1>Hello from HelenOS!</h1>
+        <p>
+            This web page is brought to you by courtesy of HelenOS web server
+    	    and TCP/IP stack.
+        </p>
+	<p>
+    	    Now <a href="foo.htm">go to page foo</a> or <a href="bar.htm">go
+    	    to bar</a>.
+        </p>
+    </body>
+</html>
