├── CacheStrategy.hpp ├── HTTPServer.hpp ├── LFUCache.cpp ├── LFUCache.hpp ├── LRUCache.cpp ├── LRUCache.hpp ├── ProxyUtils.cpp ├── ProxyUtils.hpp ├── README.md ├── SemaphoreServer.cpp ├── SemaphoreServer.hpp ├── ServerFactory.cpp ├── ServerFactory.hpp ├── ThreadPool.cpp ├── ThreadPool.hpp ├── ThreadPoolServer.cpp ├── ThreadPoolServer.hpp ├── benchmark.py ├── main.cpp ├── makefile ├── proxy_parse.c ├── proxy_parse.h ├── proxy_server └── v1 ├── proxy2.cpp ├── proxy3.cpp ├── proxy_server_with_cache.c ├── proxy_server_with_threadpool_and_cache.c └── proxy_server_without_cache.c /CacheStrategy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct cache_element 5 | { 6 | char *data; 7 | int len; 8 | char *url; 9 | time_t lru_time_track; 10 | }; 11 | 12 | class CacheStrategy 13 | { 14 | public: 15 | virtual ~CacheStrategy() = default; 16 | virtual cache_element *find(const char *url) = 0; 17 | virtual void add(const char *url, const char *data, int len) = 0; 18 | virtual void remove(const char *url) = 0; 19 | }; -------------------------------------------------------------------------------- /HTTPServer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "./CacheStrategy.hpp" 3 | #include 4 | 5 | class HTTPServer 6 | { 7 | public: 8 | HTTPServer(std::unique_ptr cache) : cache(std::move(cache)) {} 9 | virtual ~HTTPServer() = default; 10 | 11 | virtual void start(int port) = 0; 12 | 13 | protected: 14 | std::unique_ptr cache; 15 | }; -------------------------------------------------------------------------------- /LFUCache.cpp: -------------------------------------------------------------------------------- 1 | #include "./LFUCache.hpp" 2 | #include 3 | #include 4 | 5 | LFUCache::LFUCache(size_t cap) : capacity(cap) 6 | { 7 | frequency_list.push_back({0, {}}); 8 | } 9 | 10 | LFUCache::~LFUCache() 11 | { 12 | for (auto &node : frequency_list) 13 | { 14 | for (auto elem : node.elements) 15 | { 16 | free(elem->data); 17 | free(elem->url); 18 | delete elem; 19 | } 20 | } 21 | } 22 | 23 | cache_element *LFUCache::find(const char *url) 24 | { 25 | std::lock_guard lock(cache_mutex); 26 | auto it = cache_map.find(url); 27 | if (it == cache_map.end()) 28 | { 29 | return nullptr; 30 | } 31 | 32 | auto [node, element_it] = it->second; 33 | incrementFrequency(url); 34 | (*element_it)->lru_time_track = time(nullptr); 35 | return *element_it; 36 | } 37 | 38 | void LFUCache::add(const char *url, const char *data, int len) 39 | { 40 | std::lock_guard lock(cache_mutex); 41 | if (cache_map.find(url) != cache_map.end()) 42 | { 43 | incrementFrequency(url); 44 | return; 45 | } 46 | 47 | if (cache_map.size() >= capacity) 48 | { 49 | evict(); 50 | } 51 | 52 | cache_element *new_element = new cache_element; 53 | new_element->url = strdup(url); 54 | new_element->data = (char *)malloc(len); 55 | memcpy(new_element->data, data, len); 56 | new_element->len = len; 57 | new_element->lru_time_track = time(nullptr); 58 | 59 | auto &least_frequent = frequency_list.front(); 60 | least_frequent.elements.push_front(new_element); 61 | cache_map[url] = {&least_frequent, least_frequent.elements.begin()}; 62 | } 63 | 64 | void LFUCache::remove(const char *url) 65 | { 66 | std::lock_guard lock(cache_mutex); 67 | auto it = cache_map.find(url); 68 | if (it == cache_map.end()) 69 | { 70 | return; 71 | } 72 | 73 | auto [node, element_it] = it->second; 74 | cache_element *elem = *element_it; 75 | node->elements.erase(element_it); 76 | cache_map.erase(it); 77 | 78 | free(elem->data); 79 | free(elem->url); 80 | delete elem; 81 | } 82 | 83 | void LFUCache::incrementFrequency(const std::string &url) 84 | { 85 | auto [current_node, element_it] = cache_map[url]; 86 | auto next_frequency = current_node->frequency + 1; 87 | auto next_it = std::next(std::find_if(frequency_list.begin(), frequency_list.end(), 88 | [next_frequency](const FrequencyNode &node) 89 | { return node.frequency >= next_frequency; })); 90 | 91 | if (next_it == frequency_list.end() || next_it->frequency != next_frequency) 92 | { 93 | next_it = frequency_list.insert(next_it, {next_frequency, {}}); 94 | } 95 | 96 | next_it->elements.splice(next_it->elements.begin(), current_node->elements, element_it); 97 | cache_map[url] = {&*next_it, next_it->elements.begin()}; 98 | 99 | if (current_node->elements.empty() && current_node != &frequency_list.front()) 100 | { 101 | frequency_list.erase(std::find_if(frequency_list.begin(), frequency_list.end(), 102 | [current_node](const FrequencyNode &node) 103 | { return &node == current_node; })); 104 | } 105 | } 106 | 107 | void LFUCache::evict() 108 | { 109 | auto least_frequent = frequency_list.begin(); 110 | while (least_frequent->elements.empty()) 111 | { 112 | ++least_frequent; 113 | } 114 | 115 | auto victim = least_frequent->elements.back(); 116 | cache_map.erase(victim->url); 117 | least_frequent->elements.pop_back(); 118 | 119 | free(victim->data); 120 | free(victim->url); 121 | delete victim; 122 | 123 | if (least_frequent->elements.empty() && least_frequent != frequency_list.begin()) 124 | { 125 | frequency_list.erase(least_frequent); 126 | } 127 | } -------------------------------------------------------------------------------- /LFUCache.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "./CacheStrategy.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class LFUCache : public CacheStrategy 9 | { 10 | public: 11 | LFUCache(size_t capacity); 12 | ~LFUCache() override; 13 | 14 | cache_element *find(const char *url) override; 15 | void add(const char *url, const char *data, int len) override; 16 | void remove(const char *url) override; 17 | 18 | private: 19 | struct FrequencyNode 20 | { 21 | int frequency; 22 | std::list elements; 23 | }; 24 | 25 | size_t capacity; 26 | std::unordered_map::iterator>> cache_map; 27 | std::list frequency_list; 28 | std::mutex cache_mutex; 29 | 30 | void incrementFrequency(const std::string &url); 31 | void evict(); 32 | }; -------------------------------------------------------------------------------- /LRUCache.cpp: -------------------------------------------------------------------------------- 1 | #include "./LRUCache.hpp" 2 | #include 3 | 4 | LRUCache::LRUCache(size_t cap) : capacity(cap) {} 5 | 6 | LRUCache::~LRUCache() 7 | { 8 | for (auto elem : cache_list) 9 | { 10 | free(elem->data); 11 | free(elem->url); 12 | delete elem; 13 | } 14 | } 15 | 16 | cache_element *LRUCache::find(const char *url) 17 | { 18 | std::lock_guard lock(cache_mutex); 19 | auto it = cache_map.find(url); 20 | if (it == cache_map.end()) 21 | return nullptr; 22 | 23 | moveToFront(it->second); 24 | (*it->second)->lru_time_track = time(nullptr); 25 | return *it->second; 26 | } 27 | 28 | void LRUCache::add(const char *url, const char *data, int len) 29 | { 30 | std::lock_guard lock(cache_mutex); 31 | auto it = cache_map.find(url); 32 | if (it != cache_map.end()) 33 | { 34 | moveToFront(it->second); 35 | return; 36 | } 37 | 38 | if (cache_list.size() >= capacity) 39 | { 40 | evict(); 41 | } 42 | 43 | cache_element *new_element = new cache_element; 44 | new_element->url = strdup(url); 45 | new_element->data = (char *)malloc(len); 46 | memcpy(new_element->data, data, len); 47 | new_element->len = len; 48 | new_element->lru_time_track = time(nullptr); 49 | 50 | cache_list.push_front(new_element); 51 | cache_map[url] = cache_list.begin(); 52 | } 53 | 54 | void LRUCache::remove(const char *url) 55 | { 56 | std::lock_guard lock(cache_mutex); 57 | auto it = cache_map.find(url); 58 | if (it == cache_map.end()) 59 | return; 60 | 61 | cache_element *elem = *it->second; 62 | cache_list.erase(it->second); 63 | cache_map.erase(it); 64 | 65 | free(elem->data); 66 | free(elem->url); 67 | delete elem; 68 | } 69 | 70 | void LRUCache::moveToFront(std::list::iterator it) 71 | { 72 | cache_list.splice(cache_list.begin(), cache_list, it); 73 | } 74 | 75 | void LRUCache::evict() 76 | { 77 | cache_element *elem = cache_list.back(); 78 | cache_map.erase(elem->url); 79 | cache_list.pop_back(); 80 | 81 | free(elem->data); 82 | free(elem->url); 83 | delete elem; 84 | } -------------------------------------------------------------------------------- /LRUCache.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "./CacheStrategy.hpp" 3 | #include 4 | #include 5 | #include 6 | 7 | class LRUCache : public CacheStrategy 8 | { 9 | public: 10 | LRUCache(size_t capacity); 11 | ~LRUCache() override; 12 | 13 | cache_element *find(const char *url) override; 14 | void add(const char *url, const char *data, int len) override; 15 | void remove(const char *url) override; 16 | 17 | private: 18 | size_t capacity; 19 | std::list cache_list; 20 | std::unordered_map::iterator> cache_map; 21 | std::mutex cache_mutex; 22 | 23 | void moveToFront(std::list::iterator it); 24 | void evict(); 25 | }; -------------------------------------------------------------------------------- /ProxyUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "ProxyUtils.hpp" 2 | extern "C" 3 | { 4 | #include "proxy_parse.h" 5 | } 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "./CacheStrategy.hpp" 13 | 14 | int createServerSocket(int port) 15 | { 16 | int sockfd = socket(AF_INET, SOCK_STREAM, 0); 17 | if (sockfd < 0) 18 | { 19 | perror("Error creating socket"); 20 | return -1; 21 | } 22 | 23 | struct sockaddr_in serverAddr; 24 | memset(&serverAddr, 0, sizeof(serverAddr)); 25 | serverAddr.sin_family = AF_INET; 26 | serverAddr.sin_addr.s_addr = INADDR_ANY; 27 | serverAddr.sin_port = htons(port); 28 | 29 | if (bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) 30 | { 31 | perror("Error binding socket"); 32 | close(sockfd); 33 | return -1; 34 | } 35 | 36 | if (listen(sockfd, 100) < 0) 37 | { 38 | perror("Error listening on socket"); 39 | close(sockfd); 40 | return -1; 41 | } 42 | 43 | return sockfd; 44 | } 45 | 46 | // Implement other functions (convertRequestToString, createServerSocket, writeToServerSocket, writeToClientSocket, writeToClient) here 47 | // These implementations should be based on the original code provided in the question 48 | 49 | char *convertRequestToString(struct ParsedRequest *req) 50 | { 51 | // Set headers 52 | ParsedHeader_set(req, "Host", req->host); 53 | ParsedHeader_set(req, "Connection", "close"); 54 | 55 | int headersLen = ParsedHeader_headersLen(req); 56 | char *headersBuf = (char *)malloc(headersLen + 1); 57 | if (!headersBuf) 58 | { 59 | perror("Failed to allocate memory for headers"); 60 | return nullptr; 61 | } 62 | 63 | ParsedRequest_unparse_headers(req, headersBuf, headersLen); 64 | headersBuf[headersLen] = '\0'; 65 | 66 | int requestSize = strlen(req->method) + strlen(req->path) + strlen(req->version) + headersLen + 4; 67 | char *serverReq = (char *)malloc(requestSize + 1); 68 | if (!serverReq) 69 | { 70 | perror("Failed to allocate memory for server request"); 71 | free(headersBuf); 72 | return nullptr; 73 | } 74 | 75 | snprintf(serverReq, requestSize + 1, "%s %s %s\r\n%s", req->method, req->path, req->version, headersBuf); 76 | 77 | free(headersBuf); 78 | return serverReq; 79 | } 80 | 81 | int createServerSocket(const char *address, const char *port) 82 | { 83 | struct addrinfo hints, *res; 84 | memset(&hints, 0, sizeof hints); 85 | hints.ai_family = AF_UNSPEC; 86 | hints.ai_socktype = SOCK_STREAM; 87 | 88 | if (getaddrinfo(address, port, &hints, &res) != 0) 89 | { 90 | perror("getaddrinfo failed"); 91 | return -1; 92 | } 93 | 94 | int sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 95 | if (sockfd < 0) 96 | { 97 | perror("Failed to create socket"); 98 | freeaddrinfo(res); 99 | return -1; 100 | } 101 | 102 | if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) 103 | { 104 | perror("Failed to connect"); 105 | close(sockfd); 106 | freeaddrinfo(res); 107 | return -1; 108 | } 109 | 110 | freeaddrinfo(res); 111 | return sockfd; 112 | } 113 | 114 | void writeToSocket(const char *buff, int sockfd, int length) 115 | { 116 | int totalSent = 0; 117 | while (totalSent < length) 118 | { 119 | int sent = send(sockfd, buff + totalSent, length - totalSent, 0); 120 | if (sent < 0) 121 | { 122 | perror("Error in sending data"); 123 | return; 124 | } 125 | totalSent += sent; 126 | } 127 | } 128 | 129 | void writeToServerSocket(const char *buff, int sockfd, int length) 130 | { 131 | writeToSocket(buff, sockfd, length); 132 | } 133 | 134 | void writeToClientSocket(const char *buff, int sockfd, int length) 135 | { 136 | writeToSocket(buff, sockfd, length); 137 | } 138 | 139 | // void writeToClient(int clientfd, int serverfd, CacheStrategy &cache, const char *url) 140 | // { 141 | // char buffer[4096]; 142 | // ssize_t bytesRead; 143 | // std::string response; 144 | 145 | // while ((bytesRead = recv(serverfd, buffer, sizeof(buffer), 0)) > 0) 146 | // { 147 | // response.append(buffer, bytesRead); 148 | // writeToClientSocket(buffer, clientfd, bytesRead); 149 | // } 150 | 151 | // if (bytesRead < 0) 152 | // { 153 | // perror("Error receiving from server"); 154 | // } 155 | // else 156 | // { 157 | // cache.add(url, response.c_str(), response.size()); 158 | // } 159 | // } 160 | 161 | void writeToClient(int clientfd, int serverfd, CacheStrategy &cache, const char *url) 162 | { 163 | char buffer[4096]; 164 | ssize_t bytesRead; 165 | std::string response; 166 | while ((bytesRead = recv(serverfd, buffer, sizeof(buffer), 0)) > 0) 167 | { 168 | response.append(buffer, bytesRead); 169 | writeToClientSocket(buffer, clientfd, bytesRead); 170 | } 171 | if (bytesRead < 0) 172 | { 173 | perror("Error receiving from server"); 174 | } 175 | else 176 | { 177 | printf("Adding new element to cache:\n"); 178 | printf(" URL: %s\n", url); 179 | printf(" Response size: %zu bytes\n", response.size()); 180 | 181 | // Extract and print content type if available 182 | size_t contentTypePos = response.find("Content-Type:"); 183 | if (contentTypePos != std::string::npos) 184 | { 185 | size_t endOfLine = response.find("\r\n", contentTypePos); 186 | if (endOfLine != std::string::npos) 187 | { 188 | std::string contentType = response.substr(contentTypePos + 14, endOfLine - (contentTypePos + 14)); 189 | printf(" Content-Type: %s\n", contentType.c_str()); 190 | } 191 | } 192 | 193 | // Print current time 194 | time_t now = time(nullptr); 195 | printf(" Cached at: %s", ctime(&now)); 196 | 197 | cache.add(url, response.c_str(), response.size()); 198 | 199 | printf("Element successfully added to cache.\n\n"); 200 | } 201 | } -------------------------------------------------------------------------------- /ProxyUtils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "./CacheStrategy.hpp" 4 | #include "./proxy_parse.h" 5 | 6 | int createServerSocket(int port); 7 | char *convertRequestToString(struct ParsedRequest *req); 8 | int createServerSocket(const char *address, const char *port); 9 | void writeToServerSocket(const char *buff, int sockfd, int length); 10 | void writeToClientSocket(const char *buff, int sockfd, int length); 11 | void writeToClient(int clientfd, int serverfd, CacheStrategy &cache, const char *url); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multithreaded HTTP Proxy Server 2 | 3 | This project implements a multithreaded HTTP proxy server in C++. It features two server implementations (ThreadPool and Semaphore-based) and two caching strategies (LRU and LFU). 4 | 5 | ## Introduction 6 | 7 | This proxy server acts as an intermediary for requests from clients seeking resources from other servers. It incorporates several advanced concepts: 8 | 9 | - **Multithreading**: Allows the server to handle multiple client requests concurrently. 10 | - **ThreadPool**: A pool of worker threads that can be reused to perform tasks, reducing the overhead of thread creation. 11 | - **Semaphores**: Used for synchronization between threads, controlling access to shared resources. 12 | - **Caching**: Stores frequently or recently accessed data to improve response times and reduce network traffic. 13 | - **LRU (Least Recently Used)**: A caching algorithm that discards the least recently used items first. 14 | - **LFU (Least Frequently Used)**: A caching algorithm that discards the least frequently used items first. 15 | - **Factory Design Pattern**: Used to create different types of servers and caches based on runtime configuration. 16 | 17 | ## Features 18 | 19 | - Multithreaded server implementations: 20 | - ThreadPool-based server 21 | - Semaphore-based server 22 | - Caching strategies: 23 | - Least Recently Used (LRU) cache 24 | - Least Frequently Used (LFU) cache 25 | - Factory design pattern for server and cache creation 26 | - Configurable number of threads and cache size 27 | 28 | ## Project Structure 29 | 30 | The project consists of several C++ files implementing different components: 31 | 32 | - [`main.cpp`](main.cpp): Entry point of the application 33 | - [`ServerFactory.cpp/hpp`](ServerFactory.hpp): Factory for creating server and cache instances 34 | - [`HTTPServer.hpp`](HTTPServer.hpp): Base class for HTTP server implementations 35 | - [`ThreadPoolServer.cpp/hpp`](ThreadPoolServer.hpp): ThreadPool-based server implementation 36 | - [`SemaphoreServer.cpp/hpp`](SemaphoreServer.hpp): Semaphore-based server implementation 37 | - [`CacheStrategy.hpp`](CacheStrategy.hpp): Base class for cache strategies 38 | - [`LRUCache.cpp/hpp`](LRUCache.hpp): Least Recently Used cache implementation 39 | - [`LFUCache.cpp/hpp`](LFUCache.hpp): Least Frequently Used cache implementation 40 | - [`ThreadPool.cpp/hpp`](ThreadPool.hpp): ThreadPool implementation 41 | - [`ProxyUtils.cpp/hpp`](ProxyUtils.hpp)Various utility classes and headers 42 | 43 | ## Building the Project 44 | 45 | To build the project, ensure you have a Linux machine with C++ compiler and `make` installed. Then run: 46 | 47 | ``` 48 | make 49 | ``` 50 | 51 | ## Running the Server 52 | 53 | To run the server, use the following command format: 54 | 55 | ``` 56 | ./proxy_server [num_threads] [cache_size] 57 | ``` 58 | 59 | Where: 60 | 61 | - ``: Either "Threadpool" or "Semaphore" 62 | - ``: Either "LRUCache" or "LFUCache" 63 | - ``: The port number to run the server on 64 | - `[num_threads]`: (Optional) Number of threads for the server (default: 25) 65 | - `[cache_size]`: (Optional) Maximum number of elements in the cache (default: 100) 66 | 67 | Example: 68 | 69 | ``` 70 | ./proxy_server Threadpool LRUCache 8080 30 100 71 | ``` 72 | 73 | This command starts a ThreadPool-based server with LRU caching on port 8080, using 30 threads and a cache size of 10000 elements. 74 | 75 | ## Testing the Proxy Server 76 | 77 | Once the server is running, you can test it using Postman or any HTTP client. Here's how to use Postman: 78 | 79 | 1. Open Postman and create a new request. 80 | 2. Set the request type to GET (or the appropriate HTTP method). 81 | 3. Enter the URL you want to access, but replace the host and port with your proxy server's address. For example: 82 | 83 | ``` 84 | http://localhost:8080/http://example.com 85 | ``` 86 | 87 | This sends a request to `http://example.com` through your proxy server running on `localhost:8080`. 88 | 89 | 4. Send the request and observe the response. 90 | 91 | The proxy server should forward your request to the target server and return the response. You can verify the proxy's functionality by checking the response headers or timing multiple requests to see if caching is working. 92 | 93 | ## Implementation Details 94 | 95 | The server uses a factory pattern to create the appropriate server and cache instances based on command-line arguments. It supports two types of server implementations: 96 | 97 | 1. ThreadPool-based server: Uses a pool of worker threads to handle incoming connections. 98 | 2. Semaphore-based server: Uses semaphores to control concurrent access to server resources. 99 | 100 | Two caching strategies are implemented: 101 | 102 | 1. LRU (Least Recently Used): Discards the least recently used items first when the cache is full. 103 | 2. LFU (Least Frequently Used): Discards the least frequently used items first when the cache is full. 104 | 105 | ## Contributing 106 | 107 | Contributions to improve the server's functionality or performance are welcome. Please submit pull requests or open issues to discuss proposed changes. 108 | -------------------------------------------------------------------------------- /SemaphoreServer.cpp: -------------------------------------------------------------------------------- 1 | #include "SemaphoreServer.hpp" 2 | #include "ProxyUtils.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | extern "C" 13 | { 14 | #include "proxy_parse.h" 15 | } 16 | 17 | volatile sig_atomic_t running = 1; 18 | 19 | void sigint_handler(int signum) 20 | { 21 | running = 0; 22 | } 23 | 24 | SemaphoreServer::SemaphoreServer(std::unique_ptr cache, int numThreads) 25 | : HTTPServer(std::move(cache)), numThreads(numThreads) 26 | { 27 | sem_init(&thread_semaphore, 0, numThreads); 28 | } 29 | 30 | void *SemaphoreServer::staticWorkerThread(void *arg) 31 | { 32 | SemaphoreServer *server = static_cast(arg); 33 | return server->workerThread(); 34 | } 35 | void *SemaphoreServer::workerThread() 36 | { 37 | while (running) 38 | { 39 | sem_wait(&thread_semaphore); 40 | 41 | if (!running) 42 | { 43 | sem_post(&thread_semaphore); 44 | break; 45 | } 46 | 47 | int clientSocket = accept(serverSocket, NULL, NULL); 48 | if (clientSocket < 0) 49 | { 50 | // if (errno == EINTR) 51 | // { 52 | // sem_post(&thread_semaphore); 53 | // continue; 54 | // } 55 | // perror("Error accepting connection"); 56 | // sem_post(&thread_semaphore); 57 | // continue; 58 | if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) 59 | { 60 | sem_post(&thread_semaphore); 61 | continue; 62 | } 63 | perror("Error accepting connection"); 64 | sem_post(&thread_semaphore); 65 | continue; 66 | } 67 | 68 | printf("New client connection accepted\n\n"); 69 | this->datafromclient(clientSocket); 70 | close(clientSocket); 71 | sem_post(&thread_semaphore); 72 | } 73 | 74 | return NULL; 75 | } 76 | 77 | // void SemaphoreServer::start(int port) 78 | // { 79 | // int serverSocket = createServerSocket(port); 80 | // if (serverSocket < 0) 81 | // { 82 | // throw std::runtime_error("Failed to create server socket"); 83 | // } 84 | // signal(SIGINT, sigint_handler); 85 | 86 | // pthread_t worker_threads[MAX_THREADS]; 87 | // for (int i = 0; i < MAX_THREADS; i++) 88 | // { 89 | // pthread_create(&worker_threads[i], NULL, staticWorkerThread, this); 90 | // } 91 | 92 | // for (int i = 0; i < MAX_THREADS; i++) 93 | // { 94 | // pthread_join(worker_threads[i], NULL); 95 | // } 96 | 97 | // sem_destroy(&thread_semaphore); 98 | // close(serverSocket); 99 | // } 100 | 101 | void SemaphoreServer::start(int port) 102 | { 103 | serverSocket = createServerSocket(port); 104 | if (serverSocket < 0) 105 | { 106 | throw std::runtime_error("Failed to create server socket"); 107 | } 108 | 109 | signal(SIGINT, sigint_handler); 110 | 111 | pthread_t worker_threads[numThreads]; 112 | for (int i = 0; i < numThreads; i++) 113 | { 114 | pthread_create(&worker_threads[i], NULL, staticWorkerThread, this); 115 | } 116 | 117 | for (int i = 0; i < numThreads; i++) 118 | { 119 | pthread_join(worker_threads[i], NULL); 120 | } 121 | 122 | sem_destroy(&thread_semaphore); 123 | close(serverSocket); 124 | } 125 | 126 | void SemaphoreServer::datafromclient(int clientSocket) 127 | { 128 | int MAX_BUFFER_SIZE = 5000; 129 | char buf[MAX_BUFFER_SIZE]; 130 | int newsockfd = clientSocket; 131 | char *request_message; // Get message from URL 132 | request_message = (char *)malloc(MAX_BUFFER_SIZE); 133 | if (request_message == NULL) 134 | { 135 | fprintf(stderr, " Error in memory allocation ! \n"); 136 | exit(1); 137 | } 138 | 139 | request_message[0] = '\0'; 140 | int total_recieved_bits = 0; 141 | 142 | while (strstr(request_message, "\r\n\r\n") == NULL) 143 | { 144 | int recvd = recv(newsockfd, buf, MAX_BUFFER_SIZE, 0); 145 | 146 | if (recvd < 0) 147 | { 148 | fprintf(stderr, " Error while receiving ! \n"); 149 | exit(1); 150 | } 151 | else if (recvd == 0) 152 | { 153 | break; 154 | } 155 | else 156 | { 157 | total_recieved_bits += recvd; 158 | 159 | if (total_recieved_bits > MAX_BUFFER_SIZE) 160 | { 161 | MAX_BUFFER_SIZE *= 2; 162 | request_message = (char *)realloc(request_message, MAX_BUFFER_SIZE); 163 | if (request_message == NULL) 164 | { 165 | fprintf(stderr, " Error in memory re-allocation ! \n"); 166 | exit(1); 167 | } 168 | } 169 | } 170 | 171 | strncat(request_message, buf, recvd); 172 | } 173 | 174 | struct ParsedRequest *req; 175 | req = ParsedRequest_create(); 176 | 177 | if (ParsedRequest_parse(req, request_message, strlen(request_message)) < 0) 178 | { 179 | fprintf(stderr, "Error in request message. Request message: %s\n", request_message); 180 | exit(0); 181 | } 182 | 183 | printf("Received request for URL: %s%s\n", req->host, req->path); 184 | 185 | if (req->port == NULL) 186 | req->port = (char *)"80"; 187 | 188 | char *browser_req = convertRequestToString(req); 189 | std::string url(req->host); 190 | url += req->path; 191 | 192 | cache_element *cached_response = cache->find(url.c_str()); 193 | 194 | if (cached_response != nullptr) 195 | { 196 | printf("Cache hit: Response found in cache for URL: %s\n", url.c_str()); 197 | printf("Cache element details:\n"); 198 | printf(" URL: %s\n", cached_response->url); 199 | printf(" Length: %d bytes\n", cached_response->len); 200 | printf(" Last accessed: %s", ctime(&cached_response->lru_time_track)); 201 | writeToClientSocket(cached_response->data, newsockfd, cached_response->len); 202 | } 203 | else 204 | { 205 | printf("Cache miss: Response not found in cache for URL: %s\n", url.c_str()); 206 | int iServerfd = createServerSocket(req->host, req->port); 207 | printf("Sending request to remote server: %s\n", req->host); 208 | writeToServerSocket(browser_req, iServerfd, strlen(browser_req)); 209 | writeToClient(newsockfd, iServerfd, *(cache), url.c_str()); 210 | close(iServerfd); 211 | } 212 | 213 | ParsedRequest_destroy(req); 214 | free(request_message); 215 | close(newsockfd); 216 | } 217 | 218 | // void *SemaphoreServer::workerThread(void *arg) 219 | // { 220 | // int serverSocket = *((int *)arg); 221 | 222 | // while (running) 223 | // { 224 | // sem_wait(&thread_semaphore); 225 | 226 | // if (!running) 227 | // { 228 | // sem_post(&thread_semaphore); 229 | // break; 230 | // } 231 | 232 | // int clientSocket = accept(serverSocket, NULL, NULL); 233 | // if (clientSocket < 0) 234 | // { 235 | // if (errno == EINTR) 236 | // { 237 | // sem_post(&thread_semaphore); 238 | // continue; 239 | // } 240 | // perror("Error accepting connection"); 241 | // sem_post(&thread_semaphore); 242 | // continue; 243 | // } 244 | 245 | // printf("New client connection accepted\n"); 246 | // this->datafromclient(clientSocket); 247 | // close(clientSocket); 248 | // sem_post(&thread_semaphore); 249 | // } 250 | 251 | // return NULL; 252 | // } -------------------------------------------------------------------------------- /SemaphoreServer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "HTTPServer.hpp" 3 | #include 4 | 5 | class SemaphoreServer : public HTTPServer 6 | { 7 | public: 8 | SemaphoreServer(std::unique_ptr cache, int numThreads = 25); 9 | void start(int port) override; 10 | 11 | private: 12 | int serverSocket; 13 | int numThreads; 14 | sem_t thread_semaphore; 15 | static void *staticWorkerThread(void *arg); 16 | void *workerThread(); 17 | void datafromclient(int clientSocket); 18 | }; -------------------------------------------------------------------------------- /ServerFactory.cpp: -------------------------------------------------------------------------------- 1 | #include "ServerFactory.hpp" 2 | #include "ThreadPoolServer.hpp" 3 | #include "SemaphoreServer.hpp" 4 | #include "./LRUCache.hpp" 5 | #include "./LFUCache.hpp" 6 | 7 | std::unique_ptr ServerFactory::createHTTPServer(const std::string &serverType, const std::string &cacheType, int numThreads, int cacheSize) 8 | { 9 | auto cache = createCacheStrategy(cacheType, cacheSize); 10 | if (serverType == "Threadpool") 11 | { 12 | return std::make_unique(std::move(cache)); 13 | } 14 | else if (serverType == "Semaphore") 15 | { 16 | return std::make_unique(std::move(cache)); 17 | } 18 | throw std::invalid_argument("Invalid server type"); 19 | } 20 | 21 | std::unique_ptr ServerFactory::createCacheStrategy(const std::string &cacheType, int cacheSize) 22 | { 23 | if (cacheType == "LRUCache") 24 | { 25 | return std::make_unique(cacheSize); 26 | } 27 | else if (cacheType == "LFUCache") 28 | { 29 | return std::make_unique(cacheSize); 30 | } 31 | throw std::invalid_argument("Invalid cache type"); 32 | } -------------------------------------------------------------------------------- /ServerFactory.hpp: -------------------------------------------------------------------------------- 1 | // ServerFactory.hpp 2 | #pragma once 3 | #include "./HTTPServer.hpp" 4 | #include "./CacheStrategy.hpp" 5 | 6 | class ServerFactory 7 | { 8 | public: 9 | static std::unique_ptr createHTTPServer(const std::string &serverType, const std::string &cacheType, int numThreads = 25, int cacheSize = 100); 10 | 11 | private: 12 | static std::unique_ptr createCacheStrategy(const std::string &cacheType, int cacheSize); 13 | }; -------------------------------------------------------------------------------- /ThreadPool.cpp: -------------------------------------------------------------------------------- 1 | #include "ThreadPool.hpp" 2 | 3 | ThreadPool::ThreadPool(size_t numThreads) : stop(false), readyThreads(0) 4 | { 5 | for (size_t i = 0; i < numThreads; ++i) 6 | { 7 | workers.emplace_back([this] 8 | { 9 | { 10 | std::unique_lock lock(this->queueMutex); 11 | readyThreads++; 12 | allThreadsReady.notify_one(); 13 | } 14 | for(;;) { 15 | std::function task; 16 | { 17 | std::unique_lock lock(this->queueMutex); 18 | this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); }); 19 | if(this->stop && this->tasks.empty()) return; 20 | task = std::move(this->tasks.front()); 21 | this->tasks.pop(); 22 | } 23 | task(); 24 | } }); 25 | } 26 | } 27 | 28 | ThreadPool::~ThreadPool() 29 | { 30 | stop = true; 31 | condition.notify_all(); 32 | for (std::thread &worker : workers) 33 | worker.join(); 34 | } 35 | 36 | void ThreadPool::enqueue(std::function task) 37 | { 38 | { 39 | std::unique_lock lock(queueMutex); 40 | tasks.emplace(task); 41 | } 42 | condition.notify_one(); 43 | } 44 | 45 | void ThreadPool::waitForThreads() 46 | { 47 | std::unique_lock lock(queueMutex); 48 | allThreadsReady.wait(lock, [this] 49 | { return readyThreads == workers.size(); }); 50 | } -------------------------------------------------------------------------------- /ThreadPool.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class ThreadPool 11 | { 12 | public: 13 | ThreadPool(size_t numThreads); 14 | ~ThreadPool(); 15 | 16 | void enqueue(std::function task); 17 | void waitForThreads(); 18 | 19 | private: 20 | std::vector workers; 21 | std::queue> tasks; 22 | 23 | std::mutex queueMutex; 24 | std::condition_variable condition; 25 | std::atomic stop; 26 | std::atomic readyThreads; 27 | std::condition_variable allThreadsReady; 28 | }; 29 | -------------------------------------------------------------------------------- /ThreadPoolServer.cpp: -------------------------------------------------------------------------------- 1 | #include "ThreadPoolServer.hpp" 2 | #include "ProxyUtils.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | extern "C" 8 | { 9 | #include "proxy_parse.h" 10 | } 11 | #include 12 | // HERE after every response is sent to client we close the connection . objective here to make sure other clients get turn too 13 | ThreadPoolServer::ThreadPoolServer(std::unique_ptr cache, int numThreads) 14 | : HTTPServer(std::move(cache)), pool(numThreads) {} 15 | 16 | void ThreadPoolServer::start(int port) 17 | { 18 | // std::cout << "inside start\n"; 19 | struct sockaddr cli_addr; 20 | int serverSocket = createServerSocket(port); 21 | pool.waitForThreads(); 22 | int clilen = sizeof(struct sockaddr); 23 | while (true) 24 | { 25 | int clientSocket = accept(serverSocket, &cli_addr, (socklen_t *)&clilen); 26 | if (clientSocket < 0) 27 | { 28 | fprintf(stderr, "ERROR! On Accepting Request ! i.e requests limit crossed \n"); 29 | continue; 30 | } 31 | printf("New client connection accepted\n\n"); 32 | pool.enqueue([this, clientSocket] 33 | { handleClient(clientSocket); }); 34 | } 35 | } 36 | 37 | void ThreadPoolServer::handleClient(int clientSocket) 38 | { 39 | // Implement client handling logic here 40 | // Use the cache member to interact with the cache 41 | // Use ProxyUtils functions for common operations 42 | // std::cout << "inside handle client\n"; 43 | int MAX_BUFFER_SIZE = 5000; 44 | char buf[MAX_BUFFER_SIZE]; 45 | int newsockfd = clientSocket; 46 | char *request_message; // Get message from URL 47 | request_message = (char *)malloc(MAX_BUFFER_SIZE); 48 | if (request_message == NULL) 49 | { 50 | fprintf(stderr, " Error in memory allocation ! \n"); 51 | exit(1); 52 | } 53 | // std::cout << "inside handle client\n"; 54 | request_message[0] = '\0'; 55 | int total_recieved_bits = 0; 56 | 57 | while (strstr(request_message, "\r\n\r\n") == NULL) 58 | { 59 | int recvd = recv(newsockfd, buf, MAX_BUFFER_SIZE, 0); 60 | 61 | if (recvd < 0) 62 | { 63 | fprintf(stderr, " Error while receiving ! \n"); 64 | exit(1); 65 | } 66 | else if (recvd == 0) 67 | { 68 | break; 69 | } 70 | else 71 | { 72 | total_recieved_bits += recvd; 73 | 74 | if (total_recieved_bits > MAX_BUFFER_SIZE) 75 | { 76 | MAX_BUFFER_SIZE *= 2; 77 | request_message = (char *)realloc(request_message, MAX_BUFFER_SIZE); 78 | if (request_message == NULL) 79 | { 80 | fprintf(stderr, " Error in memory re-allocation ! \n"); 81 | exit(1); 82 | } 83 | } 84 | } 85 | 86 | strncat(request_message, buf, recvd); 87 | } 88 | 89 | struct ParsedRequest *req; 90 | req = ParsedRequest_create(); 91 | 92 | if (ParsedRequest_parse(req, request_message, strlen(request_message)) < 0) 93 | { 94 | fprintf(stderr, "Error in request message. Request message: %s\n", request_message); 95 | exit(0); 96 | } 97 | 98 | printf("Received request for URL: %s%s\n", req->host, req->path); 99 | 100 | if (req->port == NULL) 101 | req->port = (char *)"80"; 102 | 103 | char *browser_req = convertRequestToString(req); 104 | std::string url(req->host); 105 | url += req->path; 106 | 107 | cache_element *cached_response = cache->find(url.c_str()); 108 | 109 | if (cached_response != nullptr) 110 | { 111 | printf("Cache hit: Response found in cache for URL: %s\n", url.c_str()); 112 | printf("Cache element details:\n"); 113 | printf(" URL: %s\n", cached_response->url); 114 | printf(" Length: %d bytes\n", cached_response->len); 115 | printf(" Last accessed: %s", ctime(&cached_response->lru_time_track)); 116 | 117 | writeToClientSocket(cached_response->data, newsockfd, cached_response->len); 118 | } 119 | else 120 | { 121 | printf("Cache miss: Response not found in cache for URL: %s\n", url.c_str()); 122 | int iServerfd = createServerSocket(req->host, req->port); 123 | printf("Sending request to remote server: %s\n", req->host); 124 | writeToServerSocket(browser_req, iServerfd, strlen(browser_req)); 125 | writeToClient(newsockfd, iServerfd, *(this->cache), url.c_str()); 126 | close(iServerfd); 127 | } 128 | 129 | ParsedRequest_destroy(req); 130 | free(request_message); 131 | close(newsockfd); 132 | } -------------------------------------------------------------------------------- /ThreadPoolServer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "HTTPServer.hpp" 3 | #include "ThreadPool.hpp" 4 | 5 | class ThreadPoolServer : public HTTPServer 6 | { 7 | public: 8 | ThreadPoolServer(std::unique_ptr cache, int numThreads = 25); 9 | void start(int port) override; 10 | 11 | private: 12 | ThreadPool pool; 13 | void handleClient(int clientSocket); 14 | }; -------------------------------------------------------------------------------- /benchmark.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import time 3 | import statistics 4 | import matplotlib.pyplot as plt 5 | import requests 6 | import random 7 | from concurrent.futures import ThreadPoolExecutor, as_completed 8 | 9 | # List of common websites 10 | WEBSITES = [ 11 | "http://www.google.com", 12 | "http://www.youtube.com", 13 | "http://www.facebook.com", 14 | "http://www.amazon.com", 15 | "http://www.wikipedia.org", 16 | "http://www.twitter.com", 17 | "http://www.instagram.com", 18 | "http://www.linkedin.com", 19 | "http://www.reddit.com", 20 | "http://www.github.com" 21 | ] 22 | 23 | def start_server(server_path, server_type, cache_type, port): 24 | try: 25 | process = subprocess.Popen([server_path, server_type, cache_type, str(port)], 26 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) 27 | time.sleep(2) # Give the server time to start 28 | return_code = process.poll() 29 | if return_code is not None: 30 | stdout, stderr = process.communicate() 31 | print(f"Server failed to start. Return code: {return_code}") 32 | print(f"Stdout: {stdout.decode()}") 33 | print(f"Stderr: {stderr.decode()}") 34 | return None 35 | return process 36 | except Exception as e: 37 | print(f"Error starting server: {e}") 38 | return None 39 | 40 | def stop_server(process): 41 | if process: 42 | try: 43 | process.terminate() 44 | process.wait(timeout=5) 45 | except subprocess.TimeoutExpired: 46 | process.kill() 47 | finally: 48 | process.wait() 49 | time.sleep(2) 50 | 51 | def make_request(proxy_url, max_retries=3): 52 | for attempt in range(max_retries): 53 | try: 54 | time.sleep(0.1 * (2 ** attempt)) # Exponential backoff 55 | start_time = time.time() 56 | target_url = random.choice(WEBSITES) 57 | url = f"{proxy_url}{target_url}" 58 | response = requests.get(url, timeout=30) 59 | return time.time() - start_time, target_url 60 | except Exception as e: 61 | if attempt == max_retries - 1: 62 | print(f"Request failed after {max_retries} attempts: {e}") 63 | return None, None 64 | 65 | def benchmark(server_name, proxy_url, num_requests, concurrency): 66 | print(f"Benchmarking {server_name}...") 67 | results = [] 68 | successful_requests = 0 69 | 70 | print("Warming up server...") 71 | for _ in range(10): 72 | make_request(proxy_url) 73 | time.sleep(2) 74 | 75 | print("Starting benchmark with gradual ramp-up...") 76 | with ThreadPoolExecutor(max_workers=concurrency) as executor: 77 | for i in range(1, concurrency + 1): 78 | futures = [executor.submit(make_request, proxy_url) for _ in range(num_requests // concurrency)] 79 | for future in as_completed(futures): 80 | result = future.result() 81 | if result[0] is not None: 82 | results.append(result) 83 | successful_requests += 1 84 | print(f"Completed batch {i}/{concurrency}") 85 | time.sleep(1) 86 | 87 | if not results: 88 | print(f"Error: No successful requests for {server_name}") 89 | return None, 0 90 | 91 | latencies = [r[0] for r in results] 92 | websites = [r[1] for r in results] 93 | 94 | return { 95 | 'avg_latency': statistics.mean(latencies), 96 | 'max_latency': max(latencies), 97 | 'min_latency': min(latencies), 98 | 'requests_per_second': len(latencies) / sum(latencies), 99 | 'website_distribution': {site: websites.count(site) for site in set(websites)} 100 | }, successful_requests 101 | 102 | def run_benchmarks(server_path, port): 103 | num_requests = 380 104 | concurrency = 10 105 | proxy_url = f"http://localhost:{port}/" 106 | cache_type = "LRUCache" 107 | results = {} 108 | successful_requests = {} 109 | 110 | for server_type in ["Threadpool", "semaphore"]: 111 | process = start_server(server_path, server_type, cache_type, port) 112 | if process: 113 | results[server_type], successful_requests[server_type] = benchmark(server_type, proxy_url, num_requests, concurrency) 114 | stop_server(process) 115 | else: 116 | print(f"Skipping benchmark for {server_type} due to server start failure") 117 | results[server_type] = None 118 | successful_requests[server_type] = 0 119 | 120 | return results, successful_requests 121 | 122 | def plot_results(results): 123 | if all(result is None for result in results.values()): 124 | print("No data to plot. All benchmarks failed.") 125 | return 126 | 127 | metrics = ['avg_latency', 'max_latency', 'min_latency', 'requests_per_second'] 128 | 129 | fig, axs = plt.subplots(2, 2, figsize=(12, 10)) 130 | fig.suptitle('Proxy Server Performance Comparison') 131 | 132 | for i, metric in enumerate(metrics): 133 | ax = axs[i // 2, i % 2] 134 | values = [] 135 | labels = [] 136 | for server, data in results.items(): 137 | if data and metric in data: 138 | values.append(data[metric]) 139 | labels.append(server) 140 | if values: 141 | ax.bar(labels, values) 142 | ax.set_title(metric.replace('_', ' ').title()) 143 | ax.set_ylabel('Seconds' if 'latency' in metric else 'Requests/second') 144 | 145 | plt.tight_layout() 146 | plt.savefig('benchmark_results.png') 147 | print("Results plot saved as benchmark_results.png") 148 | 149 | # Plot website distribution 150 | fig, axs = plt.subplots(1, 2, figsize=(15, 5)) 151 | fig.suptitle('Website Distribution') 152 | 153 | for i, (server, data) in enumerate(results.items()): 154 | if data and 'website_distribution' in data: 155 | websites = list(data['website_distribution'].keys()) 156 | counts = list(data['website_distribution'].values()) 157 | axs[i].pie(counts, labels=websites, autopct='%1.1f%%') 158 | axs[i].set_title(f'{server} Server') 159 | 160 | plt.tight_layout() 161 | plt.savefig('website_distribution.png') 162 | print("Website distribution plot saved as website_distribution.png") 163 | 164 | if __name__ == "__main__": 165 | server_path = "./proxy_server" 166 | port = 10000 167 | 168 | results, successful_requests = run_benchmarks(server_path, port) 169 | 170 | print("\nResults:") 171 | for server, metrics in results.items(): 172 | print(f"\n{server} Server:") 173 | if metrics: 174 | for metric, value in metrics.items(): 175 | if metric != 'website_distribution': 176 | print(f" {metric}: {value:.4f}") 177 | else: 178 | print(" Website Distribution:") 179 | for site, count in value.items(): 180 | print(f" {site}: {count}") 181 | else: 182 | print(" No results available") 183 | 184 | plot_results(results) 185 | 186 | print("\nTotal successful responses:") 187 | for server, count in successful_requests.items(): 188 | print(f"{server} Server: {count}") 189 | 190 | 191 | # import subprocess 192 | # import time 193 | # import statistics 194 | # import matplotlib.pyplot as plt 195 | # import requests 196 | # import random 197 | # from concurrent.futures import ThreadPoolExecutor, as_completed 198 | 199 | # # List of common websites 200 | # WEBSITES = [ 201 | # "http://www.google.com", 202 | # "http://www.youtube.com", 203 | # "http://www.facebook.com", 204 | # "http://www.amazon.com", 205 | # "http://www.wikipedia.org", 206 | # "http://www.twitter.com", 207 | # "http://www.instagram.com", 208 | # "http://www.linkedin.com", 209 | # "http://www.reddit.com", 210 | # "http://www.github.com" 211 | # ] 212 | 213 | # def start_server(server_path, port): 214 | # try: 215 | # process = subprocess.Popen([server_path, str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 216 | # time.sleep(2) # Give the server time to start 217 | # return_code = process.poll() 218 | # if return_code is not None: 219 | # stdout, stderr = process.communicate() 220 | # print(f"Server failed to start. Return code: {return_code}") 221 | # print(f"Stdout: {stdout.decode()}") 222 | # print(f"Stderr: {stderr.decode()}") 223 | # return None 224 | # return process 225 | # except Exception as e: 226 | # print(f"Error starting server: {e}") 227 | # return None 228 | 229 | # def stop_server(process): 230 | # if process: 231 | # try: 232 | # process.terminate() 233 | # process.wait(timeout=5) # Wait for up to 5 seconds 234 | # except subprocess.TimeoutExpired: 235 | # process.kill() # Force kill if it doesn't terminate 236 | # finally: 237 | # process.wait() # Ensure the process is fully terminated 238 | 239 | # # Wait a bit to ensure the port is freed 240 | # time.sleep(2) 241 | 242 | # def make_request(proxy_url, max_retries=3): 243 | # for attempt in range(max_retries): 244 | # try: 245 | # time.sleep(0.1 * (2 ** attempt)) # Exponential backoff 246 | # start_time = time.time() 247 | # target_url = random.choice(WEBSITES) 248 | # url = f"{proxy_url}/{target_url}" 249 | # response = requests.get(url, timeout=30) 250 | # return time.time() - start_time, target_url 251 | # except Exception as e: 252 | # if attempt == max_retries - 1: 253 | # print(f"Request failed after {max_retries} attempts: {e}") 254 | # return None, None 255 | 256 | # def benchmark(server_name, proxy_url, num_requests, concurrency): 257 | # print(f"Benchmarking {server_name}...") 258 | # results = [] 259 | # successful_requests = 0 260 | 261 | # # Warm-up period 262 | # print("Warming up server...") 263 | # for _ in range(10): 264 | # make_request(proxy_url) 265 | # time.sleep(2) 266 | 267 | # # Gradual ramp-up 268 | # print("Starting benchmark with gradual ramp-up...") 269 | # with ThreadPoolExecutor(max_workers=concurrency) as executor: 270 | # for i in range(1, concurrency + 1): 271 | # futures = [executor.submit(make_request, proxy_url) for _ in range(num_requests // concurrency)] 272 | # for future in as_completed(futures): 273 | # result = future.result() 274 | # if result[0] is not None: 275 | # results.append(result) 276 | # successful_requests += 1 277 | # print(f"Completed batch {i}/{concurrency}") 278 | # time.sleep(1) # Short pause between batches 279 | 280 | # if not results: 281 | # print(f"Error: No successful requests for {server_name}") 282 | # return None, 0 283 | 284 | # latencies = [r[0] for r in results] 285 | # websites = [r[1] for r in results] 286 | 287 | # return { 288 | # 'avg_latency': statistics.mean(latencies), 289 | # 'max_latency': max(latencies), 290 | # 'min_latency': min(latencies), 291 | # 'requests_per_second': len(latencies) / sum(latencies), 292 | # 'website_distribution': {site: websites.count(site) for site in set(websites)} 293 | # }, successful_requests 294 | 295 | # def run_benchmarks(threadpool_server, semaphore_server, threadpool_port, semaphore_port): 296 | # num_requests = 380 # Increased for better distribution 297 | # concurrency = 50 298 | # proxy_url = "http://localhost:{}" 299 | 300 | # results = {} 301 | # successful_requests = {} 302 | 303 | # for server_name, server_path, port in [ 304 | # ("ThreadPool", threadpool_server, threadpool_port), 305 | # ("Semaphore", semaphore_server, semaphore_port) 306 | # ]: 307 | # process = start_server(server_path, port) 308 | # if process: 309 | # results[server_name], successful_requests[server_name] = benchmark(server_name, proxy_url.format(port), num_requests, concurrency) 310 | # stop_server(process) 311 | # else: 312 | # print(f"Skipping benchmark for {server_name} due to server start failure") 313 | # results[server_name] = None 314 | # successful_requests[server_name] = 0 315 | 316 | # return results, successful_requests 317 | 318 | # def plot_results(results): 319 | # if all(result is None for result in results.values()): 320 | # print("No data to plot. All benchmarks failed.") 321 | # return 322 | 323 | # metrics = ['avg_latency', 'max_latency', 'min_latency', 'requests_per_second'] 324 | 325 | # fig, axs = plt.subplots(2, 2, figsize=(12, 10)) 326 | # fig.suptitle('Proxy Server Performance Comparison') 327 | 328 | # for i, metric in enumerate(metrics): 329 | # ax = axs[i // 2, i % 2] 330 | # values = [] 331 | # labels = [] 332 | # for server, data in results.items(): 333 | # if data and metric in data: 334 | # values.append(data[metric]) 335 | # labels.append(server) 336 | # if values: 337 | # ax.bar(labels, values) 338 | # ax.set_title(metric.replace('_', ' ').title()) 339 | # ax.set_ylabel('Seconds' if 'latency' in metric else 'Requests/second') 340 | 341 | # plt.tight_layout() 342 | # plt.savefig('benchmark_results.png') 343 | # print("Results plot saved as benchmark_results.png") 344 | 345 | # # Plot website distribution 346 | # fig, axs = plt.subplots(1, 2, figsize=(15, 5)) 347 | # fig.suptitle('Website Distribution') 348 | 349 | # for i, (server, data) in enumerate(results.items()): 350 | # if data and 'website_distribution' in data: 351 | # websites = list(data['website_distribution'].keys()) 352 | # counts = list(data['website_distribution'].values()) 353 | # axs[i].pie(counts, labels=websites, autopct='%1.1f%%') 354 | # axs[i].set_title(f'{server} Server') 355 | 356 | # plt.tight_layout() 357 | # plt.savefig('website_distribution.png') 358 | # print("Website distribution plot saved as website_distribution.png") 359 | 360 | # if __name__ == "__main__": 361 | # threadpool_server = "./proxy2" 362 | # semaphore_server = "./proxy3" 363 | # threadpool_port = 20006 364 | # semaphore_port = 20005 365 | 366 | # results, successful_requests = run_benchmarks(threadpool_server, semaphore_server, threadpool_port, semaphore_port) 367 | 368 | # print("\nResults:") 369 | # for server, metrics in results.items(): 370 | # print(f"\n{server} Server:") 371 | # if metrics: 372 | # for metric, value in metrics.items(): 373 | # if metric != 'website_distribution': 374 | # print(f" {metric}: {value:.4f}") 375 | # else: 376 | # print(" Website Distribution:") 377 | # for site, count in value.items(): 378 | # print(f" {site}: {count}") 379 | # else: 380 | # print(" No results available") 381 | 382 | # plot_results(results) 383 | 384 | # print("\nTotal successful responses:") 385 | # for server, count in successful_requests.items(): 386 | # print(f"{server} Server: {count}") 387 | 388 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | // main.cpp 2 | #include 3 | #include 4 | #include 5 | #include "./ServerFactory.hpp" 6 | #include "./HTTPServer.hpp" 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | if (argc < 4 || argc > 6) 11 | { 12 | std::cerr << "Usage: " << argv[0] << " [num_threads] [cache_size]" << std::endl; 13 | std::cerr << "Server types: Threadpool, Semaphore" << std::endl; 14 | std::cerr << "Cache types: LRUCache, LFUCache" << std::endl; 15 | std::cerr << "Port: Any valid port number (e.g., 8080)" << std::endl; 16 | std::cerr << "Num threads: (Optional) Number of threads for the server" << std::endl; 17 | std::cerr << "Cache size: (Optional) Maximum number of elements in the cache" << std::endl; 18 | return 1; 19 | } 20 | 21 | std::string serverType = argv[1]; 22 | std::string cacheType = argv[2]; 23 | int port, numThreads = 25, cacheSize = 100; // Default values 24 | 25 | try 26 | { 27 | port = std::stoi(argv[3]); 28 | if (argc > 4) 29 | numThreads = std::stoi(argv[4]); 30 | if (argc > 5) 31 | cacheSize = std::stoi(argv[5]); 32 | } 33 | catch (const std::invalid_argument &e) 34 | { 35 | std::cerr << "Error: Invalid argument. Please provide valid numbers for port, num_threads, and cache_size." << std::endl; 36 | return 1; 37 | } 38 | catch (const std::out_of_range &e) 39 | { 40 | std::cerr << "Error: Number out of range for port, num_threads, or cache_size." << std::endl; 41 | return 1; 42 | } 43 | 44 | try 45 | { 46 | std::unique_ptr server = ServerFactory::createHTTPServer(serverType, cacheType, numThreads, cacheSize); 47 | std::cout << "Starting HTTP proxy server on port " << port << std::endl; 48 | std::cout << "Number of threads: " << numThreads << std::endl; 49 | std::cout << "Cache size: " << cacheSize << std::endl; 50 | server->start(port); 51 | } 52 | catch (const std::runtime_error &e) 53 | { 54 | std::cerr << "Runtime error: " << e.what() << std::endl; 55 | return 1; 56 | } 57 | catch (const std::exception &e) 58 | { 59 | std::cerr << "Error: " << e.what() << std::endl; 60 | return 1; 61 | } 62 | 63 | return 0; 64 | } -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # Makefile for HTTP Proxy Server 2 | 3 | # Compiler 4 | CXX = g++ 5 | 6 | # Compiler flags 7 | CXXFLAGS = -std=c++17 -Wall -Wextra -pedantic 8 | 9 | # Linker flags 10 | LDFLAGS = -lpthread 11 | 12 | # Source files 13 | SOURCES = main.cpp \ 14 | ServerFactory.cpp \ 15 | ThreadPoolServer.cpp \ 16 | SemaphoreServer.cpp \ 17 | LRUCache.cpp \ 18 | LFUCache.cpp \ 19 | ThreadPool.cpp \ 20 | ProxyUtils.cpp \ 21 | proxy_parse.c 22 | 23 | # Object files 24 | OBJECTS = $(SOURCES:.cpp=.o) proxy_parse.o 25 | 26 | # Executable name 27 | EXECUTABLE = proxy_server 28 | 29 | # Default target 30 | all: $(EXECUTABLE) 31 | 32 | # Rule to create the executable 33 | $(EXECUTABLE): $(OBJECTS) 34 | $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) 35 | 36 | # Rule to compile source files to object files 37 | %.o: %.cpp 38 | $(CXX) $(CXXFLAGS) -c $< -o $@ 39 | 40 | # Clean target 41 | clean: 42 | rm -f $(OBJECTS) $(EXECUTABLE) 43 | 44 | # Phony targets 45 | .PHONY: all clean 46 | 47 | # Dependencies 48 | main.o: main.cpp ServerFactory.hpp HTTPServer.hpp 49 | LRUCache.o: LRUCache.cpp LRUCache.hpp CacheStrategy.hpp 50 | LFUCache.o: LFUCache.cpp LFUCache.hpp CacheStrategy.hpp 51 | ServerFactory.o: ServerFactory.cpp ServerFactory.hpp ThreadPoolServer.hpp SemaphoreServer.hpp LRUCache.hpp LFUCache.hpp 52 | ThreadPoolServer.o: ThreadPoolServer.cpp ThreadPoolServer.hpp HTTPServer.hpp ThreadPool.hpp ProxyUtils.hpp proxy_parse.h 53 | SemaphoreServer.o: SemaphoreServer.cpp SemaphoreServer.hpp HTTPServer.hpp ProxyUtils.hpp proxy_parse.h 54 | ThreadPool.o: ThreadPool.cpp ThreadPool.hpp 55 | ProxyUtils.o: ProxyUtils.cpp ProxyUtils.hpp proxy_parse.h 56 | proxy_parse.o: proxy_parse.c proxy_parse.h -------------------------------------------------------------------------------- /proxy_parse.c: -------------------------------------------------------------------------------- 1 | /* 2 | proxy_parse.c -- a HTTP Request Parsing Library. 3 | COS 461 4 | */ 5 | 6 | #include "proxy_parse.h" 7 | 8 | #define DEFAULT_NHDRS 8 9 | #define MAX_REQ_LEN 65535 10 | #define MIN_REQ_LEN 4 11 | 12 | static const char *root_abs_path = "/"; 13 | 14 | /* private function declartions */ 15 | int ParsedRequest_printRequestLine(struct ParsedRequest *pr, 16 | char * buf, size_t buflen, 17 | size_t *tmp); 18 | size_t ParsedRequest_requestLineLen(struct ParsedRequest *pr); 19 | 20 | /* 21 | * debug() prints out debugging info if DEBUG is set to 1 22 | * 23 | * parameter format: same as printf 24 | * 25 | */ 26 | void debug(const char * format, ...) { 27 | va_list args; 28 | if (DEBUG) { 29 | va_start(args, format); 30 | vfprintf(stderr, format, args); 31 | va_end(args); 32 | } 33 | } 34 | 35 | 36 | /* 37 | * ParsedHeader Public Methods 38 | */ 39 | 40 | /* Set a header with key and value */ 41 | int ParsedHeader_set(struct ParsedRequest *pr, 42 | const char * key, const char * value) 43 | { 44 | struct ParsedHeader *ph; 45 | ParsedHeader_remove (pr, key); 46 | 47 | if (pr->headerslen <= pr->headersused+1) { 48 | pr->headerslen = pr->headerslen * 2; 49 | pr->headers = 50 | (struct ParsedHeader *)realloc(pr->headers, 51 | pr->headerslen * sizeof(struct ParsedHeader)); 52 | if (!pr->headers) 53 | return -1; 54 | } 55 | 56 | ph = pr->headers + pr->headersused; 57 | pr->headersused += 1; 58 | 59 | ph->key = (char *)malloc(strlen(key)+1); 60 | memcpy(ph->key, key, strlen(key)); 61 | ph->key[strlen(key)] = '\0'; 62 | 63 | ph->value = (char *)malloc(strlen(value)+1); 64 | memcpy(ph->value, value, strlen(value)); 65 | ph->value[strlen(value)] = '\0'; 66 | 67 | ph->keylen = strlen(key)+1; 68 | ph->valuelen = strlen(value)+1; 69 | return 0; 70 | } 71 | 72 | 73 | /* get the parsedHeader with the specified key or NULL */ 74 | struct ParsedHeader* ParsedHeader_get(struct ParsedRequest *pr, 75 | const char * key) 76 | { 77 | size_t i = 0; 78 | struct ParsedHeader * tmp; 79 | while(pr->headersused > i) 80 | { 81 | tmp = pr->headers + i; 82 | if(tmp->key && key && strcmp(tmp->key, key) == 0) 83 | { 84 | return tmp; 85 | } 86 | i++; 87 | } 88 | return NULL; 89 | } 90 | 91 | /* remove the specified key from parsedHeader */ 92 | int ParsedHeader_remove(struct ParsedRequest *pr, const char *key) 93 | { 94 | struct ParsedHeader *tmp; 95 | tmp = ParsedHeader_get(pr, key); 96 | if(tmp == NULL) 97 | return -1; 98 | 99 | free(tmp->key); 100 | free(tmp->value); 101 | tmp->key = NULL; 102 | return 0; 103 | } 104 | 105 | 106 | /* modify the header with given key, giving it a new value 107 | * return 1 on success and 0 if no such header found 108 | * 109 | int ParsedHeader_modify(struct ParsedRequest *pr, const char * key, 110 | const char *newValue) 111 | { 112 | struct ParsedHeader *tmp; 113 | tmp = ParsedHeader_get(pr, key); 114 | if(tmp != NULL) 115 | { 116 | if(tmp->valuelen < strlen(newValue)+1) 117 | { 118 | tmp->valuelen = strlen(newValue)+1; 119 | tmp->value = (char *) realloc(tmp->value, 120 | tmp->valuelen * sizeof(char)); 121 | } 122 | strcpy(tmp->value, newValue); 123 | return 1; 124 | } 125 | return 0; 126 | } 127 | */ 128 | 129 | /* 130 | ParsedHeader Private Methods 131 | */ 132 | 133 | void ParsedHeader_create(struct ParsedRequest *pr) 134 | { 135 | pr->headers = 136 | (struct ParsedHeader *)malloc(sizeof(struct ParsedHeader)*DEFAULT_NHDRS); 137 | pr->headerslen = DEFAULT_NHDRS; 138 | pr->headersused = 0; 139 | } 140 | 141 | 142 | size_t ParsedHeader_lineLen(struct ParsedHeader * ph) 143 | { 144 | if(ph->key != NULL) 145 | { 146 | return strlen(ph->key)+strlen(ph->value)+4; 147 | } 148 | return 0; 149 | } 150 | 151 | size_t ParsedHeader_headersLen(struct ParsedRequest *pr) 152 | { 153 | if (!pr || !pr->buf) 154 | return 0; 155 | 156 | size_t i = 0; 157 | int len = 0; 158 | while(pr->headersused > i) 159 | { 160 | len += ParsedHeader_lineLen(pr->headers + i); 161 | i++; 162 | } 163 | len += 2; 164 | return len; 165 | } 166 | 167 | int ParsedHeader_printHeaders(struct ParsedRequest * pr, char * buf, 168 | size_t len) 169 | { 170 | char * current = buf; 171 | struct ParsedHeader * ph; 172 | size_t i = 0; 173 | 174 | if(len < ParsedHeader_headersLen(pr)) 175 | { 176 | debug("buffer for printing headers too small\n"); 177 | return -1; 178 | } 179 | 180 | while(pr->headersused > i) 181 | { 182 | ph = pr->headers+i; 183 | if (ph->key) { 184 | memcpy(current, ph->key, strlen(ph->key)); 185 | memcpy(current+strlen(ph->key), ": ", 2); 186 | memcpy(current+strlen(ph->key) +2 , ph->value, 187 | strlen(ph->value)); 188 | memcpy(current+strlen(ph->key) +2+strlen(ph->value) , 189 | "\r\n", 2); 190 | current += strlen(ph->key)+strlen(ph->value)+4; 191 | } 192 | i++; 193 | } 194 | memcpy(current, "\r\n",2); 195 | return 0; 196 | } 197 | 198 | 199 | void ParsedHeader_destroyOne(struct ParsedHeader * ph) 200 | { 201 | if(ph->key != NULL) 202 | { 203 | free(ph->key); 204 | ph->key = NULL; 205 | free(ph->value); 206 | ph->value = NULL; 207 | ph->keylen = 0; 208 | ph->valuelen = 0; 209 | } 210 | } 211 | 212 | void ParsedHeader_destroy(struct ParsedRequest * pr) 213 | { 214 | size_t i = 0; 215 | while(pr->headersused > i) 216 | { 217 | ParsedHeader_destroyOne(pr->headers + i); 218 | i++; 219 | } 220 | pr->headersused = 0; 221 | 222 | free(pr->headers); 223 | pr->headerslen = 0; 224 | } 225 | 226 | 227 | int ParsedHeader_parse(struct ParsedRequest * pr, char * line) 228 | { 229 | char * key; 230 | char * value; 231 | char * index1; 232 | char * index2; 233 | 234 | index1 = index(line, ':'); 235 | if(index1 == NULL) 236 | { 237 | debug("No colon found\n"); 238 | return -1; 239 | } 240 | key = (char *)malloc((index1-line+1)*sizeof(char)); 241 | memcpy(key, line, index1-line); 242 | key[index1-line]='\0'; 243 | 244 | index1 += 2; 245 | index2 = strstr(index1, "\r\n"); 246 | value = (char *) malloc((index2-index1+1)*sizeof(char)); 247 | memcpy(value, index1, (index2-index1)); 248 | value[index2-index1] = '\0'; 249 | 250 | ParsedHeader_set(pr, key, value); 251 | free(key); 252 | free(value); 253 | return 0; 254 | } 255 | 256 | /* 257 | ParsedRequest Public Methods 258 | */ 259 | 260 | void ParsedRequest_destroy(struct ParsedRequest *pr) 261 | { 262 | if(pr->buf != NULL) 263 | { 264 | free(pr->buf); 265 | } 266 | if (pr->path != NULL) { 267 | free(pr->path); 268 | } 269 | if(pr->headerslen > 0) 270 | { 271 | ParsedHeader_destroy(pr); 272 | } 273 | free(pr); 274 | } 275 | 276 | struct ParsedRequest* ParsedRequest_create() 277 | { 278 | struct ParsedRequest *pr; 279 | pr = (struct ParsedRequest *)malloc(sizeof(struct ParsedRequest)); 280 | if (pr != NULL) 281 | { 282 | ParsedHeader_create(pr); 283 | pr->buf = NULL; 284 | pr->method = NULL; 285 | pr->protocol = NULL; 286 | pr->host = NULL; 287 | pr->path = NULL; 288 | pr->version = NULL; 289 | pr->buf = NULL; 290 | pr->buflen = 0; 291 | } 292 | return pr; 293 | } 294 | 295 | /* 296 | Recreate the entire buffer from a parsed request object. 297 | buf must be allocated 298 | */ 299 | int ParsedRequest_unparse(struct ParsedRequest *pr, char *buf, 300 | size_t buflen) 301 | { 302 | if (!pr || !pr->buf) 303 | return -1; 304 | 305 | size_t tmp; 306 | if (ParsedRequest_printRequestLine(pr, buf, buflen, &tmp) < 0) 307 | return -1; 308 | if (ParsedHeader_printHeaders(pr, buf+tmp, buflen-tmp) < 0) 309 | return -1; 310 | return 0; 311 | } 312 | 313 | /* 314 | Recreate the headers from a parsed request object. 315 | buf must be allocated 316 | */ 317 | int ParsedRequest_unparse_headers(struct ParsedRequest *pr, char *buf, 318 | size_t buflen) 319 | { 320 | if (!pr || !pr->buf) 321 | return -1; 322 | 323 | if (ParsedHeader_printHeaders(pr, buf, buflen) < 0) 324 | return -1; 325 | return 0; 326 | } 327 | 328 | 329 | /* Size of the headers if unparsed into a string */ 330 | size_t ParsedRequest_totalLen(struct ParsedRequest *pr) 331 | { 332 | if (!pr || !pr->buf) 333 | return 0; 334 | return ParsedRequest_requestLineLen(pr)+ParsedHeader_headersLen(pr); 335 | } 336 | 337 | 338 | /* 339 | Parse request buffer 340 | 341 | Parameters: 342 | parse: ptr to a newly created ParsedRequest object 343 | buf: ptr to the buffer containing the request (need not be NUL terminated) 344 | and the trailing \r\n\r\n 345 | buflen: length of the buffer including the trailing \r\n\r\n 346 | 347 | Return values: 348 | -1: failure 349 | 0: success 350 | */ 351 | int 352 | ParsedRequest_parse(struct ParsedRequest * parse, const char *buf, 353 | int buflen) 354 | { 355 | char *full_addr; 356 | char *saveptr; 357 | char *index; 358 | char *currentHeader; 359 | 360 | if (parse->buf != NULL) { 361 | debug("parse object already assigned to a request\n"); 362 | return -1; 363 | } 364 | 365 | if (buflen < MIN_REQ_LEN || buflen > MAX_REQ_LEN) { 366 | debug("invalid buflen %d", buflen); 367 | return -1; 368 | } 369 | 370 | /* Create NUL terminated tmp buffer */ 371 | char *tmp_buf = (char *)malloc(buflen + 1); /* including NUL */ 372 | memcpy(tmp_buf, buf, buflen); 373 | tmp_buf[buflen] = '\0'; 374 | 375 | index = strstr(tmp_buf, "\r\n\r\n"); 376 | if (index == NULL) { 377 | debug("invalid request line, no end of header\n"); 378 | free(tmp_buf); 379 | return -1; 380 | } 381 | 382 | /* Copy request line into parse->buf */ 383 | index = strstr(tmp_buf, "\r\n"); 384 | if (parse->buf == NULL) { 385 | parse->buf = (char *) malloc((index-tmp_buf)+1); 386 | parse->buflen = (index-tmp_buf)+1; 387 | } 388 | memcpy(parse->buf, tmp_buf, index-tmp_buf); 389 | parse->buf[index-tmp_buf] = '\0'; 390 | 391 | /* Parse request line */ 392 | parse->method = strtok_r(parse->buf, " ", &saveptr); 393 | if (parse->method == NULL) { 394 | debug( "invalid request line, no whitespace\n"); 395 | free(tmp_buf); 396 | free(parse->buf); 397 | parse->buf = NULL; 398 | return -1; 399 | } 400 | if (strcmp (parse->method, "GET")) { 401 | debug( "invalid request line, method not 'GET': %s\n", 402 | parse->method); 403 | free(tmp_buf); 404 | free(parse->buf); 405 | parse->buf = NULL; 406 | return -1; 407 | } 408 | 409 | full_addr = strtok_r(NULL, " ", &saveptr); 410 | 411 | if (full_addr == NULL) { 412 | debug( "invalid request line, no full address\n"); 413 | free(tmp_buf); 414 | free(parse->buf); 415 | parse->buf = NULL; 416 | return -1; 417 | } 418 | 419 | parse->version = full_addr + strlen(full_addr) + 1; 420 | 421 | if (parse->version == NULL) { 422 | debug( "invalid request line, missing version\n"); 423 | free(tmp_buf); 424 | free(parse->buf); 425 | parse->buf = NULL; 426 | return -1; 427 | } 428 | if (strncmp (parse->version, "HTTP/", 5)) { 429 | debug( "invalid request line, unsupported version %s\n", 430 | parse->version); 431 | free(tmp_buf); 432 | free(parse->buf); 433 | parse->buf = NULL; 434 | return -1; 435 | } 436 | 437 | 438 | parse->protocol = strtok_r(full_addr, "://", &saveptr); 439 | if (parse->protocol == NULL) { 440 | debug( "invalid request line, missing host\n"); 441 | free(tmp_buf); 442 | free(parse->buf); 443 | parse->buf = NULL; 444 | return -1; 445 | } 446 | 447 | const char *rem = full_addr + strlen(parse->protocol) + strlen("://"); 448 | size_t abs_uri_len = strlen(rem); 449 | 450 | parse->host = strtok_r(NULL, "/", &saveptr); 451 | if (parse->host == NULL) { 452 | debug( "invalid request line, missing host\n"); 453 | free(tmp_buf); 454 | free(parse->buf); 455 | parse->buf = NULL; 456 | return -1; 457 | } 458 | 459 | if (strlen(parse->host) == abs_uri_len) { 460 | debug("invalid request line, missing absolute path\n"); 461 | free(tmp_buf); 462 | free(parse->buf); 463 | parse->buf = NULL; 464 | return -1; 465 | } 466 | 467 | parse->path = strtok_r(NULL, " ", &saveptr); 468 | if (parse->path == NULL) { // replace empty abs_path with "/" 469 | int rlen = strlen(root_abs_path); 470 | parse->path = (char *)malloc(rlen + 1); 471 | strncpy(parse->path, root_abs_path, rlen + 1); 472 | } else if (strncmp(parse->path, root_abs_path, strlen(root_abs_path)) == 0) { 473 | debug("invalid request line, path cannot begin " 474 | "with two slash characters\n"); 475 | free(tmp_buf); 476 | free(parse->buf); 477 | parse->buf = NULL; 478 | parse->path = NULL; 479 | return -1; 480 | } else { 481 | // copy parse->path, prefix with a slash 482 | char *tmp_path = parse->path; 483 | int rlen = strlen(root_abs_path); 484 | int plen = strlen(parse->path); 485 | parse->path = (char *)malloc(rlen + plen + 1); 486 | strncpy(parse->path, root_abs_path, rlen); 487 | strncpy(parse->path + rlen, tmp_path, plen + 1); 488 | } 489 | 490 | parse->host = strtok_r(parse->host, ":", &saveptr); 491 | parse->port = strtok_r(NULL, "/", &saveptr); 492 | 493 | if (parse->host == NULL) { 494 | debug( "invalid request line, missing host\n"); 495 | free(tmp_buf); 496 | free(parse->buf); 497 | free(parse->path); 498 | parse->buf = NULL; 499 | parse->path = NULL; 500 | return -1; 501 | } 502 | 503 | if (parse->port != NULL) { 504 | int port = strtol (parse->port, (char **)NULL, 10); 505 | if (port == 0 && errno == EINVAL) { 506 | debug("invalid request line, bad port: %s\n", parse->port); 507 | free(tmp_buf); 508 | free(parse->buf); 509 | free(parse->path); 510 | parse->buf = NULL; 511 | parse->path = NULL; 512 | return -1; 513 | } 514 | } 515 | 516 | 517 | /* Parse headers */ 518 | int ret = 0; 519 | currentHeader = strstr(tmp_buf, "\r\n")+2; 520 | while (currentHeader[0] != '\0' && 521 | !(currentHeader[0] == '\r' && currentHeader[1] == '\n')) { 522 | 523 | //debug("line %s %s", parse->version, currentHeader); 524 | 525 | if (ParsedHeader_parse(parse, currentHeader)) { 526 | ret = -1; 527 | break; 528 | } 529 | 530 | currentHeader = strstr(currentHeader, "\r\n"); 531 | if (currentHeader == NULL || strlen (currentHeader) < 2) 532 | break; 533 | 534 | currentHeader += 2; 535 | } 536 | free(tmp_buf); 537 | return ret; 538 | } 539 | 540 | /* 541 | ParsedRequest Private Methods 542 | */ 543 | 544 | size_t ParsedRequest_requestLineLen(struct ParsedRequest *pr) 545 | { 546 | if (!pr || !pr->buf) 547 | return 0; 548 | 549 | size_t len = 550 | strlen(pr->method) + 1 + strlen(pr->protocol) + 3 + 551 | strlen(pr->host) + 1 + strlen(pr->version) + 2; 552 | if(pr->port != NULL) 553 | { 554 | len += strlen(pr->port)+1; 555 | } 556 | /* path is at least a slash */ 557 | len += strlen(pr->path); 558 | return len; 559 | } 560 | 561 | int ParsedRequest_printRequestLine(struct ParsedRequest *pr, 562 | char * buf, size_t buflen, 563 | size_t *tmp) 564 | { 565 | char * current = buf; 566 | 567 | if(buflen < ParsedRequest_requestLineLen(pr)) 568 | { 569 | debug("not enough memory for first line\n"); 570 | return -1; 571 | } 572 | memcpy(current, pr->method, strlen(pr->method)); 573 | current += strlen(pr->method); 574 | current[0] = ' '; 575 | current += 1; 576 | 577 | memcpy(current, pr->protocol, strlen(pr->protocol)); 578 | current += strlen(pr->protocol); 579 | memcpy(current, "://", 3); 580 | current += 3; 581 | memcpy(current, pr->host, strlen(pr->host)); 582 | current += strlen(pr->host); 583 | if(pr->port != NULL) 584 | { 585 | current[0] = ':'; 586 | current += 1; 587 | memcpy(current, pr->port, strlen(pr->port)); 588 | current += strlen(pr->port); 589 | } 590 | /* path is at least a slash */ 591 | memcpy(current, pr->path, strlen(pr->path)); 592 | current += strlen(pr->path); 593 | 594 | current[0] = ' '; 595 | current += 1; 596 | 597 | memcpy(current, pr->version, strlen(pr->version)); 598 | current += strlen(pr->version); 599 | memcpy(current, "\r\n", 2); 600 | current +=2; 601 | *tmp = current-buf; 602 | return 0; 603 | } 604 | 605 | -------------------------------------------------------------------------------- /proxy_parse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * proxy_parse.h -- a HTTP Request Parsing Library. 3 | * 4 | * Written by: Matvey Arye 5 | * For: COS 518 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #ifndef PROXY_PARSE 18 | #define PROXY_PARSE 19 | 20 | #define DEBUG 1 21 | 22 | /* 23 | ParsedRequest objects are created from parsing a buffer containing a HTTP 24 | request. The request buffer consists of a request line followed by a number 25 | of headers. Request line fields such as method, protocol etc. are stored 26 | explicitly. Headers such as 'Content-Length' and their values are maintained 27 | in a linked list. Each node in this list is a ParsedHeader and contains a 28 | key-value pair. 29 | 30 | The buf and buflen fields are used internally to maintain the parsed request 31 | line. 32 | */ 33 | struct ParsedRequest { 34 | char *method; 35 | char *protocol; 36 | char *host; 37 | char *port; 38 | char *path; 39 | char *version; 40 | char *buf; 41 | size_t buflen; 42 | struct ParsedHeader *headers; 43 | size_t headersused; 44 | size_t headerslen; 45 | }; 46 | 47 | /* 48 | ParsedHeader: any header after the request line is a key-value pair with the 49 | format "key:value\r\n" and is maintained in the ParsedHeader linked list 50 | within ParsedRequest 51 | */ 52 | struct ParsedHeader { 53 | char * key; 54 | size_t keylen; 55 | char * value; 56 | size_t valuelen; 57 | }; 58 | 59 | 60 | /* Create an empty parsing object to be used exactly once for parsing a single 61 | * request buffer */ 62 | struct ParsedRequest* ParsedRequest_create(); 63 | 64 | /* Parse the request buffer in buf given that buf is of length buflen */ 65 | int ParsedRequest_parse(struct ParsedRequest * parse, const char *buf, 66 | int buflen); 67 | 68 | /* Destroy the parsing object. */ 69 | void ParsedRequest_destroy(struct ParsedRequest *pr); 70 | 71 | /* 72 | Retrieve the entire buffer from a parsed request object. buf must be an 73 | allocated buffer of size buflen, with enough space to write the request 74 | line, headers and the trailing \r\n. buf will not be NUL terminated by 75 | unparse(). 76 | */ 77 | int ParsedRequest_unparse(struct ParsedRequest *pr, char *buf, 78 | size_t buflen); 79 | 80 | /* 81 | Retrieve the entire buffer with the exception of request line from a parsed 82 | request object. buf must be an allocated buffer of size buflen, with enough 83 | space to write the headers and the trailing \r\n. buf will not be NUL 84 | terminated by unparse(). If there are no headers, the trailing \r\n is 85 | unparsed. 86 | */ 87 | int ParsedRequest_unparse_headers(struct ParsedRequest *pr, char *buf, 88 | size_t buflen); 89 | 90 | /* Total length including request line, headers and the trailing \r\n*/ 91 | size_t ParsedRequest_totalLen(struct ParsedRequest *pr); 92 | 93 | /* Length including headers, if any, and the trailing \r\n but excluding the 94 | * request line. 95 | */ 96 | size_t ParsedHeader_headersLen(struct ParsedRequest *pr); 97 | 98 | /* Set, get, and remove null-terminated header keys and values */ 99 | int ParsedHeader_set(struct ParsedRequest *pr, const char * key, 100 | const char * value); 101 | struct ParsedHeader* ParsedHeader_get(struct ParsedRequest *pr, 102 | const char * key); 103 | int ParsedHeader_remove (struct ParsedRequest *pr, const char * key); 104 | 105 | /* debug() prints out debugging info if DEBUG is set to 1 */ 106 | void debug(const char * format, ...); 107 | 108 | /* Example usage: 109 | 110 | const char *c = 111 | "GET http://www.google.com:80/index.html/ HTTP/1.0\r\nContent-Length:" 112 | " 80\r\nIf-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT\r\n\r\n"; 113 | 114 | int len = strlen(c); 115 | //Create a ParsedRequest to use. This ParsedRequest 116 | //is dynamically allocated. 117 | ParsedRequest *req = ParsedRequest_create(); 118 | if (ParsedRequest_parse(req, c, len) < 0) { 119 | printf("parse failed\n"); 120 | return -1; 121 | } 122 | 123 | printf("Method:%s\n", req->method); 124 | printf("Host:%s\n", req->host); 125 | 126 | // Turn ParsedRequest into a string. 127 | // Friendly reminder: Be sure that you need to 128 | // dynamically allocate string and if you 129 | // do, remember to free it when you are done. 130 | // (Dynamic allocation wasn't necessary here, 131 | // but it was used as an example.) 132 | int rlen = ParsedRequest_totalLen(req); 133 | char *b = (char *)malloc(rlen+1); 134 | if (ParsedRequest_unparse(req, b, rlen) < 0) { 135 | printf("unparse failed\n"); 136 | return -1; 137 | } 138 | b[rlen]='\0'; 139 | // print out b for text request 140 | free(b); 141 | 142 | 143 | // Turn the headers from the request into a string. 144 | rlen = ParsedHeader_headersLen(req); 145 | char buf[rlen+1]; 146 | if (ParsedRequest_unparse_headers(req, buf, rlen) < 0) { 147 | printf("unparse failed\n"); 148 | return -1; 149 | } 150 | buf[rlen] ='\0'; 151 | //print out buf for text headers only 152 | 153 | // Get a specific header (key) from the headers. A key is a header field 154 | // such as "If-Modified-Since" which is followed by ":" 155 | struct ParsedHeader *r = ParsedHeader_get(req, "If-Modified-Since"); 156 | printf("Modified value: %s\n", r->value); 157 | 158 | // Remove a specific header by name. In this case remove 159 | // the "If-Modified-Since" header. 160 | if (ParsedHeader_remove(req, "If-Modified-Since") < 0){ 161 | printf("remove header key not work\n"); 162 | return -1; 163 | } 164 | 165 | // Set a specific header (key) to a value. In this case, 166 | //we set the "Last-Modified" key to be set to have as 167 | //value a date in February 2014 168 | 169 | if (ParsedHeader_set(req, "Last-Modified", " Wed, 12 Feb 2014 12:43:31 GMT") < 0){ 170 | printf("set header key not work\n"); 171 | return -1; 172 | 173 | } 174 | 175 | // Check the modified Header key value pair 176 | r = ParsedHeader_get(req, "Last-Modified"); 177 | printf("Last-Modified value: %s\n", r->value); 178 | 179 | // Call destroy on any ParsedRequests that you 180 | // create once you are done using them. This will 181 | // free memory dynamically allocated by the proxy_parse library. 182 | ParsedRequest_destroy(req); 183 | */ 184 | 185 | #endif 186 | 187 | -------------------------------------------------------------------------------- /proxy_server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omsurase/MultithreadedWebserver/a5835cade5c7e838f1d4fc6e4f9f4afe86dffdee/proxy_server -------------------------------------------------------------------------------- /v1/proxy2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "proxy_parse.h" 24 | 25 | using namespace std; 26 | 27 | struct cache_element 28 | { 29 | char *data; 30 | int len; 31 | char *url; 32 | time_t lru_time_track; 33 | }; 34 | 35 | class LRUCache 36 | { 37 | public: 38 | LRUCache(size_t capacity); 39 | ~LRUCache(); 40 | 41 | cache_element *find(const char *url); 42 | void add(const char *url, const char *data, int len); 43 | void remove(const char *url); 44 | 45 | private: 46 | size_t capacity; 47 | list cache_list; 48 | unordered_map::iterator> cache_map; 49 | mutex cache_mutex; 50 | 51 | void moveToFront(list::iterator it); 52 | void evict(); 53 | }; 54 | 55 | LRUCache::LRUCache(size_t cap) : capacity(cap) {} 56 | 57 | LRUCache::~LRUCache() 58 | { 59 | for (auto elem : cache_list) 60 | { 61 | free(elem->data); 62 | free(elem->url); 63 | delete elem; 64 | } 65 | } 66 | 67 | cache_element *LRUCache::find(const char *url) 68 | { 69 | lock_guard lock(cache_mutex); 70 | auto it = cache_map.find(url); 71 | if (it == cache_map.end()) 72 | return nullptr; 73 | 74 | moveToFront(it->second); 75 | (*it->second)->lru_time_track = time(nullptr); 76 | return *it->second; 77 | } 78 | 79 | void LRUCache::add(const char *url, const char *data, int len) 80 | { 81 | lock_guard lock(cache_mutex); 82 | auto it = cache_map.find(url); 83 | if (it != cache_map.end()) 84 | { 85 | moveToFront(it->second); 86 | printf("Updating existing cache entry for URL: %s\n", url); 87 | return; 88 | } 89 | 90 | if (cache_list.size() >= capacity) 91 | { 92 | printf("Cache full, evicting least recently used entry\n"); 93 | evict(); 94 | } 95 | 96 | cache_element *new_element = new cache_element; 97 | new_element->url = strdup(url); 98 | new_element->data = (char *)malloc(len); 99 | memcpy(new_element->data, data, len); 100 | new_element->len = len; 101 | new_element->lru_time_track = time(nullptr); 102 | 103 | cache_list.push_front(new_element); 104 | cache_map[url] = cache_list.begin(); 105 | printf("Stored new response in cache for URL: %s\n", url); 106 | } 107 | 108 | void LRUCache::remove(const char *url) 109 | { 110 | lock_guard lock(cache_mutex); 111 | auto it = cache_map.find(url); 112 | if (it == cache_map.end()) 113 | return; 114 | 115 | cache_element *elem = *it->second; 116 | cache_list.erase(it->second); 117 | cache_map.erase(it); 118 | 119 | free(elem->data); 120 | free(elem->url); 121 | delete elem; 122 | } 123 | 124 | void LRUCache::moveToFront(list::iterator it) 125 | { 126 | cache_list.splice(cache_list.begin(), cache_list, it); 127 | } 128 | 129 | void LRUCache::evict() 130 | { 131 | cache_element *elem = cache_list.back(); 132 | cache_map.erase(elem->url); 133 | cache_list.pop_back(); 134 | 135 | free(elem->data); 136 | free(elem->url); 137 | delete elem; 138 | } 139 | 140 | class ThreadPool 141 | { 142 | public: 143 | ThreadPool(size_t numThreads); 144 | ~ThreadPool(); 145 | 146 | void enqueue(std::function task); 147 | void waitForThreads(); // New method 148 | 149 | private: 150 | std::vector workers; 151 | std::queue> tasks; 152 | 153 | std::mutex queueMutex; 154 | std::condition_variable condition; 155 | std::atomic stop; 156 | std::atomic readyThreads; // New variable to track ready threads 157 | std::condition_variable allThreadsReady; // New condition variable 158 | }; 159 | 160 | ThreadPool::ThreadPool(size_t numThreads) : stop(false), readyThreads(0) 161 | { 162 | for (size_t i = 0; i < numThreads; ++i) 163 | { 164 | workers.emplace_back([this] 165 | { 166 | { 167 | std::unique_lock lock(this->queueMutex); 168 | readyThreads++; 169 | allThreadsReady.notify_one(); 170 | } 171 | for(;;) { 172 | std::function task; 173 | 174 | { 175 | std::unique_lock lock(this->queueMutex); 176 | this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); }); 177 | if(this->stop && this->tasks.empty()) return; 178 | task = std::move(this->tasks.front()); 179 | this->tasks.pop(); 180 | } 181 | 182 | task(); 183 | } }); 184 | } 185 | } 186 | 187 | ThreadPool::~ThreadPool() 188 | { 189 | stop = true; 190 | condition.notify_all(); 191 | for (std::thread &worker : workers) 192 | worker.join(); 193 | } 194 | 195 | void ThreadPool::enqueue(std::function task) 196 | { 197 | { 198 | std::unique_lock lock(queueMutex); 199 | tasks.emplace(task); 200 | } 201 | condition.notify_one(); 202 | } 203 | 204 | char *convert_Request_to_string(struct ParsedRequest *req) 205 | { 206 | /* Set headers */ 207 | ParsedHeader_set(req, "Host", req->host); 208 | ParsedHeader_set(req, "Connection", "close"); 209 | 210 | int iHeadersLen = ParsedHeader_headersLen(req); 211 | 212 | char *headersBuf; 213 | 214 | headersBuf = (char *)malloc(iHeadersLen + 1); 215 | 216 | if (headersBuf == NULL) 217 | { 218 | fprintf(stderr, " Error in memory allocation of headersBuffer ! \n"); 219 | exit(1); 220 | } 221 | 222 | ParsedRequest_unparse_headers(req, headersBuf, iHeadersLen); 223 | headersBuf[iHeadersLen] = '\0'; 224 | 225 | int request_size = strlen(req->method) + strlen(req->path) + strlen(req->version) + iHeadersLen + 4; 226 | 227 | char *serverReq; 228 | 229 | serverReq = (char *)malloc(request_size + 1); 230 | 231 | if (serverReq == NULL) 232 | { 233 | fprintf(stderr, " Error in memory allocation for serverrequest ! \n"); 234 | exit(1); 235 | } 236 | 237 | serverReq[0] = '\0'; 238 | strcpy(serverReq, req->method); 239 | strcat(serverReq, " "); 240 | strcat(serverReq, req->path); 241 | strcat(serverReq, " "); 242 | strcat(serverReq, req->version); 243 | strcat(serverReq, "\r\n"); 244 | strcat(serverReq, headersBuf); 245 | 246 | free(headersBuf); 247 | 248 | return serverReq; 249 | } 250 | 251 | int createserverSocket(char *pcAddress, char *pcPort) 252 | { 253 | struct addrinfo ahints; 254 | struct addrinfo *paRes; 255 | 256 | int iSockfd; 257 | 258 | /* Get address information for stream socket on input port */ 259 | memset(&ahints, 0, sizeof(ahints)); 260 | ahints.ai_family = AF_UNSPEC; 261 | ahints.ai_socktype = SOCK_STREAM; 262 | if (getaddrinfo(pcAddress, pcPort, &ahints, &paRes) != 0) 263 | { 264 | fprintf(stderr, " Error in server address format ! \n"); 265 | exit(1); 266 | } 267 | 268 | /* Create and connect */ 269 | if ((iSockfd = socket(paRes->ai_family, paRes->ai_socktype, paRes->ai_protocol)) < 0) 270 | { 271 | fprintf(stderr, " Error in creating socket to server ! \n"); 272 | exit(1); 273 | } 274 | if (connect(iSockfd, paRes->ai_addr, paRes->ai_addrlen) < 0) 275 | { 276 | fprintf(stderr, " Error in connecting to server ! \n"); 277 | exit(1); 278 | } 279 | 280 | /* Free paRes, which was dynamically allocated by getaddrinfo */ 281 | freeaddrinfo(paRes); 282 | 283 | return iSockfd; 284 | } 285 | 286 | void writeToserverSocket(const char *buff_to_server, int sockfd, int buff_length) 287 | { 288 | string temp; 289 | temp.append(buff_to_server); 290 | 291 | int totalsent = 0; 292 | int senteach; 293 | 294 | while (totalsent < buff_length) 295 | { 296 | if ((senteach = send(sockfd, (void *)(buff_to_server + totalsent), buff_length - totalsent, 0)) < 0) 297 | { 298 | fprintf(stderr, " Error in sending to server ! \n"); 299 | exit(1); 300 | } 301 | totalsent += senteach; 302 | } 303 | } 304 | 305 | void writeToclientSocket(const char *buff_to_client, int sockfd, int buff_length) 306 | { 307 | string temp; 308 | temp.append(buff_to_client); 309 | 310 | int totalsent = 0; 311 | int senteach; 312 | 313 | while (totalsent < buff_length) 314 | { 315 | if ((senteach = send(sockfd, (void *)(buff_to_client + totalsent), buff_length - totalsent, 0)) < 0) 316 | { 317 | fprintf(stderr, " Error in sending to client ! \n"); 318 | exit(1); 319 | } 320 | totalsent += senteach; 321 | } 322 | } 323 | 324 | void writeToClient(int Clientfd, int Serverfd, LRUCache &cache, const char *url) 325 | { 326 | int MAX_BUF_SIZE = 5000; 327 | int iRecv; 328 | char buf[MAX_BUF_SIZE]; 329 | 330 | string response; 331 | while ((iRecv = recv(Serverfd, buf, MAX_BUF_SIZE, 0)) > 0) 332 | { 333 | response.append(buf, iRecv); 334 | writeToclientSocket(buf, Clientfd, iRecv); 335 | memset(buf, 0, sizeof buf); 336 | } 337 | 338 | if (iRecv < 0) 339 | { 340 | fprintf(stderr, " Error while receiving from server ! \n"); 341 | exit(1); 342 | } 343 | 344 | cache.add(url, response.c_str(), response.size()); 345 | printf("Response received and stored in cache for URL: %s\n", url); 346 | } 347 | 348 | void ThreadPool::waitForThreads() 349 | { 350 | std::unique_lock lock(queueMutex); 351 | allThreadsReady.wait(lock, [this] 352 | { return readyThreads == workers.size(); }); 353 | } 354 | 355 | void *datafromclient(void *sockid) 356 | { 357 | int MAX_BUFFER_SIZE = 5000; 358 | char buf[MAX_BUFFER_SIZE]; 359 | 360 | int newsockfd = *((int *)sockid); 361 | 362 | char *request_message; // Get message from URL 363 | request_message = (char *)malloc(MAX_BUFFER_SIZE); 364 | 365 | if (request_message == NULL) 366 | { 367 | fprintf(stderr, " Error in memory allocation ! \n"); 368 | exit(1); 369 | } 370 | 371 | request_message[0] = '\0'; 372 | int total_recieved_bits = 0; 373 | 374 | while (strstr(request_message, "\r\n\r\n") == NULL) 375 | { 376 | int recvd = recv(newsockfd, buf, MAX_BUFFER_SIZE, 0); 377 | 378 | if (recvd < 0) 379 | { 380 | fprintf(stderr, " Error while receiving ! \n"); 381 | exit(1); 382 | } 383 | else if (recvd == 0) 384 | { 385 | break; 386 | } 387 | else 388 | { 389 | total_recieved_bits += recvd; 390 | 391 | if (total_recieved_bits > MAX_BUFFER_SIZE) 392 | { 393 | MAX_BUFFER_SIZE *= 2; 394 | request_message = (char *)realloc(request_message, MAX_BUFFER_SIZE); 395 | if (request_message == NULL) 396 | { 397 | fprintf(stderr, " Error in memory re-allocation ! \n"); 398 | exit(1); 399 | } 400 | } 401 | } 402 | 403 | strncat(request_message, buf, recvd); 404 | } 405 | 406 | struct ParsedRequest *req; 407 | req = ParsedRequest_create(); 408 | 409 | if (ParsedRequest_parse(req, request_message, strlen(request_message)) < 0) 410 | { 411 | fprintf(stderr, "Error in request message. Request message: %s\n", request_message); 412 | exit(0); 413 | } 414 | 415 | printf("Received request for URL: %s%s\n", req->host, req->path); 416 | 417 | if (req->port == NULL) 418 | req->port = (char *)"80"; 419 | 420 | char *browser_req = convert_Request_to_string(req); 421 | string url(req->host); 422 | url += req->path; 423 | 424 | static LRUCache cache(100); // Define the cache with a size of 100 425 | cache_element *cached_response = cache.find(url.c_str()); 426 | 427 | if (cached_response != nullptr) 428 | { 429 | printf("Cache hit: Response found in cache for URL: %s\n", url.c_str()); 430 | writeToclientSocket(cached_response->data, newsockfd, cached_response->len); 431 | } 432 | else 433 | { 434 | printf("Cache miss: Response not found in cache for URL: %s\n", url.c_str()); 435 | int iServerfd = createserverSocket(req->host, req->port); 436 | printf("Sending request to remote server: %s\n", req->host); 437 | writeToserverSocket(browser_req, iServerfd, strlen(browser_req)); 438 | writeToClient(newsockfd, iServerfd, cache, url.c_str()); 439 | close(iServerfd); 440 | } 441 | 442 | ParsedRequest_destroy(req); 443 | free(request_message); 444 | close(newsockfd); 445 | 446 | int y = 3; 447 | int *p = &y; 448 | return p; 449 | } 450 | 451 | int main(int argc, char *argv[]) 452 | { 453 | int sockfd, newsockfd; 454 | struct sockaddr_in serv_addr; 455 | struct sockaddr cli_addr; 456 | 457 | if (argc < 2) 458 | { 459 | fprintf(stderr, "SORRY! Provide A Port ! \n"); 460 | return 1; 461 | } 462 | 463 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 464 | 465 | if (sockfd < 0) 466 | { 467 | fprintf(stderr, "SORRY! Cannot create a socket ! \n"); 468 | return 1; 469 | } 470 | 471 | memset(&serv_addr, 0, sizeof serv_addr); 472 | 473 | int portno = atoi(argv[1]); 474 | serv_addr.sin_family = AF_INET; 475 | serv_addr.sin_addr.s_addr = INADDR_ANY; 476 | serv_addr.sin_port = htons(portno); 477 | 478 | int binded = bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); 479 | 480 | if (binded < 0) 481 | { 482 | fprintf(stderr, "Error on binding! \n"); 483 | return 1; 484 | } 485 | 486 | listen(sockfd, 1000); 487 | printf("Proxy server listening on port %d\n", portno); 488 | 489 | int clilen = sizeof(struct sockaddr); 490 | ThreadPool pool(250); 491 | pool.waitForThreads(); 492 | 493 | while (1) 494 | { 495 | newsockfd = accept(sockfd, &cli_addr, (socklen_t *)&clilen); 496 | 497 | if (newsockfd < 0) 498 | { 499 | fprintf(stderr, "ERROR! On Accepting Request ! i.e requests limit crossed \n"); 500 | continue; 501 | } 502 | printf("New client connection accepted\n"); 503 | pool.enqueue([newsockfd] 504 | { 505 | datafromclient((void *)&newsockfd); 506 | close(newsockfd); }); 507 | } 508 | 509 | close(sockfd); 510 | 511 | return 0; 512 | } 513 | -------------------------------------------------------------------------------- /v1/proxy3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "proxy_parse.h" 26 | 27 | using namespace std; 28 | 29 | #define MAX_THREADS 400 30 | sem_t thread_semaphore; 31 | volatile sig_atomic_t running = 1; 32 | 33 | void sigint_handler(int signum); 34 | void *worker_thread(void *arg); 35 | 36 | struct cache_element 37 | { 38 | char *data; 39 | int len; 40 | char *url; 41 | time_t lru_time_track; 42 | }; 43 | 44 | class LRUCache 45 | { 46 | public: 47 | LRUCache(size_t capacity); 48 | ~LRUCache(); 49 | 50 | cache_element *find(const char *url); 51 | void add(const char *url, const char *data, int len); 52 | void remove(const char *url); 53 | 54 | private: 55 | size_t capacity; 56 | list cache_list; 57 | unordered_map::iterator> cache_map; 58 | mutex cache_mutex; 59 | 60 | void moveToFront(list::iterator it); 61 | void evict(); 62 | }; 63 | 64 | LRUCache::LRUCache(size_t cap) : capacity(cap) {} 65 | 66 | LRUCache::~LRUCache() 67 | { 68 | for (auto elem : cache_list) 69 | { 70 | free(elem->data); 71 | free(elem->url); 72 | delete elem; 73 | } 74 | } 75 | 76 | cache_element *LRUCache::find(const char *url) 77 | { 78 | lock_guard lock(cache_mutex); 79 | auto it = cache_map.find(url); 80 | if (it == cache_map.end()) 81 | return nullptr; 82 | 83 | moveToFront(it->second); 84 | (*it->second)->lru_time_track = time(nullptr); 85 | return *it->second; 86 | } 87 | 88 | void LRUCache::add(const char *url, const char *data, int len) 89 | { 90 | lock_guard lock(cache_mutex); 91 | auto it = cache_map.find(url); 92 | if (it != cache_map.end()) 93 | { 94 | moveToFront(it->second); 95 | printf("Updating existing cache entry for URL: %s\n", url); 96 | return; 97 | } 98 | 99 | if (cache_list.size() >= capacity) 100 | { 101 | printf("Cache full, evicting least recently used entry\n"); 102 | evict(); 103 | } 104 | 105 | cache_element *new_element = new cache_element; 106 | new_element->url = strdup(url); 107 | new_element->data = (char *)malloc(len); 108 | memcpy(new_element->data, data, len); 109 | new_element->len = len; 110 | new_element->lru_time_track = time(nullptr); 111 | 112 | cache_list.push_front(new_element); 113 | cache_map[url] = cache_list.begin(); 114 | printf("Stored new response in cache for URL: %s\n", url); 115 | } 116 | 117 | void LRUCache::remove(const char *url) 118 | { 119 | lock_guard lock(cache_mutex); 120 | auto it = cache_map.find(url); 121 | if (it == cache_map.end()) 122 | return; 123 | 124 | cache_element *elem = *it->second; 125 | cache_list.erase(it->second); 126 | cache_map.erase(it); 127 | 128 | free(elem->data); 129 | free(elem->url); 130 | delete elem; 131 | } 132 | 133 | void LRUCache::moveToFront(list::iterator it) 134 | { 135 | cache_list.splice(cache_list.begin(), cache_list, it); 136 | } 137 | 138 | void LRUCache::evict() 139 | { 140 | cache_element *elem = cache_list.back(); 141 | cache_map.erase(elem->url); 142 | cache_list.pop_back(); 143 | 144 | free(elem->data); 145 | free(elem->url); 146 | delete elem; 147 | } 148 | 149 | char *convert_Request_to_string(struct ParsedRequest *req) 150 | { 151 | /* Set headers */ 152 | ParsedHeader_set(req, "Host", req->host); 153 | ParsedHeader_set(req, "Connection", "close"); 154 | 155 | int iHeadersLen = ParsedHeader_headersLen(req); 156 | 157 | char *headersBuf; 158 | 159 | headersBuf = (char *)malloc(iHeadersLen + 1); 160 | 161 | if (headersBuf == NULL) 162 | { 163 | fprintf(stderr, " Error in memory allocation of headersBuffer ! \n"); 164 | exit(1); 165 | } 166 | 167 | ParsedRequest_unparse_headers(req, headersBuf, iHeadersLen); 168 | headersBuf[iHeadersLen] = '\0'; 169 | 170 | int request_size = strlen(req->method) + strlen(req->path) + strlen(req->version) + iHeadersLen + 4; 171 | 172 | char *serverReq; 173 | 174 | serverReq = (char *)malloc(request_size + 1); 175 | 176 | if (serverReq == NULL) 177 | { 178 | fprintf(stderr, " Error in memory allocation for serverrequest ! \n"); 179 | exit(1); 180 | } 181 | 182 | serverReq[0] = '\0'; 183 | strcpy(serverReq, req->method); 184 | strcat(serverReq, " "); 185 | strcat(serverReq, req->path); 186 | strcat(serverReq, " "); 187 | strcat(serverReq, req->version); 188 | strcat(serverReq, "\r\n"); 189 | strcat(serverReq, headersBuf); 190 | 191 | free(headersBuf); 192 | 193 | return serverReq; 194 | } 195 | 196 | int createserverSocket(char *pcAddress, char *pcPort) 197 | { 198 | struct addrinfo ahints; 199 | struct addrinfo *paRes; 200 | 201 | int iSockfd; 202 | 203 | /* Get address information for stream socket on input port */ 204 | memset(&ahints, 0, sizeof(ahints)); 205 | ahints.ai_family = AF_UNSPEC; 206 | ahints.ai_socktype = SOCK_STREAM; 207 | if (getaddrinfo(pcAddress, pcPort, &ahints, &paRes) != 0) 208 | { 209 | fprintf(stderr, " Error in server address format ! \n"); 210 | exit(1); 211 | } 212 | 213 | /* Create and connect */ 214 | if ((iSockfd = socket(paRes->ai_family, paRes->ai_socktype, paRes->ai_protocol)) < 0) 215 | { 216 | fprintf(stderr, " Error in creating socket to server ! \n"); 217 | exit(1); 218 | } 219 | if (connect(iSockfd, paRes->ai_addr, paRes->ai_addrlen) < 0) 220 | { 221 | fprintf(stderr, " Error in connecting to server ! \n"); 222 | exit(1); 223 | } 224 | 225 | /* Free paRes, which was dynamically allocated by getaddrinfo */ 226 | freeaddrinfo(paRes); 227 | 228 | return iSockfd; 229 | } 230 | 231 | void writeToserverSocket(const char *buff_to_server, int sockfd, int buff_length) 232 | { 233 | string temp; 234 | temp.append(buff_to_server); 235 | 236 | int totalsent = 0; 237 | int senteach; 238 | 239 | while (totalsent < buff_length) 240 | { 241 | if ((senteach = send(sockfd, (void *)(buff_to_server + totalsent), buff_length - totalsent, 0)) < 0) 242 | { 243 | fprintf(stderr, " Error in sending to server ! \n"); 244 | exit(1); 245 | } 246 | totalsent += senteach; 247 | } 248 | } 249 | 250 | void writeToclientSocket(const char *buff_to_client, int sockfd, int buff_length) 251 | { 252 | string temp; 253 | temp.append(buff_to_client); 254 | 255 | int totalsent = 0; 256 | int senteach; 257 | 258 | while (totalsent < buff_length) 259 | { 260 | if ((senteach = send(sockfd, (void *)(buff_to_client + totalsent), buff_length - totalsent, 0)) < 0) 261 | { 262 | fprintf(stderr, " Error in sending to client ! \n"); 263 | exit(1); 264 | } 265 | totalsent += senteach; 266 | } 267 | } 268 | 269 | void writeToClient(int Clientfd, int Serverfd, LRUCache &cache, const char *url) 270 | { 271 | int MAX_BUF_SIZE = 5000; 272 | int iRecv; 273 | char buf[MAX_BUF_SIZE]; 274 | 275 | string response; 276 | while ((iRecv = recv(Serverfd, buf, MAX_BUF_SIZE, 0)) > 0) 277 | { 278 | response.append(buf, iRecv); 279 | writeToclientSocket(buf, Clientfd, iRecv); 280 | memset(buf, 0, sizeof buf); 281 | } 282 | 283 | if (iRecv < 0) 284 | { 285 | fprintf(stderr, " Error while receiving from server ! \n"); 286 | exit(1); 287 | } 288 | 289 | cache.add(url, response.c_str(), response.size()); 290 | printf("Response received and stored in cache for URL: %s\n", url); 291 | } 292 | 293 | void datafromclient(void *sockid) 294 | { 295 | int MAX_BUFFER_SIZE = 5000; 296 | char buf[MAX_BUFFER_SIZE]; 297 | 298 | int newsockfd = *((int *)sockid); 299 | 300 | char *request_message; // Get message from URL 301 | request_message = (char *)malloc(MAX_BUFFER_SIZE); 302 | 303 | if (request_message == NULL) 304 | { 305 | fprintf(stderr, " Error in memory allocation ! \n"); 306 | exit(1); 307 | } 308 | 309 | request_message[0] = '\0'; 310 | int total_recieved_bits = 0; 311 | 312 | while (strstr(request_message, "\r\n\r\n") == NULL) 313 | { 314 | int recvd = recv(newsockfd, buf, MAX_BUFFER_SIZE, 0); 315 | 316 | if (recvd < 0) 317 | { 318 | fprintf(stderr, " Error while receiving ! \n"); 319 | exit(1); 320 | } 321 | else if (recvd == 0) 322 | { 323 | break; 324 | } 325 | else 326 | { 327 | total_recieved_bits += recvd; 328 | 329 | if (total_recieved_bits > MAX_BUFFER_SIZE) 330 | { 331 | MAX_BUFFER_SIZE *= 2; 332 | request_message = (char *)realloc(request_message, MAX_BUFFER_SIZE); 333 | if (request_message == NULL) 334 | { 335 | fprintf(stderr, " Error in memory re-allocation ! \n"); 336 | exit(1); 337 | } 338 | } 339 | } 340 | 341 | strncat(request_message, buf, recvd); 342 | } 343 | 344 | struct ParsedRequest *req; 345 | req = ParsedRequest_create(); 346 | 347 | if (ParsedRequest_parse(req, request_message, strlen(request_message)) < 0) 348 | { 349 | fprintf(stderr, "Error in request message. Request message: %s\n", request_message); 350 | exit(0); 351 | } 352 | 353 | printf("Received request for URL: %s%s\n", req->host, req->path); 354 | 355 | if (req->port == NULL) 356 | req->port = (char *)"80"; 357 | 358 | char *browser_req = convert_Request_to_string(req); 359 | string url(req->host); 360 | url += req->path; 361 | 362 | static LRUCache cache(100); // Define the cache with a size of 100 363 | cache_element *cached_response = cache.find(url.c_str()); 364 | 365 | if (cached_response != nullptr) 366 | { 367 | printf("Cache hit: Response found in cache for URL: %s\n", url.c_str()); 368 | writeToclientSocket(cached_response->data, newsockfd, cached_response->len); 369 | } 370 | else 371 | { 372 | printf("Cache miss: Response not found in cache for URL: %s\n", url.c_str()); 373 | int iServerfd = createserverSocket(req->host, req->port); 374 | printf("Sending request to remote server: %s\n", req->host); 375 | writeToserverSocket(browser_req, iServerfd, strlen(browser_req)); 376 | writeToClient(newsockfd, iServerfd, cache, url.c_str()); 377 | close(iServerfd); 378 | } 379 | 380 | ParsedRequest_destroy(req); 381 | free(request_message); 382 | close(newsockfd); 383 | } 384 | 385 | int main(int argc, char *argv[]) 386 | { 387 | int sockfd; 388 | struct sockaddr_in serv_addr; 389 | 390 | if (argc < 2) 391 | { 392 | fprintf(stderr, "SORRY! Provide A Port ! \n"); 393 | return 1; 394 | } 395 | 396 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 397 | 398 | if (sockfd < 0) 399 | { 400 | fprintf(stderr, "SORRY! Cannot create a socket ! \n"); 401 | return 1; 402 | } 403 | 404 | memset(&serv_addr, 0, sizeof serv_addr); 405 | 406 | int portno = atoi(argv[1]); 407 | serv_addr.sin_family = AF_INET; 408 | serv_addr.sin_addr.s_addr = INADDR_ANY; 409 | serv_addr.sin_port = htons(portno); 410 | 411 | int binded = bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); 412 | 413 | if (binded < 0) 414 | { 415 | fprintf(stderr, "Error on binding! \n"); 416 | return 1; 417 | } 418 | 419 | listen(sockfd, 100); 420 | printf("Proxy server listening on port %d\n", portno); 421 | 422 | // Initialize semaphore 423 | sem_init(&thread_semaphore, 0, MAX_THREADS); 424 | 425 | // Set up signal handler 426 | signal(SIGINT, sigint_handler); 427 | 428 | // Create worker threads 429 | pthread_t worker_threads[MAX_THREADS]; 430 | for (int i = 0; i < MAX_THREADS; i++) 431 | { 432 | pthread_create(&worker_threads[i], NULL, worker_thread, (void *)&sockfd); 433 | } 434 | 435 | // Wait for worker threads to finish 436 | for (int i = 0; i < MAX_THREADS; i++) 437 | { 438 | pthread_join(worker_threads[i], NULL); 439 | } 440 | 441 | // Cleanup 442 | sem_destroy(&thread_semaphore); 443 | close(sockfd); 444 | 445 | return 0; 446 | } 447 | 448 | void sigint_handler(int signum) 449 | { 450 | running = 0; 451 | } 452 | 453 | void *worker_thread(void *arg) 454 | { 455 | int sockfd = *((int *)arg); 456 | 457 | while (running) 458 | { 459 | sem_wait(&thread_semaphore); 460 | 461 | if (!running) 462 | { 463 | sem_post(&thread_semaphore); 464 | break; 465 | } 466 | 467 | int newsockfd = accept(sockfd, NULL, NULL); 468 | 469 | if (newsockfd < 0) 470 | { 471 | if (errno == EINTR) 472 | { 473 | sem_post(&thread_semaphore); 474 | continue; 475 | } 476 | fprintf(stderr, "ERROR! On Accepting Request ! i.e requests limit crossed \n"); 477 | sem_post(&thread_semaphore); 478 | continue; 479 | } 480 | 481 | printf("New client connection accepted\n"); 482 | datafromclient((void *)&newsockfd); 483 | close(newsockfd); 484 | 485 | sem_post(&thread_semaphore); 486 | } 487 | 488 | return NULL; 489 | } -------------------------------------------------------------------------------- /v1/proxy_server_with_cache.c: -------------------------------------------------------------------------------- 1 | #include "proxy_parse.h" 2 | #include //input output 3 | #include 4 | #include //basic string 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include //time based lru cache 13 | #include 14 | #include 15 | #include //threads 16 | #include 17 | #include 18 | 19 | #define MAX_BYTES 4096 // max allowed size of request/response 20 | #define MAX_CLIENTS 400 // max number of client requests served at a time 21 | #define MAX_SIZE 200 * (1 << 20) // size of the cache 22 | #define MAX_ELEMENT_SIZE 10 * (1 << 20) // max size of an element in cache 23 | 24 | typedef struct cache_element cache_element; 25 | 26 | struct cache_element 27 | { 28 | char *data; // data stores response 29 | int len; // length of data i.e.. sizeof(data)... 30 | char *url; // url stores the request 31 | time_t lru_time_track; // lru_time_track stores the latest time the element is accesed 32 | cache_element *next; // pointer to next element 33 | }; 34 | 35 | cache_element *find(char *url); 36 | int add_cache_element(char *data, int size, char *url); 37 | void remove_cache_element(); 38 | 39 | int port_number = 8080; // Default Port 40 | int proxy_socketId; // socket descriptor of proxy server 41 | pthread_t tid[MAX_CLIENTS]; // array to store the thread ids of clients 42 | sem_t seamaphore; // if client requests exceeds the max_clients this seamaphore puts the 43 | // waiting threads to sleep and wakes them when traffic on queue decreases 44 | // sem_t cache_lock; 45 | pthread_mutex_t lock; // lock is used for locking the cache 46 | 47 | cache_element *head; // pointer to the cache 48 | int cache_size; // cache_size denotes the current size of the cache 49 | 50 | int sendErrorMessage(int socket, int status_code) 51 | { 52 | char str[1024]; 53 | char currentTime[50]; 54 | time_t now = time(0); 55 | 56 | struct tm data = *gmtime(&now); 57 | strftime(currentTime, sizeof(currentTime), "%a, %d %b %Y %H:%M:%S %Z", &data); 58 | 59 | switch (status_code) 60 | { 61 | case 400: 62 | snprintf(str, sizeof(str), "HTTP/1.1 400 Bad Request\r\nContent-Length: 95\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n400 Bad Request\n

