text: /*
   * MIT License
   *
   * Copyright (c) 2018 Lewis Van Winkle
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   */
  
  #ifdef _WIN32
  #define WINDOWS
  #ifndef _WIN32_WINNT
  #define _WIN32_WINNT 0x0600
  #endif
  #include <winsock2.h>
  #include <ws2tcpip.h>
  
  #pragma comment(lib, "ws2_32.lib")
  
  #define ISVALIDSOCKET(s) ((s) != INVALID_SOCKET)
  #define CLOSESOCKET(s) closesocket(s)
  #define GETSOCKETERRNO() (WSAGetLastError())
  
  #else
  #define LINUX
  #include <arpa/inet.h>
  #include <errno.h>
  #include <netdb.h>
  #include <netinet/in.h>
  #include <sys/socket.h>
  #include <sys/types.h>
  #include <unistd.h>
  
  #define ISVALIDSOCKET(s) ((s) >= 0)
  #define CLOSESOCKET(s) close(s)
  #define SOCKET int
  #define GETSOCKETERRNO() (errno)
  
  #endif
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <time.h>
  
  int main(void) {
  
    const int MAX_CONN = 10; // Maximum number of simultaneous clients
  
  #ifdef WINDOWS
    WSADATA d;
    if (WSAStartup(MAKEWORD(2, 2), &d)) {
      fprintf(stderr, "Error: Failed to initialize Winsock.\n");
      exit(EXIT_FAILURE);
    }
  #endif
  
    puts("Configuring local address...");
    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
  
    struct addrinfo *iface_bind;
    getaddrinfo(0, "8080", &hints, &iface_bind);
  
    puts("Creating socket...");
    SOCKET server_sock; // Type SOCKET on MS Windows and int on GNU/Linux
    server_sock = socket(iface_bind->ai_family, iface_bind->ai_socktype,
                         iface_bind->ai_protocol);
    if (!ISVALIDSOCKET(server_sock)) {
      fprintf(stderr, "socket() failed. (%d)\n", GETSOCKETERRNO());
      exit(EXIT_FAILURE);
    }
    if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &(int){1},
                   sizeof(int)) < 0) {
      puts("setsockopt(SO_REUSEADDR) failed");
      exit(EXIT_FAILURE);
    }
  
    puts("Binding socket to interface...");
    if (bind(server_sock, iface_bind->ai_addr, iface_bind->ai_addrlen)) {
      fprintf(stderr, "bind() failed. (%d)\n", GETSOCKETERRNO());
      exit(EXIT_FAILURE);
    }
    freeaddrinfo(iface_bind);
  
    puts("Listening...");
    if (listen(server_sock, MAX_CONN) < 0) {
      fprintf(stderr, "listen() failed. (%d)\n", GETSOCKETERRNO());
      exit(EXIT_FAILURE);
    }
  
    puts("Waiting for connections from clients...");
    struct sockaddr_storage client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    SOCKET client_sock =
        accept(server_sock, (struct sockaddr *)&client_addr, &client_addr_len);
    if (!ISVALIDSOCKET(client_sock)) {
      fprintf(stderr, "accept() failed. (%d)\n", GETSOCKETERRNO());
      exit(EXIT_FAILURE);
    }
  
    puts("Client is connected...");
    char address_buffer[100];
    getnameinfo((struct sockaddr *)&client_addr, client_addr_len, address_buffer,
                sizeof(address_buffer), 0, 0, NI_NUMERICHOST);
    printf("%s\n", address_buffer);
  
    puts("Reading request...");
    char request[1024];
    int bytes_received;
  
    if ((bytes_received = recv(client_sock, request, 1024, 0)) < 1) {
      puts("Connection terminated by the client...");
    } else {
      printf("Received %d bytes.\n", bytes_received);
      printf("%.*s", bytes_received, request);
  
      puts("Sending response...");
      const char *response = "HTTP/1.1 200 OK\r\n"
                             "Connection: close\r\n"
                             "Content-Type: text/plain\r\n\r\n"
                             "Local time is: ";
      int bytes_sent = send(client_sock, response, strlen(response), 0);
      printf("Sent %d of %d bytes.\n", bytes_sent, (int)strlen(response));
  
      time_t timer;
      time(&timer);
      char *time_msg = ctime(&timer);
      bytes_sent = send(client_sock, time_msg, strlen(time_msg), 0);
      printf("Sent %d of %d bytes.\n", bytes_sent, (int)strlen(time_msg));
    }
  
    puts("Closing connection...");
    CLOSESOCKET(client_sock);
  
    puts("Closing listening socket...");
    CLOSESOCKET(server_sock);
  
  #ifdef WINDOWS
    WSACleanup();
  #endif
  
    puts("Finished.");
  
    exit(EXIT_SUCCESS);
  }
  