GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

No menu could be recovered. Follow the blue links in the page.

This view

  • Original page
  • New address

GeezerVison is showing raw.githubusercontent.com — raw.githubusercontent.com

// // Copyright (c) 2023 vorakl // SPDX-License-Identifier: Apache-2.0 // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.hpp" #include "httpserver.hpp" int get_tcp_listener(sa_family_t af, in_addr_t hostaddr, in_port_t port) { int srv_sock; sockaddr_in srv_addr{}; // initialize with all zeros!!! const int on = 1; // Define IP protocol, Address, and Port srv_addr.sin_family = af; srv_addr.sin_addr.s_addr = htonl(hostaddr); srv_addr.sin_port = htons(port); // Create a socket for accepting TCP connections if ((srv_sock = socket(af, SOCK_STREAM, 0)) == -1) { handle_error("get_tcp_listener::socket"); } // Set socket options if (setsockopt(srv_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { handle_error("get_tcp_listener::setsockopt"); } // Attach a socket to a local network interface if (bind(srv_sock, (sockaddr *) &srv_addr, sizeof(sockaddr)) == -1) { handle_error("get_tcp_listener::bind"); } // begin accepting connections on the socket if (listen(srv_sock, BACKLOG_CONN) == -1) { handle_error("get_tcp_listener::listen"); } return srv_sock; } int accept_tcp_conn(int srv_sock, sockaddr_in* peer_addr) { int peer_sock; socklen_t addr_len = sizeof(sockaddr); if ((peer_sock = accept(srv_sock, (sockaddr*) peer_addr, &addr_len)) == -1) { if (errno != EINTR) { handle_error("accept_tcp_connection::accept"); } } return peer_sock; } void* serve_in_thread(void* arg) { connInfo* ci = (connInfo*) arg; http_req_t http_req{}; std::string req_header; char msg[MSG_SIZE]{}; req_header = get_req_header(ci); if (req_header == "") { http_response_400(ci); // There is no a header goto finish; } else { strncpy(msg, req_header.c_str(), MSG_SIZE-1); } if (parse_http_req(&http_req, msg) == -1) { http_response_400(ci); // There is no a header goto finish; } else { process_http_req(ci, &http_req); } finish: close(ci->sock); // Close the TCP connection! delete ci; ci = nullptr; return nullptr; } int parse_http_req(http_req_t* req, char* str) { // str format: 'HTTP/\n' char* token; char tmp[VER_SIZE]{}; char buf[BUF_SIZE]{}; char buf2[BUF_SIZE]{}; // get verb token = strtok(str, " \t\n\r"); if (token != nullptr) { memset(req->verb, 0, VERB_SIZE); strncpy(req->verb, token, VERB_SIZE-1); } else { return -1; } // get uri token = strtok(nullptr, " \t\n\r"); if (token != nullptr) { memset(req->uri, 0, URI_SIZE); strncpy(req->uri, token, URI_SIZE-1); } else { return -1; } // get HTTP version token = strtok(nullptr, " \t\n\r"); if (token != nullptr) { strncpy(tmp, token, VER_SIZE-1); token = strtok(tmp, "/"); token = strtok(nullptr, "/"); if (token != nullptr) { memset(req->ver, 0, VER_SIZE); strcpy(req->ver, token); } else { return -1; } } else { return -1; } // extract QUERY STRING (a part after ?) memset(buf, 0, BUF_SIZE); memset(buf2, 0, BUF_SIZE); strncpy(buf2, req->uri, BUF_SIZE-1); token = strtok(buf2, "?"); if (token != nullptr) { // before ? symbol strncpy(buf, token, BUF_SIZE-1); // get QUERY_STRING token = strtok(nullptr, "?"); if (token != nullptr) { strncpy(req->query_str, token, QUERYSTR_SIZE-1); } else { memset(req->query_str, 0, QUERYSTR_SIZE); } } else { strncpy(buf, req->uri, BUF_SIZE-1); } // Extract full file name std::string s1(buf); std::regex rerep ("^https?://"); s1 = std::regex_replace(s1, rerep, ""); // remove https?:// std::regex re1("^[^/]*(/.*)$"); std::smatch m1; if (std::regex_match(s1, m1, re1)) { strncpy(req->fpath, m1[1].str().c_str(), FNAME_SIZE-1); } // Extract an extension memset(buf, 0, BUF_SIZE); memset(req->ext, 0, EXT_SIZE); strncpy(buf, req->fpath, BUF_SIZE-1); token = strtok(buf, "."); while (token != nullptr) { strncpy(req->ext, token, EXT_SIZE-1); token = strtok(nullptr, "."); } return 0; } void http_response_400(const connInfo* ci) { char buf[BUF_SIZE]{}; sprintf(buf, "400 Bad request"); log(ci->pipeWr, ci->mutex, buf); sprintf(buf, "HTTP/1.1 400 Bad request\r\nConnection: Close\r\nContent-Length: 0\r\n\r\n"); write(ci->sock, buf, strlen(buf)); } void http_response_404(const connInfo* ci, const http_req_t* req) { char msg[MSG_SIZE]{}; sprintf(msg, "%s %s 404 File not found", req->verb, req->uri); log(ci->pipeWr, ci->mutex, msg); memset(msg, 0, MSG_SIZE); sprintf(msg, "HTTP/%s 404 File not found\r\nConnection: Close\r\nContent-Length: 0\r\n\r\n", req->ver); write(ci->sock, msg, strlen(msg)); } void http_response_500(const connInfo* ci, const http_req_t* req) { char msg[MSG_SIZE]{}; sprintf(msg, "%s %s 500 Internal Server Error", req->verb, req->uri); log(ci->pipeWr, ci->mutex, msg); memset(msg, 0, MSG_SIZE); sprintf(msg, "HTTP/%s 500 Internal Server Error\r\nConnection: Close\r\nContent-Length: 0\r\n\r\n", req->ver); write(ci->sock, msg, strlen(msg)); } void http_response_quit(const connInfo* ci, const http_req_t* req) { char msg[MSG_SIZE]{}; const char* shutdownMsg = "Received a QUIT request. Shutting Down..."; sprintf(msg, "%s %s 200 OK content-type: text/plain content-length: %li", req->verb, req->uri, strlen(shutdownMsg)); log(ci->pipeWr, ci->mutex, msg); log(ci->pipeWr, ci->mutex, shutdownMsg); memset(msg, 0, MSG_SIZE); sprintf(msg, "HTTP/1.1 200 OK\r\nConnection: Close\r\nContent-Type: text/plain\r\nContent-Length: %li\r\n\r\n%s", strlen(shutdownMsg), shutdownMsg); write(ci->sock, msg, strlen(msg)); } void process_http_req(const connInfo* ci, const http_req_t* req) { if (strcmp(req->fpath, "/quit") == 0) { // tell the main process to stop gracefully and exit http_response_quit(ci, req); kill(getpid(), SIGTERM); } else { http_try_files(ci, req); // Look for files in the DocumentRoot } } void http_try_files(const connInfo* ci, const http_req_t* req) { char fn[FNAME_SIZE]{}; char fn_send[FNAME_SIZE*2]{}; char ex[EXT_SIZE]{}; size_t fs = 0; char ft[FTYPE_SIZE]{}; char msg[MSG_SIZE]{}; bool type_found = false; struct stat file_stat; int fd; int bsent = 0; off_t offset = 0; int bleft; // Check for the DirectoryIndex if (req->fpath[strlen(req->fpath)-1] == '/') { strcpy(fn, req->fpath); strncat(fn, DIRECTORY_INDEX, FNAME_SIZE-1); strcpy(ex, "html"); } else { strcpy(fn, req->fpath); strcpy(ex, req->ext); } // Look up a file strcpy(fn_send, ci->doc_root); strncat(fn_send, fn, FNAME_SIZE); restart_syscall: fd = open(fn_send, O_RDONLY); if (fd == -1) { if (errno == EINTR) { goto restart_syscall; } // file not found http_response_404(ci, req); goto finish; } else { if (fstat(fd, &file_stat) == -1) { // unable to get info about a file http_response_500(ci, req); goto finish; } fs = file_stat.st_size; } // Search for a file type for (int i=0; i <= FTYPES_COUNT; ++i) { if (strcmp(ex, file_types[i].ext) == 0) { strcpy(ft, file_types[i].type); type_found = true; } } if (! type_found) { strcpy(ft, "application/octet-stream"); } // send logs memset(msg, 0, MSG_SIZE); sprintf(msg, "%s %s 200 OK content-type: %s content-length: %li", req->verb, req->uri, ft, fs); log(ci->pipeWr, ci->mutex, msg); // send a header memset(msg, 0, MSG_SIZE); sprintf(msg, "HTTP/%s 200 OK\r\nConnection: Close\r\nContent-Type: %s\r\nContent-Length: %li\r\n\r\n", req->ver, ft, fs); write(ci->sock, msg, strlen(msg)); // send a file bleft = fs; while (((bsent = sendfile(ci->sock, fd, &offset, BUFSIZ)) > 0) && (bleft > 0)) { bleft -= bsent; } finish: return; } std::string get_req_header(const connInfo* ci) { bool exit_loop = false; ssize_t cnt; // a number of read bytes char buf[BUF_SIZE]{}; char buf2[BUF_SIZE]{}; std::vector req_data; // Reading from the socket while (! exit_loop) { cnt = read(ci->sock, buf, BUF_SIZE-1); if (cnt == -1) { perror("serve_in_thread::read"); } else if (cnt == 0) { exit_loop = true; // TCP client disconnected } else { buf[cnt] = '\0'; // make a string from a byte array memset(buf2, 0, BUF_SIZE); for (int i = 0, k = 0; buf[i] != '\0'; ++i) { if (buf[i] == '\r' && buf[i+1] == '\n') { // check in a line ends with \r\n if (strlen(buf2) == 0) { // Header has finished. Exit and go processing it exit_loop = true; break; } req_data.push_back(std::string(buf2)); k = 0; ++i; memset(buf2, 0, BUF_SIZE); continue; } else if (buf[i] == '\n') { // check in a line ends with \n if (strlen(buf2) == 0) { // Header has finished. Exit and go processing it exit_loop = true; break; } req_data.push_back(std::string(buf2)); k = 0; memset(buf2, 0, BUF_SIZE); continue; } buf2[k++] = buf[i]; } if (strlen(buf2) != 0) { req_data.push_back(std::string(buf2)); } } } if (req_data.size() >= 1) { return req_data[0]; // Get only 1st line of a header for parsing } else { return ""; } }
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.