Static HTTP Server

A few illustrated reminds about simple static http server.
For complete explanations, please have a look at : https://web.enib.fr/~harrouet


Simple HTTP Request / Response

code_hello.zip

simple_http_1.svg

simple_http_2.svg

httpServer_hello.c
// CREDITS : F. Harrouet, www.enib.fr/~harrouet
//======================================================================
#include "main.h"
#include "httpServerLib.h"

#define BUFFER_SIZE 0x1000

//======================================================================
void * dialogThread(void *arg)
{
	pthread_detach(pthread_self());
	char buffer[BUFFER_SIZE];
	Request *req=(Request *)arg;
	//---- receive and analyse HTTP request line by line ----
	bool first=true;
	for(;;)
	  {
		  if(recvLine(req->sock,buffer,BUFFER_SIZE)<=0) { break; }
		  printf("%s",buffer);
		  if(first)
			{
					first=false;
					sscanf(buffer,"%s %s",req->requestMethod,req->requestUri);
			}
		  else if(!strcmp(buffer,"\n")||!strcmp(buffer,"\r\n"))
			{
					break;
			}
	  }
	//---- prepare and send HTTP reply ----
	int r=sprintf(buffer,"HTTP/1.0 200 OK\n"
						 "Connection: close\n"
						 "Content-Type: text/html\n"
						 "\n"
						 "<html><body>\n"	// Text displayed in the web browser :
						 "<b>200 - OK</b><br>\n"
						 "method: %s<br>\n"
						 "uri: %s<br>\n"
						 "hello world\n"
						 "</body></html>\n",
						 req->requestMethod,
						 req->requestUri);
	sendAll(req->sock,buffer,r);

	destroyRequest(req);
	return (void *)0;
}

//======================================================================
//						MAIN
//======================================================================

int main(int argc, char **argv)
{
	//---- avoid exiting on broken client connection (ignore SIGPIPE) ----
	struct sigaction sa;
	memset(&sa,0,sizeof(struct sigaction));
	sa.sa_handler=SIG_IGN;
	if(sigaction(SIGPIPE,&sa,(struct sigaction *)0)==-1)
											{ perror("sigaction"); exit(1); }

	//---- check command line arguments ----
	if(argc!=2)
											{ fprintf(stderr,"usage: %s http_port\n",argv[0]); exit(1); }

	//---- extract local port number ----
	int httpPortNumber;
	if(sscanf(argv[1],"%d",&httpPortNumber)!=1)
											{ fprintf(stderr,"invalid port %s\n",argv[1]); exit(1); }

	//---- multithreaded TCP server launching dialogThread() ----

	//---- create HTTP listen socket ----
	int httpSocket=socket(PF_INET,SOCK_STREAM,0);
	if(httpSocket==-1)
											{ perror("socket"); exit(1); }
	// ... avoiding timewait problems (optional)
	int on=1;
	if(setsockopt(httpSocket,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(int))==-1)
											{ perror("setsockopt"); exit(1); }
	// ... bound to any local address on the specified port
	struct sockaddr_in myAddr;
	myAddr.sin_family=AF_INET;
	myAddr.sin_port=htons(httpPortNumber);
	myAddr.sin_addr.s_addr=htonl(INADDR_ANY);
	if(bind(httpSocket,(struct sockaddr *)&myAddr,sizeof(myAddr))==-1)
											{ perror("bind"); exit(1); }
	// ... listening connections
	if(listen(httpSocket,10)==-1)
											{ perror("listen"); exit(1); }

	for(;;)
	  {
		  //---- accept new HTTP connection ----
		  struct sockaddr_in fromAddr;
		  socklen_t len=sizeof(fromAddr);
		  int dialogSocket=accept(httpSocket,(struct sockaddr *)&fromAddr,&len);
		  if(dialogSocket==-1)
												{ perror("accept"); exit(1); }
		  printf("new HTTP connection from %s:%d\n",
							inet_ntoa(fromAddr.sin_addr),ntohs(fromAddr.sin_port));

		  //---- start a new dialog thread ----
		  pthread_t th;
		  if(pthread_create(&th,(pthread_attr_t *)0, dialogThread,createRequest(dialogSocket)))
												{ fprintf(stderr,"cannot create thread\n"); exit(1); }
	  }

	close(httpSocket);
	return 0;
}

//^^^^^^^^^^^^^^^^^^^^^^^^^^ EOF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# RPI Terminal

$ ./exec_httpServer_hello 5555
new HTTP connection from 192.168.0.1:40120
GET / HTTP/1.1
Host: 192.168.0.2:5555
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0

HTTP Static Server

code_file.zip

http_static_server_1.svg

http_static_server_2.svg

http_static_server_3.svg

http_static_server_4.svg

http_static_server_5.svg

httpServer_file.c
// CREDITS : F. Harrouet, www.enib.fr/~harrouet
//======================================================================
#include "main.h"
#include "httpServerLib.h"

#define BUFFER_SIZE 0x1000