400 Bad Rqeuest

\n", currentTime); 63 | printf("400 Bad Request\n"); 64 | send(socket, str, strlen(str), 0); 65 | break; 66 | 67 | case 403: 68 | snprintf(str, sizeof(str), "HTTP/1.1 403 Forbidden\r\nContent-Length: 112\r\nContent-Type: text/html\r\nConnection: keep-alive\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n403 Forbidden\n

403 Forbidden


Permission Denied\n", currentTime); 69 | printf("403 Forbidden\n"); 70 | send(socket, str, strlen(str), 0); 71 | break; 72 | 73 | case 404: 74 | snprintf(str, sizeof(str), "HTTP/1.1 404 Not Found\r\nContent-Length: 91\r\nContent-Type: text/html\r\nConnection: keep-alive\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n404 Not Found\n

404 Not Found

\n", currentTime); 75 | printf("404 Not Found\n"); 76 | send(socket, str, strlen(str), 0); 77 | break; 78 | 79 | case 500: 80 | snprintf(str, sizeof(str), "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 115\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n500 Internal Server Error\n

500 Internal Server Error

\n", currentTime); 81 | // printf("500 Internal Server Error\n"); 82 | send(socket, str, strlen(str), 0); 83 | break; 84 | 85 | case 501: 86 | snprintf(str, sizeof(str), "HTTP/1.1 501 Not Implemented\r\nContent-Length: 103\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n404 Not Implemented\n

