B15
is a HTTP compliant TCP socket server and client. The server can accept any HTTP client (including ours of course) and acts as a proxy to NewsAPI.org. The client can be used to send requests to the server and receive responses from the server. The client and server are written in Python and some tests are written in shell bash.
B15 is quite different from other TCP socket servers due to a multitude of factors, the biggest of which, is that it's compliant with the HTTP/1.1 RFC.
Note
nginx
has inspired the idea of this project!
The hypertext transfer protocol is an application-level protocol for distributed, collaborative, hypermedia information systems. HTTP is the foundation of data communication for the World Wide Web. It is a OSI Layer 7 Protocol built on top of a OSI Layer 4 protocol (TCP).
The idea of this project, is to provide our TCP server socket the ability to accept HTTP clients from any source, and that is done by making it understand the requests, and encode responses in a very specific manner.
Going through the project, you'll find multiple modules that facilitate communication with all clients. You'll also find routing utilities that are responsible for multiplexing the requests to the required controller. You'll also find out that our client can communicate with any HTTP server if configured of course.
Important
Use a mature Linux distro, or WSL if you are on windows!
To run B15, you'll need to have Python 3.11.0 or higher installed on your machine.
First, get an API key from NewsAPI.org and add it to a .env
file in the root directory of the project Like this.
API_KEY=<YOUR_API_KEY_HERE>
On each shell you open, you'll need to activate the venv, and configure it. For this, run source config-env.sh
.
This script creates a new venv (if needed), adds all the dependencies (if needed), and configs all aliases for streamlined project execution!
To run the server, run B15 server
from the root dir of the project.
To run the client, run B15 client
from the root dir of the project.
To view more info (aka this README), run B15 info
from the root dir of the project.
There are mainly two scripts, but they use extensive utilities from the src
module in each of their directories. Read below for how its done
server.py
: This is the main server script that hosts the passive server socket that accepts client connections
from src.utils.server_logging import server_logger
from src.handler import HandleClient
from rich.traceback import install
from dotenv import load_dotenv
import threading
import socket
import sys
load_dotenv() # load the .env file for API key
install() # pretty print errors
# Expose ip interface if given as CLI argument, else localhost
HOST = sys.argv[1] if len(sys.argv) > 1 else '127.0.0.1'
PORT = 9090
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((HOST, PORT))
server_socket.listen() # listen for an infinite amt of clients
server_logger.info(f"listening and serving on {HOST}:{PORT}")
try:
while True:
client_conn, address = server_socket.accept()
client_thread = threading.Thread(
target=HandleClient, args=(client_conn,))
client_thread.start()
except KeyboardInterrupt:
server_logger.info(f"Server stopped")
finally:
server_socket.shutdown(socket.SHUT_RDWR)
server_socket.close()
sys.exit(0)
src/http
: This is the module that contains theHttpRequest
andHTTPResponseWriter
classes. It also containes the router from which we control client requests and return appropriate responses.
Tip
Check out the README inside server/src/http
for more information
src/logging
takes care of logging the requests and responses (to JSON and to the console).server/log
contains server logs and also client logs (inJSON
)
client.py
This is the main client script
from src.http.request_creator import ClientHttpRequest
from rich.console import Console
from src.conn import ServerConn
from rich.prompt import Prompt
import sys
try:
username = Prompt.ask("[bold green]Enter your username")
ServerConn(username)
except ConnectionRefusedError:
Console().print("[bold red]The Server is not live")
sys.exit(1)
except KeyboardInterrupt:
if 'username' in locals() and username:
# disconnect the client before ending
if ClientHttpRequest().POST(url="/disconn", host="localhost", port=9090, data={"client_name": username}).get_status_code() == 200:
Console().print(f"[bold white]\nGoodbye {username}!")
else:
Console().print(f"[bold red]\nerror disconnecting client")
else:
print("\nGoodbye!")
sys.exit(0)
src/conn.py
This script establishes a new connection with the server and registers the client's namesrc/UI
contains the scripts that are necessary to output a nice terminal UImarkdowns.py
contains all the menus outputted in the terminalmenus.py
contains all the menus that also make requests to the serverheadline_response_fmt
andsources_response_fmt.py
format allJSON
responses comming from the server
src/http
contains theClientHTTPRequest
module that creates and sends HTTP compliantGET
andPOST
requests to our server. It also containsHttpResponseParser
That parses the raw response from our server
B15
was built using test driven development, you can see all the shell scripts used to test in thetests
directory, and use them too!- To use them, run
sudo chmod 777 *
inside thetests
directory, and then run./<script-name>
- To use them, run
- A
.env
file was used to ensure security of our API key, and is loaded inside theserver.py
script using thedotenv
package - A python
.gitignore
template was used to not push any unimportant scripts - B15 server adapts to any HTTP client, including
Postman
orcurl
!- You may also build other clients using Js, Go, or anything really 😄
- Shell script is extensively used to run B15
B15
is fully thread safe, and uses concurrency concepts like mutex locks to manage race conditions between multiple clients- Last but not least, a great terminal UI is used for
B15
, provided by therich
module
.
├── client
│ ├── client.py
│ └── src
│ ├── conn.py
│ ├── http
│ │ ├── request_creator.py
│ │ └── response_parser.py
│ └── UI
│ ├── headline_response_fmt.py
│ ├── markdowns.py
│ ├── menus.py
│ └── sources_response_fmt.py
├── config-env.sh
├── LICENSE
├── README.md
├── requirements.txt
├── scripts
│ ├── client.sh
│ ├── run.sh
│ └── server.sh
├── server
│ ├── log
│ │ └── json
│ │ ├── B15_aa_1.1.json
│ │ ├── B15_aa_None.json
│ │ ├── B15_ab_1.1.json
│ │ ├── B15_ab_None.json
│ │ ├── B15_ak_1.1.json
│ │ ├── B15_ak_1.3.json
│ │ ├── B15_ak_2.1.json
│ │ ├── B15_ak_2.3.json
│ │ └── B15_ak_None.json
│ ├── server.py
│ └── src
│ ├── controllers
│ │ ├── headlines.py
│ │ ├── README.md
│ │ └── sources.py
│ ├── handler.py
│ ├── http
│ │ ├── request_parser.py
│ │ ├── response_writer.py
│ │ └── router.py
│ └── utils
│ ├── client_logging.py
│ ├── data_validation.py
│ └── server_logging.py
└── tests
├── test_concurrency.sh
├── test_conn.sh
├── test_headlines.sh
├── test_sources.sh
└── test_status_codes.sh
22 directories, 40 files
Group B15 - Course ITNE352 - Section 2
- Abdulrahman Khaled Idrees - 202200729
- Yousef Raja Salem - 202109958