//======================================================================
void * dialogThread(void *arg)
{
	pthread_detach(pthread_self());
	char buffer[BUFFER_SIZE];
	Request *req=(Request *)arg;
	//---- receive and analyse HTTP request line by line ----
	bool first=true;
	
	for(;;)
	  {
		  if(recvLine(req->sock,buffer,BUFFER_SIZE)<=0) { break; }
		  printf("%s",buffer);
		  if(first)
			{
				first=false;
				sscanf(buffer,"%s %s",req->requestMethod,req->requestUri);
			}
		  else if(!strcmp(buffer,"\n")||!strcmp(buffer,"\r\n"))
			{
				break;
			}
	  }
	//---- prepare and send HTTP reply ----
	sprintf(req->fileName,"./www%s",req->requestUri);
	char *params=strchr(req->fileName,'?');
	if(params) { *params='\0'; }
	struct stat st;
	int r=stat(req->fileName,&st);
	if((r!=-1)&&S_ISDIR(st.st_mode))
	  {
		  strcat(req->fileName,"/index.html");
		  r=stat(req->fileName,&st);
	  }
	int input=open(req->fileName,O_RDONLY);
		
	
	if(input==-1)
	  {
				  r=sprintf(buffer,"HTTP/1.0 404 Not Found\n"
								   "Connection: close\n"
								   "Content-Type: text/html\n"
								   "\n"
								   "<html><body>\n"
								   "<b>404 - Not Found</b><br>\n"
								   "method: %s<br>\n"
								   "uri: %s<br>\n"
								   "fileName: %s<br>\n"
								   "</body></html>\n",
								   req->requestMethod,
								   req->requestUri,
								   req->fileName);
				  sendAll(req->sock,buffer,r);
	  }
	else
	  {
				  const char *contentType="unknown/unknown";
				  const char *ext=strrchr(req->fileName,'.');
				  if(ext)
					{
						if(!strcmp(ext,".html"))     contentType="text/html";
						else if(!strcmp(ext,".png")) contentType="image/png";
						else if(!strcmp(ext,".ico")) contentType="image/vnd.microsoft.icon";
					}
				  r=sprintf(buffer,"HTTP/1.0 200 OK\n"
								   "Connection: close\n"
								   "Content-Type: %s\n"
								   "Content-Length: %ld\n\n",
								   contentType,
								   st.st_size);
				  sendAll(req->sock,buffer,r);
				  for(;;)
					{
						r=read(input,buffer,BUFFER_SIZE);
						if(r<=0) break;
						sendAll(req->sock,buffer,r);
					}
				  close(input);
	  }

	destroyRequest(req);
	return (void *)0;
	}

//======================================================================
//						MAIN
//======================================================================

int main(int argc, char **argv)
{
	//---- avoid exiting on broken client connection (ignore SIGPIPE) ----
	struct sigaction sa;
	memset(&sa,0,sizeof(struct sigaction));
	sa.sa_handler=SIG_IGN;
	if(sigaction(SIGPIPE,&sa,(struct sigaction *)0)==-1)
											{ perror("sigaction"); exit(1); }

	//---- check command line arguments ----
	if(argc!=2)
											{ fprintf(stderr,"usage: %s http_port\n",argv[0]); exit(1); }

	//---- extract local port number ----
	int httpPortNumber;
	if(sscanf(argv[1],"%d",&httpPortNumber)!=1)
											{ fprintf(stderr,"invalid port %s\n",argv[1]); exit(1); }

	//---- multithreaded TCP server launching dialogThread() ----

	//---- create HTTP listen socket ----
	int httpSocket=socket(PF_INET,SOCK_STREAM,0);
	if(httpSocket==-1)
											{ perror("socket"); exit(1); }
	// ... avoiding timewait problems (optional)
	int on=1;
	if(setsockopt(httpSocket,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(int))==-1)
											{ perror("setsockopt"); exit(1); }
	// ... bound to any local address on the specified port
	struct sockaddr_in myAddr;
	myAddr.sin_family=AF_INET;
	myAddr.sin_port=htons(httpPortNumber);
	myAddr.sin_addr.s_addr=htonl(INADDR_ANY);
	if(bind(httpSocket,(struct sockaddr *)&myAddr,sizeof(myAddr))==-1)
											{ perror("bind"); exit(1); }
	// ... listening connections
	if(listen(httpSocket,10)==-1)
											{ perror("listen"); exit(1); }

	for(;;)
	  {
		  //---- accept new HTTP connection ----
		  struct sockaddr_in fromAddr;
		  socklen_t len=sizeof(fromAddr);
		  int dialogSocket=accept(httpSocket,(struct sockaddr *)&fromAddr,&len);
		  if(dialogSocket==-1)
												{ perror("accept"); exit(1); }
		  printf("new HTTP connection from %s:%d\n",
							inet_ntoa(fromAddr.sin_addr),ntohs(fromAddr.sin_port));

		  //---- start a new dialog thread ----
		  pthread_t th;
		  if(pthread_create(&th,(pthread_attr_t *)0, dialogThread,createRequest(dialogSocket)))
												{ fprintf(stderr,"cannot create thread\n"); exit(1); }
	  }

	close(httpSocket);
	return 0;
}

//^^^^^^^^^^^^^^^^^^^^^^^^^^ EOF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# RPI Terminal

$ ./exec_httpServer_file 5555
new HTTP connection from 192.168.0.1:40164
GET /index.html HTTP/1.1
Host: 192.168.0.2:5555
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1

new HTTP connection from 192.168.0.1:40166
GET /favicon.ico HTTP/1.1
Host: 192.168.0.2:5555
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0
Accept: */*
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive

# After clicking on  "static page"  

new HTTP connection from 192.168.0.1:40168
GET /page.html HTTP/1.1
Host: 192.168.0.2:5555
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.0.2:5555/index.html
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1