501 Not Implemented

\n", currentTime); 87 | printf("501 Not Implemented\n"); 88 | send(socket, str, strlen(str), 0); 89 | break; 90 | 91 | case 505: 92 | snprintf(str, sizeof(str), "HTTP/1.1 505 HTTP Version Not Supported\r\nContent-Length: 125\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n505 HTTP Version Not Supported\n

505 HTTP Version Not Supported

\n", currentTime); 93 | printf("505 HTTP Version Not Supported\n"); 94 | send(socket, str, strlen(str), 0); 95 | break; 96 | 97 | default: 98 | return -1; 99 | } 100 | return 1; 101 | } 102 | 103 | int connectRemoteServer(char *host_addr, int port_num) 104 | { 105 | // Creating Socket for remote server --------------------------- 106 | 107 | int remoteSocket = socket(AF_INET, SOCK_STREAM, 0); 108 | 109 | if (remoteSocket < 0) 110 | { 111 | printf("Error in Creating Socket.\n"); 112 | return -1; 113 | } 114 | 115 | // Get host by the name or ip address provided 116 | 117 | struct hostent *host = gethostbyname(host_addr); 118 | if (host == NULL) 119 | { 120 | fprintf(stderr, "No such host exists.\n"); 121 | return -1; 122 | } 123 | 124 | // inserts ip address and port number of host in struct `server_addr` 125 | struct sockaddr_in server_addr; 126 | 127 | bzero((char *)&server_addr, sizeof(server_addr)); 128 | server_addr.sin_family = AF_INET; 129 | server_addr.sin_port = htons(port_num); 130 | 131 | bcopy((char *)host->h_addr, (char *)&server_addr.sin_addr.s_addr, host->h_length); 132 | 133 | // Connect to Remote server ---------------------------------------------------- 134 | 135 | if (connect(remoteSocket, (struct sockaddr *)&server_addr, (socklen_t)sizeof(server_addr)) < 0) 136 | { 137 | fprintf(stderr, "Error in connecting !\n"); 138 | return -1; 139 | } 140 | // free(host_addr); 141 | return remoteSocket; 142 | } 143 | 144 | int handle_request(int clientSocket, ParsedRequest *request, char *tempReq) 145 | { 146 | char *buf = (char *)malloc(sizeof(char) * MAX_BYTES); 147 | strcpy(buf, "GET "); 148 | strcat(buf, request->path); 149 | strcat(buf, " "); 150 | strcat(buf, request->version); 151 | strcat(buf, "\r\n"); 152 | 153 | size_t len = strlen(buf); 154 | 155 | if (ParsedHeader_set(request, "Connection", "close") < 0) 156 | { 157 | printf("set header key not work\n"); 158 | } 159 | 160 | if (ParsedHeader_get(request, "Host") == NULL) 161 | { 162 | if (ParsedHeader_set(request, "Host", request->host) < 0) 163 | { 164 | printf("Set \"Host\" header key not working\n"); 165 | } 166 | } 167 | 168 | if (ParsedRequest_unparse_headers(request, buf + len, (size_t)MAX_BYTES - len) < 0) 169 | { 170 | printf("unparse failed\n"); 171 | // return -1; // If this happens Still try to send request without header 172 | } 173 | 174 | int server_port = 80; // Default Remote Server Port 175 | if (request->port != NULL) 176 | server_port = atoi(request->port); 177 | 178 | int remoteSocketID = connectRemoteServer(request->host, server_port); 179 | 180 | if (remoteSocketID < 0) 181 | return -1; 182 | 183 | int bytes_send = send(remoteSocketID, buf, strlen(buf), 0); 184 | 185 | bzero(buf, MAX_BYTES); 186 | 187 | bytes_send = recv(remoteSocketID, buf, MAX_BYTES - 1, 0); 188 | char *temp_buffer = (char *)malloc(sizeof(char) * MAX_BYTES); // temp buffer 189 | int temp_buffer_size = MAX_BYTES; 190 | int temp_buffer_index = 0; 191 | 192 | while (bytes_send > 0) 193 | { 194 | bytes_send = send(clientSocket, buf, bytes_send, 0); 195 | 196 | for (int i = 0; i < bytes_send / sizeof(char); i++) 197 | { 198 | temp_buffer[temp_buffer_index] = buf[i]; 199 | // printf("%c",buf[i]); // Response Printing 200 | temp_buffer_index++; 201 | } 202 | temp_buffer_size += MAX_BYTES; 203 | temp_buffer = (char *)realloc(temp_buffer, temp_buffer_size); 204 | 205 | if (bytes_send < 0) 206 | { 207 | perror("Error in sending data to client socket.\n"); 208 | break; 209 | } 210 | bzero(buf, MAX_BYTES); 211 | 212 | bytes_send = recv(remoteSocketID, buf, MAX_BYTES - 1, 0); 213 | } 214 | temp_buffer[temp_buffer_index] = '\0'; 215 | free(buf); 216 | add_cache_element(temp_buffer, strlen(temp_buffer), tempReq); 217 | printf("Done\n"); 218 | free(temp_buffer); 219 | 220 | close(remoteSocketID); 221 | return 0; 222 | } 223 | 224 | int checkHTTPversion(char *msg) 225 | { 226 | int version = -1; 227 | 228 | if (strncmp(msg, "HTTP/1.1", 8) == 0) 229 | { 230 | version = 1; 231 | } 232 | else if (strncmp(msg, "HTTP/1.0", 8) == 0) 233 | { 234 | version = 1; // Handling this similar to version 1.1 235 | } 236 | else 237 | version = -1; 238 | 239 | return version; 240 | } 241 | 242 | void *thread_fn(void *socketNew) 243 | { 244 | sem_wait(&seamaphore); 245 | int p; 246 | sem_getvalue(&seamaphore, &p); 247 | printf("semaphore value:%d\n", p); 248 | int *t = (int *)(socketNew); 249 | int socket = *t; // Socket is socket descriptor of the connected Client 250 | int bytes_send_client, len; // Bytes Transferred 251 | 252 | char *buffer = (char *)calloc(MAX_BYTES, sizeof(char)); // Creating buffer of 4kb for a client 253 | 254 | bzero(buffer, MAX_BYTES); // Making buffer zero 255 | bytes_send_client = recv(socket, buffer, MAX_BYTES, 0); // Receiving the Request of client by proxy server 256 | 257 | while (bytes_send_client > 0) 258 | { 259 | len = strlen(buffer); 260 | // loop until u find "\r\n\r\n" in the buffer 261 | if (strstr(buffer, "\r\n\r\n") == NULL) 262 | { 263 | bytes_send_client = recv(socket, buffer + len, MAX_BYTES - len, 0); 264 | } 265 | else 266 | { 267 | break; 268 | } 269 | } 270 | 271 | // printf("--------------------------------------------\n"); 272 | // printf("%s\n",buffer); 273 | // printf("----------------------%d----------------------\n",strlen(buffer)); 274 | 275 | char *tempReq = (char *)malloc(strlen(buffer) * sizeof(char) + 1); 276 | // tempReq, buffer both store the http request sent by client 277 | for (int i = 0; i < strlen(buffer); i++) 278 | { 279 | tempReq[i] = buffer[i]; 280 | } 281 | 282 | // checking for the request in cache 283 | struct cache_element *temp = find(tempReq); 284 | 285 | if (temp != NULL) 286 | { 287 | // request found in cache, so sending the response to client from proxy's cache 288 | int size = temp->len / sizeof(char); 289 | int pos = 0; 290 | char response[MAX_BYTES]; 291 | while (pos < size) 292 | { 293 | bzero(response, MAX_BYTES); 294 | for (int i = 0; i < MAX_BYTES; i++) 295 | { 296 | response[i] = temp->data[pos]; 297 | pos++; 298 | } 299 | send(socket, response, MAX_BYTES, 0); 300 | } 301 | printf("Data retrived from the Cache\n\n"); 302 | printf("%s\n\n", response); 303 | // close(socketNew); 304 | // sem_post(&seamaphore); 305 | // return NULL; 306 | } 307 | 308 | else if (bytes_send_client > 0) 309 | { 310 | len = strlen(buffer); 311 | // Parsing the request 312 | ParsedRequest *request = ParsedRequest_create(); 313 | 314 | // ParsedRequest_parse returns 0 on success and -1 on failure.On success it stores parsed request in 315 | // the request 316 | if (ParsedRequest_parse(request, buffer, len) < 0) 317 | { 318 | printf("Parsing failed\n"); 319 | } 320 | else 321 | { 322 | bzero(buffer, MAX_BYTES); 323 | if (!strcmp(request->method, "GET")) 324 | { 325 | 326 | if (request->host && request->path && (checkHTTPversion(request->version) == 1)) 327 | { 328 | bytes_send_client = handle_request(socket, request, tempReq); // Handle GET request 329 | if (bytes_send_client == -1) 330 | { 331 | sendErrorMessage(socket, 500); 332 | } 333 | } 334 | else 335 | sendErrorMessage(socket, 500); // 500 Internal Error 336 | } 337 | else 338 | { 339 | printf("This code doesn't support any method other than GET\n"); 340 | } 341 | } 342 | // freeing up the request pointer 343 | ParsedRequest_destroy(request); 344 | } 345 | 346 | else if (bytes_send_client < 0) 347 | { 348 | perror("Error in receiving from client.\n"); 349 | } 350 | else if (bytes_send_client == 0) 351 | { 352 | printf("Client disconnected!\n"); 353 | } 354 | 355 | shutdown(socket, SHUT_RDWR); 356 | close(socket); 357 | free(buffer); 358 | sem_post(&seamaphore); 359 | 360 | sem_getvalue(&seamaphore, &p); 361 | printf("Semaphore post value:%d\n", p); 362 | free(tempReq); 363 | return NULL; 364 | } 365 | 366 | int main(int argc, char *argv[]) 367 | { 368 | 369 | int client_socketId, client_len; // client_socketId == to store the client socket id 370 | struct sockaddr_in server_addr, client_addr; // Address of client and server to be assigned 371 | 372 | sem_init(&seamaphore, 0, MAX_CLIENTS); // Initializing seamaphore and lock 373 | pthread_mutex_init(&lock, NULL); // Initializing lock for cache 374 | 375 | if (argc == 2) // checking whether two arguments are received or not 376 | { 377 | port_number = atoi(argv[1]); 378 | } 379 | else 380 | { 381 | printf("Too few arguments\n"); 382 | exit(1); // system calls 383 | } 384 | 385 | printf("Setting Proxy Server Port : %d\n", port_number); 386 | 387 | // creating the proxy socket 388 | proxy_socketId = socket(AF_INET, SOCK_STREAM, 0); // SOCK_Stream implies TCP 389 | 390 | if (proxy_socketId < 0) 391 | { 392 | perror("Failed to create socket.\n"); 393 | exit(1); 394 | } 395 | 396 | int reuse = 1; 397 | if (setsockopt(proxy_socketId, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse, sizeof(reuse)) < 0) 398 | perror("setsockopt(SO_REUSEADDR) failed\n"); 399 | 400 | bzero((char *)&server_addr, sizeof(server_addr)); // removes the garbage values from the variables passed to it, needs size of to know till which memory location it needs to do that 401 | server_addr.sin_family = AF_INET; // ipv4 402 | server_addr.sin_port = htons(port_number); // Assigning port to the Proxy, and making bigendian and littleendian changes in numbers 403 | server_addr.sin_addr.s_addr = INADDR_ANY; // Any available adress assigned 404 | 405 | // Binding the socket 406 | if (bind(proxy_socketId, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) 407 | { 408 | perror("Port is not free\n"); 409 | exit(1); 410 | } 411 | printf("Binding on port: %d\n", port_number); 412 | 413 | // Proxy socket listening to the requests 414 | int listen_status = listen(proxy_socketId, MAX_CLIENTS); 415 | 416 | if (listen_status < 0) 417 | { 418 | perror("Error while Listening !\n"); 419 | exit(1); 420 | } 421 | 422 | int i = 0; // Iterator for thread_id (tid) and Accepted Client_Socket for each thread 423 | int Connected_socketId[MAX_CLIENTS]; // This array stores socket descriptors of connected clients 424 | 425 | // Infinite Loop for accepting connections 426 | while (1) 427 | { 428 | 429 | bzero((char *)&client_addr, sizeof(client_addr)); // Clears struct client_addr 430 | client_len = sizeof(client_addr); 431 | 432 | // Accepting the connections 433 | client_socketId = accept(proxy_socketId, (struct sockaddr *)&client_addr, (socklen_t *)&client_len); // Accepts connection 434 | if (client_socketId < 0) 435 | { 436 | fprintf(stderr, "Error in Accepting connection !\n"); 437 | exit(1); 438 | } 439 | else 440 | { 441 | Connected_socketId[i] = client_socketId; // Storing accepted client into array 442 | } 443 | 444 | // Getting IP address and port number of client 445 | struct sockaddr_in *client_pt = (struct sockaddr_in *)&client_addr; 446 | struct in_addr ip_addr = client_pt->sin_addr; 447 | char str[INET_ADDRSTRLEN]; // INET_ADDRSTRLEN: Default ip address size 448 | inet_ntop(AF_INET, &ip_addr, str, INET_ADDRSTRLEN); 449 | printf("Client is connected with port number: %d and ip address: %s \n", ntohs(client_addr.sin_port), str); 450 | // printf("Socket values of index %d in main function is %d\n",i, client_socketId); 451 | pthread_create(&tid[i], NULL, thread_fn, (void *)&Connected_socketId[i]); // Creating a thread for each client accepted 452 | i++; 453 | } 454 | close(proxy_socketId); // Close socket 455 | return 0; 456 | } 457 | 458 | cache_element *find(char *url) 459 | { 460 | 461 | // Checks for url in the cache if found returns pointer to the respective cache element or else returns NULL 462 | cache_element *site = NULL; 463 | // sem_wait(&cache_lock); 464 | int temp_lock_val = pthread_mutex_lock(&lock); 465 | printf("Remove Cache Lock Acquired %d\n", temp_lock_val); 466 | if (head != NULL) 467 | { 468 | site = head; 469 | while (site != NULL) 470 | { 471 | if (!strcmp(site->url, url)) 472 | { 473 | printf("LRU Time Track Before : %ld", site->lru_time_track); 474 | printf("\nurl found\n"); 475 | // Updating the time_track 476 | site->lru_time_track = time(NULL); 477 | printf("LRU Time Track After : %ld", site->lru_time_track); 478 | break; 479 | } 480 | site = site->next; 481 | } 482 | } 483 | else 484 | { 485 | printf("\nurl not found\n"); 486 | } 487 | // sem_post(&cache_lock); 488 | temp_lock_val = pthread_mutex_unlock(&lock); 489 | printf("Remove Cache Lock Unlocked %d\n", temp_lock_val); 490 | return site; 491 | } 492 | 493 | void remove_cache_element() 494 | { 495 | // If cache is not empty searches for the node which has the least lru_time_track and deletes it 496 | cache_element *p; // Cache_element Pointer (Prev. Pointer) 497 | cache_element *q; // Cache_element Pointer (Next Pointer) 498 | cache_element *temp; // Cache element to remove 499 | // sem_wait(&cache_lock); 500 | int temp_lock_val = pthread_mutex_lock(&lock); 501 | printf("Remove Cache Lock Acquired %d\n", temp_lock_val); 502 | if (head != NULL) 503 | { // Cache != empty 504 | for (q = head, p = head, temp = head; q->next != NULL; 505 | q = q->next) 506 | { // Iterate through entire cache and search for oldest time track 507 | if (((q->next)->lru_time_track) < (temp->lru_time_track)) 508 | { 509 | temp = q->next; 510 | p = q; 511 | } 512 | } 513 | if (temp == head) 514 | { 515 | head = head->next; /*Handle the base case*/ 516 | } 517 | else 518 | { 519 | p->next = temp->next; 520 | } 521 | cache_size = cache_size - (temp->len) - sizeof(cache_element) - 522 | strlen(temp->url) - 1; // updating the cache size 523 | free(temp->data); 524 | free(temp->url); // Free the removed element 525 | free(temp); 526 | } 527 | // sem_post(&cache_lock); 528 | temp_lock_val = pthread_mutex_unlock(&lock); 529 | printf("Remove Cache Lock Unlocked %d\n", temp_lock_val); 530 | } 531 | 532 | int add_cache_element(char *data, int size, char *url) 533 | { 534 | // Adds element to the cache 535 | // sem_wait(&cache_lock); 536 | int temp_lock_val = pthread_mutex_lock(&lock); 537 | printf("Add Cache Lock Acquired %d\n", temp_lock_val); 538 | int element_size = size + 1 + strlen(url) + sizeof(cache_element); // Size of the new element which will be added to the cache 539 | if (element_size > MAX_ELEMENT_SIZE) 540 | { 541 | // sem_post(&cache_lock); 542 | // If element size is greater than MAX_ELEMENT_SIZE we don't add the element to the cache 543 | temp_lock_val = pthread_mutex_unlock(&lock); 544 | printf("Add Cache Lock Unlocked %d\n", temp_lock_val); 545 | // free(data); 546 | // printf("--\n"); 547 | // free(url); 548 | return 0; 549 | } 550 | else 551 | { 552 | while (cache_size + element_size > MAX_SIZE) 553 | { 554 | // We keep removing elements from cache until we get enough space to add the element 555 | remove_cache_element(); 556 | } 557 | cache_element *element = (cache_element *)malloc(sizeof(cache_element)); // Allocating memory for the new cache element 558 | element->data = (char *)malloc(size + 1); // Allocating memory for the response to be stored in the cache element 559 | strcpy(element->data, data); 560 | element->url = (char *)malloc(1 + (strlen(url) * sizeof(char))); // Allocating memory for the request to be stored in the cache element (as a key) 561 | strcpy(element->url, url); 562 | element->lru_time_track = time(NULL); // Updating the time_track 563 | element->next = head; 564 | element->len = size; 565 | head = element; 566 | cache_size += element_size; 567 | temp_lock_val = pthread_mutex_unlock(&lock); 568 | printf("Add Cache Lock Unlocked %d\n", temp_lock_val); 569 | // sem_post(&cache_lock); 570 | // free(data); 571 | // printf("--\n"); 572 | // free(url); 573 | return 1; 574 | } 575 | return 0; 576 | } 577 | -------------------------------------------------------------------------------- /v1/proxy_server_with_threadpool_and_cache.c: -------------------------------------------------------------------------------- 1 | #include "proxy_parse.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define MAX_BYTES 4096 19 | #define MAX_CLIENTS 400 20 | #define MAX_SIZE 200 * (1 << 20) 21 | #define MAX_ELEMENT_SIZE 10 * (1 << 20) 22 | #define THREAD_POOL_SIZE 200 23 | 24 | typedef struct cache_element cache_element; 25 | 26 | struct cache_element 27 | { 28 | char *data; 29 | int len; 30 | char *url; 31 | time_t lru_time_track; 32 | cache_element *next; 33 | }; 34 | 35 | cache_element *find(char *url); 36 | int add_cache_element(char *data, int size, char *url); 37 | void remove_cache_element(); 38 | 39 | int port_number = 8080; 40 | int proxy_socketId; 41 | pthread_t thread_pool[THREAD_POOL_SIZE]; 42 | pthread_mutex_t lock; 43 | pthread_mutex_t queue_lock; 44 | pthread_cond_t queue_cond; 45 | cache_element *head; 46 | int cache_size; 47 | 48 | typedef struct client_queue 49 | { 50 | int client_socket; 51 | struct client_queue *next; 52 | } client_queue; 53 | 54 | client_queue *queue_head = NULL, *queue_tail = NULL; 55 | 56 | int checkHTTPversion(char *msg) 57 | { 58 | int version = -1; 59 | 60 | if (strncmp(msg, "HTTP/1.1", 8) == 0) 61 | { 62 | printf("HTTP/1.1"); 63 | version = 1; 64 | } 65 | else if (strncmp(msg, "HTTP/1.0", 8) == 0) 66 | { 67 | printf("HTTP/1.0"); 68 | version = 1; // Handling this similar to version 1.1 69 | } 70 | else 71 | version = -1; 72 | 73 | return version; 74 | } 75 | 76 | int sendErrorMessage(int socket, int status_code) 77 | { 78 | char str[1024]; 79 | char currentTime[50]; 80 | time_t now = time(0); 81 | 82 | struct tm data = *gmtime(&now); 83 | strftime(currentTime, sizeof(currentTime), "%a, %d %b %Y %H:%M:%S %Z", &data); 84 | 85 | switch (status_code) 86 | { 87 | case 400: 88 | snprintf(str, sizeof(str), "HTTP/1.1 400 Bad Request\r\nContent-Length: 95\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n400 Bad Request\n

400 Bad Rqeuest

\n", currentTime); 89 | printf("400 Bad Request\n"); 90 | send(socket, str, strlen(str), 0); 91 | break; 92 | 93 | case 403: 94 | snprintf(str, sizeof(str), "HTTP/1.1 403 Forbidden\r\nContent-Length: 112\r\nContent-Type: text/html\r\nConnection: keep-alive\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n403 Forbidden\n

403 Forbidden


Permission Denied\n", currentTime); 95 | printf("403 Forbidden\n"); 96 | send(socket, str, strlen(str), 0); 97 | break; 98 | 99 | case 404: 100 | snprintf(str, sizeof(str), "HTTP/1.1 404 Not Found\r\nContent-Length: 91\r\nContent-Type: text/html\r\nConnection: keep-alive\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n404 Not Found\n

404 Not Found

\n", currentTime); 101 | printf("404 Not Found\n"); 102 | send(socket, str, strlen(str), 0); 103 | break; 104 | 105 | case 500: 106 | snprintf(str, sizeof(str), "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 115\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n500 Internal Server Error\n

500 Internal Server Error

\n", currentTime); 107 | // printf("500 Internal Server Error\n"); 108 | send(socket, str, strlen(str), 0); 109 | break; 110 | 111 | case 501: 112 | snprintf(str, sizeof(str), "HTTP/1.1 501 Not Implemented\r\nContent-Length: 103\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n404 Not Implemented\n

501 Not Implemented

\n", currentTime); 113 | printf("501 Not Implemented\n"); 114 | send(socket, str, strlen(str), 0); 115 | break; 116 | 117 | case 505: 118 | snprintf(str, sizeof(str), "HTTP/1.1 505 HTTP Version Not Supported\r\nContent-Length: 125\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n505 HTTP Version Not Supported\n

505 HTTP Version Not Supported

\n", currentTime); 119 | printf("505 HTTP Version Not Supported\n"); 120 | send(socket, str, strlen(str), 0); 121 | break; 122 | 123 | default: 124 | return -1; 125 | } 126 | return 1; 127 | } 128 | int connectRemoteServer(char *host_addr, int port_num) 129 | { 130 | // Creating Socket for remote server --------------------------- 131 | 132 | int remoteSocket = socket(AF_INET, SOCK_STREAM, 0); 133 | 134 | if (remoteSocket < 0) 135 | { 136 | printf("Error in Creating Socket.\n"); 137 | return -1; 138 | } 139 | 140 | // Get host by the name or ip address provided 141 | 142 | struct hostent *host = gethostbyname(host_addr); 143 | if (host == NULL) 144 | { 145 | fprintf(stderr, "No such host exists.\n"); 146 | return -1; 147 | } 148 | 149 | // inserts ip address and port number of host in struct `server_addr` 150 | struct sockaddr_in server_addr; 151 | 152 | bzero((char *)&server_addr, sizeof(server_addr)); 153 | server_addr.sin_family = AF_INET; 154 | server_addr.sin_port = htons(port_num); 155 | 156 | bcopy((char *)host->h_addr, (char *)&server_addr.sin_addr.s_addr, host->h_length); 157 | 158 | // Connect to Remote server ---------------------------------------------------- 159 | 160 | if (connect(remoteSocket, (struct sockaddr *)&server_addr, (socklen_t)sizeof(server_addr)) < 0) 161 | { 162 | fprintf(stderr, "Error in connecting !\n"); 163 | return -1; 164 | } 165 | // free(host_addr); 166 | return remoteSocket; 167 | } 168 | 169 | int handle_request(int clientSocket, ParsedRequest *request, char *tempReq) 170 | { 171 | char *buf = (char *)malloc(sizeof(char) * MAX_BYTES); 172 | strcpy(buf, "GET "); 173 | strcat(buf, request->path); 174 | strcat(buf, " "); 175 | strcat(buf, request->version); 176 | strcat(buf, "\r\n"); 177 | 178 | size_t len = strlen(buf); 179 | 180 | if (ParsedHeader_set(request, "Connection", "close") < 0) 181 | { 182 | printf("set header key not work\n"); 183 | } 184 | 185 | if (ParsedHeader_get(request, "Host") == NULL) 186 | { 187 | if (ParsedHeader_set(request, "Host", request->host) < 0) 188 | { 189 | printf("Set \"Host\" header key not working\n"); 190 | } 191 | } 192 | 193 | if (ParsedRequest_unparse_headers(request, buf + len, (size_t)MAX_BYTES - len) < 0) 194 | { 195 | printf("unparse failed\n"); 196 | // return -1; // If this happens Still try to send request without header 197 | } 198 | 199 | int server_port = 80; // Default Remote Server Port 200 | if (request->port != NULL) 201 | server_port = atoi(request->port); 202 | 203 | int remoteSocketID = connectRemoteServer(request->host, server_port); 204 | 205 | if (remoteSocketID < 0) 206 | return -1; 207 | 208 | int bytes_send = send(remoteSocketID, buf, strlen(buf), 0); 209 | 210 | bzero(buf, MAX_BYTES); 211 | 212 | bytes_send = recv(remoteSocketID, buf, MAX_BYTES - 1, 0); 213 | char *temp_buffer = (char *)malloc(sizeof(char) * MAX_BYTES); // temp buffer 214 | int temp_buffer_size = MAX_BYTES; 215 | int temp_buffer_index = 0; 216 | 217 | while (bytes_send > 0) 218 | { 219 | bytes_send = send(clientSocket, buf, bytes_send, 0); 220 | 221 | for (int i = 0; i < bytes_send / sizeof(char); i++) 222 | { 223 | temp_buffer[temp_buffer_index] = buf[i]; 224 | // printf("%c",buf[i]); // Response Printing 225 | temp_buffer_index++; 226 | } 227 | temp_buffer_size += MAX_BYTES; 228 | temp_buffer = (char *)realloc(temp_buffer, temp_buffer_size); 229 | 230 | if (bytes_send < 0) 231 | { 232 | perror("Error in sending data to client socket.\n"); 233 | break; 234 | } 235 | bzero(buf, MAX_BYTES); 236 | 237 | bytes_send = recv(remoteSocketID, buf, MAX_BYTES - 1, 0); 238 | } 239 | temp_buffer[temp_buffer_index] = '\0'; 240 | free(buf); 241 | add_cache_element(temp_buffer, strlen(temp_buffer), tempReq); 242 | printf("Done\n"); 243 | free(temp_buffer); 244 | 245 | close(remoteSocketID); 246 | return 0; 247 | } 248 | 249 | void enqueue(int client_socket) 250 | { 251 | client_queue *new_node = (client_queue *)malloc(sizeof(client_queue)); 252 | new_node->client_socket = client_socket; 253 | new_node->next = NULL; 254 | pthread_mutex_lock(&queue_lock); 255 | if (queue_tail == NULL) 256 | { 257 | queue_head = new_node; 258 | queue_tail = new_node; 259 | } 260 | else 261 | { 262 | queue_tail->next = new_node; 263 | queue_tail = new_node; 264 | } 265 | pthread_cond_signal(&queue_cond); 266 | pthread_mutex_unlock(&queue_lock); 267 | } 268 | 269 | int dequeue() 270 | { 271 | pthread_mutex_lock(&queue_lock); 272 | while (queue_head == NULL) 273 | { 274 | pthread_cond_wait(&queue_cond, &queue_lock); 275 | } 276 | int client_socket = queue_head->client_socket; 277 | client_queue *temp = queue_head; 278 | queue_head = queue_head->next; 279 | if (queue_head == NULL) 280 | { 281 | queue_tail = NULL; 282 | } 283 | free(temp); 284 | pthread_mutex_unlock(&queue_lock); 285 | return client_socket; 286 | } 287 | 288 | void handle_client(int client_socket) 289 | { 290 | int bytes_send_client, len; 291 | char *buffer = (char *)calloc(MAX_BYTES, sizeof(char)); 292 | bzero(buffer, MAX_BYTES); 293 | bytes_send_client = recv(client_socket, buffer, MAX_BYTES, 0); 294 | 295 | while (bytes_send_client > 0) 296 | { 297 | len = strlen(buffer); 298 | if (strstr(buffer, "\r\n\r\n") == NULL) 299 | { 300 | bytes_send_client = recv(client_socket, buffer + len, MAX_BYTES - len, 0); 301 | } 302 | else 303 | { 304 | break; 305 | } 306 | } 307 | 308 | char *tempReq = (char *)malloc(strlen(buffer) * sizeof(char) + 1); 309 | strcpy(tempReq, buffer); 310 | struct cache_element *temp = find(tempReq); 311 | 312 | if (temp != NULL) 313 | { 314 | int size = temp->len / sizeof(char); 315 | int pos = 0; 316 | char response[MAX_BYTES]; 317 | while (pos < size) 318 | { 319 | bzero(response, MAX_BYTES); 320 | for (int i = 0; i < MAX_BYTES; i++) 321 | { 322 | response[i] = temp->data[pos]; 323 | pos++; 324 | } 325 | send(client_socket, response, MAX_BYTES, 0); 326 | } 327 | printf("Data retrieved from the Cache\n\n"); 328 | } 329 | else if (bytes_send_client > 0) 330 | { 331 | len = strlen(buffer); 332 | ParsedRequest *request = ParsedRequest_create(); 333 | 334 | if (ParsedRequest_parse(request, buffer, len) < 0) 335 | { 336 | printf("Parsing failed\n"); 337 | } 338 | else 339 | { 340 | printf("Parsed Request Host: %s\n", request->host); 341 | printf("Parsed Request Path: %s\n", request->path); 342 | printf("Parsed Request Version: %s\n", request->version); 343 | 344 | bzero(buffer, MAX_BYTES); 345 | if (!strcmp(request->method, "GET")) 346 | { 347 | if (request->host && request->path && (checkHTTPversion(request->version) == 1)) 348 | { 349 | bytes_send_client = handle_request(client_socket, request, tempReq); 350 | if (bytes_send_client == -1) 351 | { 352 | sendErrorMessage(client_socket, 500); 353 | } 354 | } 355 | else 356 | { 357 | sendErrorMessage(client_socket, 500); 358 | } 359 | } 360 | else 361 | { 362 | printf("This code doesn't support any method other than GET\n"); 363 | } 364 | } 365 | ParsedRequest_destroy(request); 366 | } 367 | else if (bytes_send_client < 0) 368 | { 369 | perror("Error in receiving from client.\n"); 370 | } 371 | else if (bytes_send_client == 0) 372 | { 373 | printf("Client disconnected!\n"); 374 | } 375 | 376 | shutdown(client_socket, SHUT_RDWR); 377 | close(client_socket); 378 | free(buffer); 379 | free(tempReq); 380 | } 381 | 382 | void *worker_thread(void *arg) 383 | { 384 | while (1) 385 | { 386 | int client_socket = dequeue(); 387 | handle_client(client_socket); 388 | } 389 | return NULL; 390 | } 391 | 392 | int main(int argc, char *argv[]) 393 | { 394 | int client_socketId, client_len; 395 | struct sockaddr_in server_addr, client_addr; 396 | 397 | pthread_mutex_init(&lock, NULL); 398 | pthread_mutex_init(&queue_lock, NULL); 399 | pthread_cond_init(&queue_cond, NULL); 400 | 401 | if (argc == 2) 402 | { 403 | port_number = atoi(argv[1]); 404 | } 405 | else 406 | { 407 | printf("Too few arguments\n"); 408 | exit(1); 409 | } 410 | 411 | printf("Setting Proxy Server Port : %d\n", port_number); 412 | 413 | proxy_socketId = socket(AF_INET, SOCK_STREAM, 0); 414 | if (proxy_socketId < 0) 415 | { 416 | perror("Failed to create socket.\n"); 417 | exit(1); 418 | } 419 | 420 | int reuse = 1; 421 | if (setsockopt(proxy_socketId, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse, sizeof(reuse)) < 0) 422 | perror("setsockopt(SO_REUSEADDR) failed\n"); 423 | 424 | bzero((char *)&server_addr, sizeof(server_addr)); 425 | server_addr.sin_family = AF_INET; 426 | server_addr.sin_port = htons(port_number); 427 | server_addr.sin_addr.s_addr = INADDR_ANY; 428 | 429 | if (bind(proxy_socketId, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) 430 | { 431 | perror("Port is not free\n"); 432 | exit(1); 433 | } 434 | printf("Binding on port: %d\n", port_number); 435 | 436 | int listen_status = listen(proxy_socketId, MAX_CLIENTS); 437 | if (listen_status < 0) 438 | { 439 | perror("Error while Listening !\n"); 440 | exit(1); 441 | } 442 | 443 | for (int i = 0; i < THREAD_POOL_SIZE; i++) 444 | { 445 | pthread_create(&thread_pool[i], NULL, worker_thread, NULL); 446 | } 447 | 448 | while (1) 449 | { 450 | bzero((char *)&client_addr, sizeof(client_addr)); 451 | client_len = sizeof(client_addr); 452 | client_socketId = accept(proxy_socketId, (struct sockaddr *)&client_addr, (socklen_t *)&client_len); 453 | if (client_socketId < 0) 454 | { 455 | fprintf(stderr, "Error in Accepting connection !\n"); 456 | exit(1); 457 | } 458 | 459 | struct sockaddr_in *client_pt = (struct sockaddr_in *)&client_addr; 460 | struct in_addr ip_addr = client_pt->sin_addr; 461 | char str[INET_ADDRSTRLEN]; 462 | inet_ntop(AF_INET, &ip_addr, str, INET_ADDRSTRLEN); 463 | printf("Client is connected with port number: %d and ip address: %s \n", ntohs(client_addr.sin_port), str); 464 | enqueue(client_socketId); 465 | } 466 | 467 | close(proxy_socketId); 468 | return 0; 469 | } 470 | 471 | cache_element *find(char *url) 472 | { 473 | 474 | // Checks for url in the cache if found returns pointer to the respective cache element or else returns NULL 475 | cache_element *site = NULL; 476 | // sem_wait(&cache_lock); 477 | int temp_lock_val = pthread_mutex_lock(&lock); 478 | printf("Remove Cache Lock Acquired %d\n", temp_lock_val); 479 | if (head != NULL) 480 | { 481 | site = head; 482 | while (site != NULL) 483 | { 484 | if (!strcmp(site->url, url)) 485 | { 486 | printf("LRU Time Track Before : %ld", site->lru_time_track); 487 | printf("\nurl found\n"); 488 | // Updating the time_track 489 | site->lru_time_track = time(NULL); 490 | printf("LRU Time Track After : %ld", site->lru_time_track); 491 | break; 492 | } 493 | site = site->next; 494 | } 495 | } 496 | else 497 | { 498 | printf("\nurl not found\n"); 499 | } 500 | // sem_post(&cache_lock); 501 | temp_lock_val = pthread_mutex_unlock(&lock); 502 | printf("Remove Cache Lock Unlocked %d\n", temp_lock_val); 503 | return site; 504 | } 505 | 506 | void remove_cache_element() 507 | { 508 | // If cache is not empty searches for the node which has the least lru_time_track and deletes it 509 | cache_element *p; // Cache_element Pointer (Prev. Pointer) 510 | cache_element *q; // Cache_element Pointer (Next Pointer) 511 | cache_element *temp; // Cache element to remove 512 | // sem_wait(&cache_lock); 513 | int temp_lock_val = pthread_mutex_lock(&lock); 514 | printf("Remove Cache Lock Acquired %d\n", temp_lock_val); 515 | if (head != NULL) 516 | { // Cache != empty 517 | for (q = head, p = head, temp = head; q->next != NULL; 518 | q = q->next) 519 | { // Iterate through entire cache and search for oldest time track 520 | if (((q->next)->lru_time_track) < (temp->lru_time_track)) 521 | { 522 | temp = q->next; 523 | p = q; 524 | } 525 | } 526 | if (temp == head) 527 | { 528 | head = head->next; /*Handle the base case*/ 529 | } 530 | else 531 | { 532 | p->next = temp->next; 533 | } 534 | cache_size = cache_size - (temp->len) - sizeof(cache_element) - strlen(temp->url) - 1; // updating the cache size 535 | free(temp->data); 536 | free(temp->url); // Free the removed element 537 | free(temp); 538 | } 539 | // sem_post(&cache_lock); 540 | temp_lock_val = pthread_mutex_unlock(&lock); 541 | printf("Remove Cache Lock Unlocked %d\n", temp_lock_val); 542 | } 543 | 544 | int add_cache_element(char *data, int size, char *url) 545 | { 546 | // Adds element to the cache 547 | // sem_wait(&cache_lock); 548 | int temp_lock_val = pthread_mutex_lock(&lock); 549 | printf("Add Cache Lock Acquired %d\n", temp_lock_val); 550 | int element_size = size + 1 + strlen(url) + sizeof(cache_element); // Size of the new element which will be added to the cache 551 | if (element_size > MAX_ELEMENT_SIZE) 552 | { 553 | // sem_post(&cache_lock); 554 | // If element size is greater than MAX_ELEMENT_SIZE we don't add the element to the cache 555 | temp_lock_val = pthread_mutex_unlock(&lock); 556 | printf("Add Cache Lock Unlocked %d\n", temp_lock_val); 557 | // free(data); 558 | // printf("--\n"); 559 | // free(url); 560 | return 0; 561 | } 562 | else 563 | { 564 | while (cache_size + element_size > MAX_SIZE) 565 | { 566 | // We keep removing elements from cache until we get enough space to add the element 567 | remove_cache_element(); 568 | } 569 | cache_element *element = (cache_element *)malloc(sizeof(cache_element)); // Allocating memory for the new cache element 570 | element->data = (char *)malloc(size + 1); // Allocating memory for the response to be stored in the cache element 571 | strcpy(element->data, data); 572 | element->url = (char *)malloc(1 + (strlen(url) * sizeof(char))); // Allocating memory for the request to be stored in the cache element (as a key) 573 | strcpy(element->url, url); 574 | element->lru_time_track = time(NULL); // Updating the time_track 575 | element->next = head; 576 | element->len = size; 577 | head = element; 578 | cache_size += element_size; 579 | temp_lock_val = pthread_mutex_unlock(&lock); 580 | printf("Add Cache Lock Unlocked %d\n", temp_lock_val); 581 | // sem_post(&cache_lock); 582 | // free(data); 583 | // printf("--\n"); 584 | // free(url); 585 | return 1; 586 | } 587 | return 0; 588 | } 589 | -------------------------------------------------------------------------------- /v1/proxy_server_without_cache.c: -------------------------------------------------------------------------------- 1 | #include "proxy_parse.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #define MAX_BYTES 4096 // max allowed size of request/response 21 | 22 | #define MAX_CLIENTS 400 // max number of client requests served at a time 23 | 24 | #define MAX_SIZE 200 * (1 << 20) // size of the cache 25 | 26 | #define MAX_ELEMENT_SIZE 10 * (1 << 20) // max size of an element in cache 27 | 28 | typedef struct cache_element cache_element; 29 | 30 | struct cache_element 31 | { 32 | 33 | char *data; // data stores response 34 | 35 | int len; // length of data i.e.. sizeof(data)... 36 | 37 | char *url; // url stores the request 38 | 39 | time_t lru_time_track; // lru_time_track stores the latest time the element is accesed 40 | 41 | cache_element *next; // pointer to next element 42 | }; 43 | 44 | pthread_mutex_t lock; // lock is used for locking the cache 45 | 46 | cache_element *find(char *url); 47 | 48 | int add_cache_element(char *data, int size, char *url); 49 | 50 | void remove_cache_element(); 51 | 52 | int port_number = 8080; // Default Port 53 | 54 | int proxy_socketId; // socket descriptor of proxy server 55 | 56 | pthread_t tid[MAX_CLIENTS]; // array to store the thread ids of clients 57 | 58 | sem_t seamaphore; // if client requests exceeds the max_clients this seamaphore puts the 59 | 60 | // waiting threads to sleep and wakes them when traffic on queue decreases 61 | 62 | // sem_t cache_lock; 63 | 64 | cache_element *head; // pointer to the cache 65 | 66 | int cache_size = 0; // cache_size denotes the current size of the cache 67 | 68 | int sendErrorMessage(int socket, int status_code) 69 | 70 | { 71 | 72 | char str[1024]; 73 | 74 | char currentTime[50]; 75 | 76 | time_t now = time(0); 77 | 78 | struct tm data = *gmtime(&now); 79 | 80 | strftime(currentTime, sizeof(currentTime), "%a, %d %b %Y %H:%M:%S %Z", &data); 81 | 82 | switch (status_code) 83 | 84 | { 85 | 86 | case 400: 87 | snprintf(str, sizeof(str), "HTTP/1.1 400 Bad Request\r\nContent-Length: 95\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n400 Bad Request\n

400 Bad Rqeuest

\n", currentTime); 88 | 89 | printf("400 Bad Request\n"); 90 | 91 | send(socket, str, strlen(str), 0); 92 | 93 | break; 94 | 95 | case 403: 96 | snprintf(str, sizeof(str), "HTTP/1.1 403 Forbidden\r\nContent-Length: 112\r\nContent-Type: text/html\r\nConnection: keep-alive\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n403 Forbidden\n

403 Forbidden


Permission Denied\n", currentTime); 97 | 98 | printf("403 Forbidden\n"); 99 | 100 | send(socket, str, strlen(str), 0); 101 | 102 | break; 103 | 104 | case 404: 105 | snprintf(str, sizeof(str), "HTTP/1.1 404 Not Found\r\nContent-Length: 91\r\nContent-Type: text/html\r\nConnection: keep-alive\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n404 Not Found\n

404 Not Found

\n", currentTime); 106 | 107 | printf("404 Not Found\n"); 108 | 109 | send(socket, str, strlen(str), 0); 110 | 111 | break; 112 | 113 | case 500: 114 | snprintf(str, sizeof(str), "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 115\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n500 Internal Server Error\n

500 Internal Server Error

\n", currentTime); 115 | 116 | // printf("500 Internal Server Error\n"); 117 | 118 | send(socket, str, strlen(str), 0); 119 | 120 | break; 121 | 122 | case 501: 123 | snprintf(str, sizeof(str), "HTTP/1.1 501 Not Implemented\r\nContent-Length: 103\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n404 Not Implemented\n

501 Not Implemented

\n", currentTime); 124 | 125 | printf("501 Not Implemented\n"); 126 | 127 | send(socket, str, strlen(str), 0); 128 | 129 | break; 130 | 131 | case 505: 132 | snprintf(str, sizeof(str), "HTTP/1.1 505 HTTP Version Not Supported\r\nContent-Length: 125\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: %s\r\nServer: VaibhavN/14785\r\n\r\n505 HTTP Version Not Supported\n

505 HTTP Version Not Supported

\n", currentTime); 133 | 134 | printf("505 HTTP Version Not Supported\n"); 135 | 136 | send(socket, str, strlen(str), 0); 137 | 138 | break; 139 | 140 | default: 141 | return -1; 142 | } 143 | 144 | return 1; 145 | } 146 | 147 | int connectRemoteServer(char *host_addr, int port_num) 148 | 149 | { 150 | 151 | // Creating Socket for remote server --------------------------- 152 | 153 | int remoteSocket = socket(AF_INET, SOCK_STREAM, 0); 154 | 155 | if (remoteSocket < 0) 156 | 157 | { 158 | 159 | printf("Error in Creating Socket.\n"); 160 | 161 | return -1; 162 | } 163 | 164 | // Get host by the name or ip address provided 165 | 166 | struct hostent *host = gethostbyname(host_addr); 167 | 168 | if (host == NULL) 169 | 170 | { 171 | 172 | fprintf(stderr, "No such host exists.\n"); 173 | 174 | return -1; 175 | } 176 | 177 | // inserts ip address and port number of host in struct `server_addr` 178 | 179 | struct sockaddr_in server_addr; 180 | 181 | bzero((char *)&server_addr, sizeof(server_addr)); 182 | 183 | server_addr.sin_family = AF_INET; 184 | 185 | server_addr.sin_port = htons(port_num); 186 | 187 | bcopy((char *)host->h_addr, (char *)&server_addr.sin_addr.s_addr, host->h_length); 188 | 189 | // Connect to Remote server ---------------------------------------------------- 190 | 191 | if (connect(remoteSocket, (struct sockaddr *)&server_addr, (socklen_t)sizeof(server_addr)) < 0) 192 | 193 | { 194 | 195 | fprintf(stderr, "Error in connecting !\n"); 196 | 197 | return -1; 198 | } 199 | 200 | return remoteSocket; 201 | } 202 | 203 | int handle_request(int clientSocket, struct ParsedRequest *request, char *buf, char *tempReq) 204 | 205 | { 206 | 207 | strcpy(buf, "GET "); 208 | 209 | strcat(buf, request->path); 210 | 211 | strcat(buf, " "); 212 | 213 | strcat(buf, request->version); 214 | 215 | strcat(buf, "\r\n"); 216 | 217 | size_t len = strlen(buf); 218 | 219 | if (ParsedHeader_set(request, "Connection", "close") < 0) 220 | { 221 | 222 | printf("set header key not work\n"); 223 | } 224 | 225 | if (ParsedHeader_get(request, "Host") == NULL) 226 | 227 | { 228 | 229 | if (ParsedHeader_set(request, "Host", request->host) < 0) 230 | { 231 | 232 | printf("Set \"Host\" header key not working\n"); 233 | } 234 | } 235 | 236 | if (ParsedRequest_unparse_headers(request, buf + len, (size_t)MAX_BYTES - len) < 0) 237 | { 238 | 239 | printf("unparse failed\n"); 240 | 241 | // return -1; // If this happens Still try to send request without header 242 | } 243 | 244 | int server_port = 80; // Default Remote Server Port 245 | 246 | if (request->port != NULL) 247 | 248 | server_port = atoi(request->port); 249 | 250 | int remoteSocketID = connectRemoteServer(request->host, server_port); 251 | 252 | if (remoteSocketID < 0) 253 | 254 | return -1; 255 | 256 | int bytes_send = send(remoteSocketID, buf, strlen(buf), 0); 257 | 258 | bzero(buf, MAX_BYTES); 259 | 260 | bytes_send = recv(remoteSocketID, buf, MAX_BYTES - 1, 0); 261 | 262 | char *temp_buffer = (char *)malloc(sizeof(char) * MAX_BYTES); // temp buffer 263 | 264 | int temp_buffer_size = MAX_BYTES; 265 | 266 | int temp_buffer_index = 0; 267 | 268 | while (bytes_send > 0) 269 | 270 | { 271 | 272 | bytes_send = send(clientSocket, buf, bytes_send, 0); 273 | 274 | for (int i = 0; i < bytes_send / sizeof(char); i++) 275 | { 276 | 277 | temp_buffer[temp_buffer_index] = buf[i]; 278 | 279 | // printf("%c",buf[i]); // Response Printing 280 | 281 | temp_buffer_index++; 282 | } 283 | 284 | temp_buffer_size += MAX_BYTES; 285 | 286 | temp_buffer = (char *)realloc(temp_buffer, temp_buffer_size); 287 | 288 | if (bytes_send < 0) 289 | 290 | { 291 | 292 | perror("Error in sending data to client socket.\n"); 293 | 294 | break; 295 | } 296 | 297 | bzero(buf, MAX_BYTES); 298 | 299 | bytes_send = recv(remoteSocketID, buf, MAX_BYTES - 1, 0); 300 | } 301 | 302 | temp_buffer[temp_buffer_index] = '\0'; 303 | 304 | free(temp_buffer); 305 | 306 | free(tempReq); 307 | 308 | // add_cache_element(temp_buffer, sizeof(temp_buffer), tempReq); 309 | 310 | printf("Done\n"); 311 | 312 | // close(remoteSocketID); 313 | 314 | return 0; 315 | } 316 | 317 | int checkHTTPversion(char *msg) 318 | 319 | { 320 | 321 | int version = -1; 322 | 323 | if (strncmp(msg, "HTTP/1.1", 8) == 0) 324 | 325 | { 326 | 327 | version = 1; 328 | } 329 | 330 | else if (strncmp(msg, "HTTP/1.0", 8) == 0) 331 | 332 | { 333 | 334 | version = 1; // Handling this similar to version 1.1 335 | } 336 | 337 | else 338 | 339 | version = -1; 340 | 341 | return version; 342 | } 343 | 344 | void *thread_fn(void *socketNew) 345 | 346 | { 347 | 348 | sem_wait(&seamaphore); 349 | 350 | int p; 351 | 352 | sem_getvalue(&seamaphore, &p); 353 | 354 | printf("semaphore value:%d\n", p); 355 | 356 | int *t = (int *)(socketNew); 357 | 358 | int socket = *t; // Socket is socket descriptor of the connected Client 359 | 360 | int bytes_send_client, len; // Bytes Transferred 361 | 362 | char *buffer = (char *)calloc(MAX_BYTES, sizeof(char)); // Creating buffer of 4kb for a client 363 | 364 | bzero(buffer, MAX_BYTES); // Making buffer zero 365 | 366 | bytes_send_client = recv(socket, buffer, MAX_BYTES, 0); // Receiving the Request of client by proxy server 367 | 368 | while (bytes_send_client > 0) 369 | 370 | { 371 | 372 | len = strlen(buffer); 373 | 374 | // loop until u find "\r\n\r\n" in the buffer 375 | 376 | if (strstr(buffer, "\r\n\r\n") == NULL) 377 | 378 | { 379 | 380 | bytes_send_client = recv(socket, buffer + len, MAX_BYTES - len, 0); 381 | } 382 | 383 | else 384 | { 385 | 386 | break; 387 | } 388 | } 389 | 390 | char *tempReq = (char *)malloc(strlen(buffer) * sizeof(char) + 10); 391 | 392 | // tempReq, buffer both store the http request sent by client 393 | 394 | for (int i = 0; i < strlen(buffer); i++) 395 | 396 | { 397 | 398 | tempReq[i] = buffer[i]; 399 | } 400 | 401 | // checking for the request in cache 402 | 403 | struct cache_element *temp = find(tempReq); 404 | 405 | if (temp != NULL) 406 | { 407 | 408 | // request found in cache, so sending the response to client from proxy's cache 409 | 410 | int size = temp->len / sizeof(char); 411 | 412 | int pos = 0; 413 | 414 | char response[MAX_BYTES]; 415 | 416 | while (pos < size) 417 | { 418 | 419 | bzero(response, MAX_BYTES); 420 | 421 | for (int i = 0; i < MAX_BYTES; i++) 422 | { 423 | 424 | response[i] = temp->data[pos]; 425 | 426 | pos++; 427 | } 428 | 429 | send(socket, response, MAX_BYTES, 0); 430 | } 431 | 432 | printf("Data retrived from the Cache\n\n"); 433 | 434 | printf("%s\n\n", response); 435 | 436 | // return NULL; 437 | } 438 | 439 | else if (bytes_send_client > 0) 440 | 441 | { 442 | 443 | len = strlen(buffer); 444 | 445 | // Parsing the request 446 | 447 | struct ParsedRequest *request = ParsedRequest_create(); 448 | 449 | // ParsedRequest_parse returns 0 on success and -1 on failure.On success it stores parsed request in 450 | 451 | // the request 452 | 453 | if (ParsedRequest_parse(request, buffer, len) < 0) 454 | 455 | { 456 | 457 | printf("Parsing failed\n"); 458 | } 459 | 460 | else 461 | 462 | { 463 | 464 | bzero(buffer, MAX_BYTES); 465 | 466 | if (!strcmp(request->method, "GET")) 467 | 468 | { 469 | 470 | if (request->host && request->path && (checkHTTPversion(request->version) == 1)) 471 | 472 | { 473 | 474 | bytes_send_client = handle_request(socket, request, buffer, tempReq); // Handle GET request 475 | 476 | if (bytes_send_client == -1) 477 | 478 | { 479 | 480 | sendErrorMessage(socket, 500); 481 | } 482 | } 483 | 484 | else 485 | 486 | sendErrorMessage(socket, 500); // 500 Internal Error 487 | } 488 | 489 | else 490 | 491 | { 492 | 493 | printf("This code doesn't support any method other than GET\n"); 494 | } 495 | } 496 | 497 | // freeing up the request pointer 498 | 499 | ParsedRequest_destroy(request); 500 | } 501 | 502 | else if (bytes_send_client < 0) 503 | 504 | { 505 | 506 | perror("Error in receiving from client.\n"); 507 | } 508 | 509 | else if (bytes_send_client == 0) 510 | 511 | { 512 | 513 | printf("Client disconnected!\n"); 514 | } 515 | 516 | shutdown(socket, SHUT_RDWR); 517 | 518 | close(socket); 519 | 520 | free(buffer); 521 | 522 | sem_post(&seamaphore); 523 | 524 | sem_getvalue(&seamaphore, &p); 525 | 526 | printf("Semaphore post value:%d\n", p); 527 | 528 | return NULL; 529 | } 530 | 531 | int main(int argc, char *argv[]) 532 | { 533 | 534 | // printf("\n%d\n",MAX_SIZE); 535 | 536 | // printf("\n%d\n",MAX_ELEMENT_SIZE); 537 | 538 | int client_socketId, client_len; // client_socketId == to store the client socket id 539 | 540 | struct sockaddr_in server_addr, client_addr; // Address of client and server to be assigned 541 | 542 | sem_init(&seamaphore, 0, MAX_CLIENTS); // seamaphore and lock 543 | 544 | pthread_mutex_init(&lock, NULL); // Initializing lock for cache 545 | 546 | if (argc == 2) // checking whether two arguments are received or not 547 | 548 | { 549 | 550 | port_number = atoi(argv[1]); 551 | } 552 | 553 | else 554 | 555 | { 556 | 557 | printf("Too few arguments\n"); 558 | 559 | exit(1); 560 | } 561 | 562 | printf("Setting Proxy Server Port : %d\n", port_number); 563 | 564 | // creating the proxy socket 565 | 566 | proxy_socketId = socket(AF_INET, SOCK_STREAM, 0); 567 | 568 | if (proxy_socketId < 0) 569 | 570 | { 571 | 572 | perror("Failed to create socket.\n"); 573 | 574 | exit(1); 575 | } 576 | 577 | int reuse = 1; 578 | 579 | if (setsockopt(proxy_socketId, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse, sizeof(reuse)) < 0) 580 | 581 | perror("setsockopt(SO_REUSEADDR) failed\n"); 582 | 583 | bzero((char *)&server_addr, sizeof(server_addr)); 584 | 585 | server_addr.sin_family = AF_INET; 586 | 587 | server_addr.sin_port = htons(port_number); // Assigning port to the Proxy 588 | 589 | server_addr.sin_addr.s_addr = INADDR_ANY; // Any available adress assigned 590 | 591 | // Binding the socket 592 | 593 | if (bind(proxy_socketId, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) 594 | 595 | { 596 | 597 | perror("Port is not free\n"); 598 | 599 | exit(1); 600 | } 601 | 602 | printf("Binding on port: %d\n", port_number); 603 | 604 | // Proxy socket listening to the requests 605 | 606 | int listen_status = listen(proxy_socketId, MAX_CLIENTS); 607 | 608 | if (listen_status < 0) 609 | 610 | { 611 | 612 | perror("Error while Listening !\n"); 613 | 614 | exit(1); 615 | } 616 | 617 | int i = 0; // Iterator for thread_id (tid) and Accepted Client_Socket for each thread 618 | 619 | int Connected_socketId[MAX_CLIENTS]; // This array stores socket descriptors of connected clients 620 | 621 | // Infinite Loop for accepting connections 622 | 623 | while (1) 624 | 625 | { 626 | 627 | bzero((char *)&client_addr, sizeof(client_addr)); // Clears struct client_addr 628 | 629 | client_len = sizeof(client_addr); 630 | 631 | // Accepting the connections 632 | 633 | client_socketId = accept(proxy_socketId, (struct sockaddr *)&client_addr, (socklen_t *)&client_len); // Accepts connection 634 | 635 | if (client_socketId < 0) 636 | 637 | { 638 | 639 | fprintf(stderr, "Error in Accepting connection !\n"); 640 | 641 | exit(1); 642 | } 643 | 644 | else 645 | { 646 | 647 | Connected_socketId[i] = client_socketId; // Storing accepted client into array 648 | } 649 | 650 | // Getting IP address and port number of client 651 | 652 | struct sockaddr_in *client_pt = (struct sockaddr_in *)&client_addr; 653 | 654 | struct in_addr ip_addr = client_pt->sin_addr; 655 | 656 | char str[INET_ADDRSTRLEN]; // INET_ADDRSTRLEN: Default ip address size 657 | 658 | inet_ntop(AF_INET, &ip_addr, str, INET_ADDRSTRLEN); 659 | 660 | // printf("Client is connected with port number: %d and ip address: %s \n",ntohs(client_addr.sin_port), str); 661 | 662 | // printf("Socket values of index %d in main function is %d\n",i, client_socketId); 663 | 664 | pthread_create(&tid[i], NULL, thread_fn, (void *)&Connected_socketId[i]); // Creating a thread for each client accepted 665 | 666 | i++; 667 | } 668 | 669 | close(proxy_socketId); // Close socket 670 | 671 | return 0; 672 | } 673 | 674 | cache_element *find(char *url) 675 | { 676 | 677 | // Checks for url in the cache if found returns pointer to the respective cache element or else returns NULL 678 | 679 | cache_element *site = NULL; 680 | 681 | int temp_lock_val = pthread_mutex_lock(&lock); 682 | 683 | printf("Find Cache Lock Acquired %d\n", temp_lock_val); 684 | 685 | if (head != NULL) 686 | { 687 | 688 | site = head; 689 | 690 | while (site != NULL) 691 | 692 | { 693 | 694 | if (!strcmp(site->url, url)) 695 | { 696 | 697 | printf("\nurl found\n"); 698 | 699 | // Updating the time_track 700 | 701 | site->lru_time_track = time(NULL); 702 | 703 | break; 704 | } 705 | 706 | site = site->next; 707 | } 708 | } 709 | 710 | else 711 | { 712 | 713 | printf("\nurl not found\n"); 714 | } 715 | 716 | // sem_post(&cache_lock); 717 | 718 | temp_lock_val = pthread_mutex_unlock(&lock); 719 | 720 | printf("Find Cache Lock Unlocked %d\n", temp_lock_val); 721 | 722 | return site; 723 | } 724 | 725 | void remove_cache_element() 726 | { 727 | 728 | // If cache is not empty searches for the node which has the least lru_time_track and deletes it 729 | 730 | cache_element *p; // Cache_element Pointer (Prev. Pointer) 731 | 732 | cache_element *q; // Cache_element Pointer (Next Pointer) 733 | 734 | cache_element *temp; // Cache element to remove 735 | 736 | // sem_wait(&cache_lock); 737 | 738 | int temp_lock_val = pthread_mutex_lock(&lock); 739 | 740 | printf("Remove Cache Lock Acquired %d\n", temp_lock_val); 741 | 742 | if (head != NULL) 743 | { // Cache != empty 744 | 745 | for (q = head, p = head, temp = head; q->next != NULL; 746 | 747 | q = q->next) 748 | { // Iterate through entire cache and search for oldest time track 749 | 750 | if (((q->next)->lru_time_track) < (temp->lru_time_track)) 751 | { 752 | 753 | temp = q->next; 754 | 755 | p = q; 756 | } 757 | } 758 | 759 | if (temp == head) 760 | { 761 | 762 | head = head->next; /*Handle the base case*/ 763 | } 764 | else 765 | { 766 | 767 | p->next = temp->next; 768 | } 769 | 770 | cache_size = cache_size - (temp->len) - sizeof(cache_element) - 771 | 772 | strlen(temp->url) - 1; // updating the cache size 773 | 774 | free(temp->data); 775 | 776 | free(temp->url); 777 | 778 | free(temp); // Free the removed element 779 | } 780 | 781 | // sem_post(&cache_lock); 782 | 783 | temp_lock_val = pthread_mutex_unlock(&lock); 784 | 785 | printf("Remove Cache Lock Unlocked %d\n", temp_lock_val); 786 | } 787 | 788 | int add_cache_element(char *data, int size, char *url) 789 | { 790 | 791 | // Adds element to the cache 792 | 793 | // sem_wait(&cache_lock); 794 | 795 | printf("\nurl:%s\n", url); 796 | 797 | printf("\ndata:%s\n", data); 798 | 799 | int ret = 0; 800 | 801 | int temp_lock_val = pthread_mutex_lock(&lock); 802 | 803 | printf("Add Cache Lock Acquired %d\n", temp_lock_val); 804 | 805 | int element_size = size + 1 + strlen(url) + sizeof(cache_element); // Size of the new element which will be added to the cache 806 | 807 | /*if(element_size>MAX_ELEMENT_SIZE){ 808 | 809 | //sem_post(&cache_lock); 810 | 811 | // If element size is greater than MAX_ELEMENT_SIZE we don't add the element to the cache 812 | 813 | temp_lock_val = pthread_mutex_unlock(&lock); 814 | 815 | printf("Add Cache Lock Unlocked %d\n", temp_lock_val); 816 | 817 | return 0; 818 | 819 | }*/ 820 | 821 | if (element_size <= MAX_ELEMENT_SIZE) 822 | 823 | { 824 | while (cache_size + element_size > MAX_SIZE) 825 | { 826 | 827 | // We keep removing elements from cache until we get enough space to add the element 828 | 829 | remove_cache_element(); 830 | } 831 | 832 | cache_element *element = (cache_element *)malloc(sizeof(cache_element)); // Allocating memory for the new cache element 833 | 834 | element->data = (char *)malloc(size + 10); // Allocating memory for the response to be stored in the cache element 835 | 836 | strcpy(element->data, data); 837 | 838 | element->url = (char *)malloc(10 + (strlen(url) * sizeof(char))); // Allocating memory for the request to be stored in the cache element (as a key) 839 | 840 | strcpy(element->url, url); 841 | 842 | element->lru_time_track = time(NULL); // Updating the time_track 843 | 844 | element->next = head; 845 | 846 | element->len = size; 847 | 848 | head = element; 849 | 850 | cache_size += element_size; 851 | 852 | // sem_post(&cache_lock); 853 | 854 | ret = 1; 855 | } 856 | 857 | temp_lock_val = pthread_mutex_unlock(&lock); 858 | 859 | printf("Add Cache Lock Unlocked %d\n", temp_lock_val); 860 | 861 | free(url); 862 | 863 | free(data); 864 | 865 | printf("\ncache size:%d\n", cache_size); 866 | 867 | return ret; 868 | } 869 | --------------------------------------------------------------------------------