Created
June 5, 2015 06:26
-
-
Save rmccullagh/05107b7515df4afcd85a to your computer and use it in GitHub Desktop.
Hello HTTP server, that only implements GET|HEAD requests
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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