Skip to content

Instantly share code, notes, and snippets.

@rmccullagh
Created June 5, 2015 06:26
Show Gist options
  • Save rmccullagh/05107b7515df4afcd85a to your computer and use it in GitHub Desktop.
Save rmccullagh/05107b7515df4afcd85a to your computer and use it in GitHub Desktop.
Hello HTTP server, that only implements GET|HEAD requests
/*
* Copyright (c) 2015 Ryan McCullagh <me@ryanmccullagh.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <smart_string.h>
#define D_BUFFER_SIZE 1024
static void __attribute__ ((noreturn)) _socket_error(int last_errno, const char* file, const char* function, int lineno)
{
fprintf(stderr, "%s:%s:%d Errno %d: %s\n", file, function, lineno, last_errno, strerror(last_errno));
exit(1);
}
#define socket_error(last_errno) do { \
_socket_error(last_errno, __FILE__, __FUNCTION__, __LINE__); \
} \
while(0)
int sock_fd;
int accept_fd;
struct sockaddr_in server;
struct sockaddr_in client;
void sig_handler(int sig)
{
fprintf(stderr, "debug: got SIGINT, shutting down\n");
close(sock_fd);
exit(0);
}
static bool is_end(SmartString* buffer)
{
if(buffer->length >= 4) {
if(buffer->buffer[buffer->length-1] == '\n') {
if(buffer->buffer[buffer->length-2] == '\r') {
if(buffer->buffer[buffer->length-3] == '\n') {
if(buffer->buffer[buffer->length-4] == '\r') {
return true;
}
}
}
}
}
return false;
}
int main()
{
signal(SIGINT, sig_handler);
int option = 1;
/* domain, type, protocol */
if((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
socket_error(errno);
}
if(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)) < 0) {
socket_error(errno);
}
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(8080);
if(bind(sock_fd, (struct sockaddr*)&server, sizeof(server)) < 0) {
socket_error(errno);
}
if(listen(sock_fd, 4) < 0) {
socket_error(errno);
}
fprintf(stderr, "debug: listening on %s:%d\n",
inet_ntoa(server.sin_addr), ntohs(server.sin_port));
fprintf(stderr, "debug: entering main loop\n");
for(;;) {
size_t client_len = sizeof(client);
if((accept_fd = accept(sock_fd, (struct sockaddr*)&client, &client_len)) < 0) {
fprintf(stderr, "debug: accept failed with errno %d, %s, continue\n", errno, strerror(errno));
continue;
}
fprintf(stderr, "debug: accept connection from %s:%d\n", inet_ntoa(client.sin_addr),
ntohs(client.sin_port));
SmartString* buffer = smart_string_new();
if(!buffer) {
fprintf(stderr, "debug: smart_string_new failed for the request buffer, closing client connection, continue\n");
close(accept_fd);
continue;
}
/* maybe fork here? */
for(;;) {
char temp[D_BUFFER_SIZE];
ssize_t bytes = recv(accept_fd, temp, sizeof(buffer), 0);
temp[bytes] = '\0';
if(bytes < 0) {
fprintf(stderr, "debug: recv returned < 0\n");
close(accept_fd);
break;
} else if(bytes > 0) {
if(!smart_string_append(buffer, temp)) {
fprintf(stderr, "smart_string_append returned false\n");
close(accept_fd);
break;
}
fprintf(stderr, "debug: recieved %zu bytes from client\n", (size_t)bytes);
if(is_end(buffer)) {
fprintf(stderr, "debug: recieved termination sequence from client\n");
fprintf(stderr, "debug: sending response to client\n");
/*
* write client resonse here
*/
SmartString* response_headers = smart_string_new();
if(!response_headers) {
fprintf(stderr, "debug: smart_string_new failed for the response_headers buffer, closing client connection, continue\n");
close(accept_fd);
break;
}
SmartString* response_body = smart_string_new();
if(!response_body) {
fprintf(stderr, "debug: smart_string_new failed for the response_body buffer, closing client connection, continue\n");
close(accept_fd);
smart_string_destroy(response_headers);
break;
}
smart_string_append(response_body,"Hello");
smart_string_append(response_headers, "HTTP/1.1 200 OK\r\n");
smart_string_append(response_headers, "Server: como\r\n");
smart_string_append_sprintf(response_headers, "Content-Length: %d\r\n", response_body->length);
smart_string_append(response_headers, "Content-Type: text/html; charset=utf-8\r\n");
smart_string_append(response_headers, "\r\n");
ssize_t bytes_sent = send(accept_fd, response_headers->buffer, response_headers->length, 0);
fprintf(stderr, "debug: sent %d bytes to client\n", bytes_sent);
if(bytes_sent < 0) {
fprintf(stderr, "debug: errno %d: %s\n", errno, strerror(errno));
}
bytes_sent = send(accept_fd, response_body->buffer, response_body->length, 0);
fprintf(stderr, "debug: sent %d bytes to client\n", bytes_sent);
if(bytes_sent < 0) {
fprintf(stderr, "debug: errno %d: %s\n", errno, strerror(errno));
}
smart_string_destroy(response_headers);
smart_string_destroy(response_body);
close(accept_fd);
fprintf(stderr,"debug: closing client connection\n");
break;
}
} else if(bytes == 0) {
fprintf(stderr, "debug: client has closed connection\n");
close(accept_fd);
break;
} else {
if(errno == EINTR) {
fprintf(stderr, "debug: got errno %d: %s, trying again\n", errno, strerror(errno));
continue;
}
}
}
smart_string_destroy(buffer);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment