2024-12-31 01:57:48 +00:00
import socket
import os
2024-12-31 05:44:19 +00:00
from datetime import datetime
import pathlib
import html
server_start = datetime . now ( )
import pgs
print ( pgs . add ( 1 , 2 ) )
def get_server_uptime ( ) :
seconds = ( datetime . now ( ) - server_start ) . total_seconds ( )
if seconds > 60 * 60 : # hour
return f " { int ( seconds / 60 / 60 ) } hours "
elif seconds > 60 : # minute
return f " { int ( seconds / 60 ) } minutes "
return f " { seconds } seconds "
2024-12-31 01:57:48 +00:00
# Hostname, only resolved at startup.
hostname = socket . gethostname ( )
# Specify environment based on hostname.
env = " dev " if hostname . startswith ( " retoor " ) else " prod "
# This dict will contain the connections in this format: {downstream_fd:upstream_fd]
streams = { }
# This is debug variable. It holds the number of connections total made.
counter = 0
def is_ssh ( header_bytes ) :
return b ' SSH ' in header_bytes
def is_http ( header_bytes ) :
"""
Check if the header is an HTTP request .
"""
return b ' HTTP/1.1 ' in header_bytes or b ' HTTP/1.0 ' in header_bytes or b ' HTTP/2.0 ' in header_bytes or b ' HTTP/3.0 ' in header_bytes or b ' Connection: ' in header_bytes
def is_https ( header_bytes ) :
return not any ( [ is_ssh ( header_bytes ) , is_http ( header_bytes ) ] )
def route ( downstream , upstream ) :
"""
This is a connection router which will be called by the server every
time a client connects . This function will be used to determine
the upstream . The downstream and upstream are file descriptors .
The upstream is not connected yet , it only holds a file descriptor .
The connection will be made in this function . The connection will be
set non blocking after this function by pgs .
This way of routing is so dynamic that you can :
- Create [ your - site ] . localhost redirects without configuring DNS .
- Run multiple services on the same port . It is possible to run ssh
on the same port as your HTTPS server . This is a good idea in sense
of security . Very unique , who does / expects that ?
- Rewrite urls in general .
- Make clients always connect to the same upstream . Servers only have
to manage their own session instead of having to communicate with
redis .
- You can inject headers in the request .
- You can add HTTP Basic Authentication to protect all your services
in a very early stage .
- This server is quick , it can act as ddos protection .
- You can make your server act as a load balancer .
- You can make your server act as a reverse proxy .
- You can apply rate limits .
- You can cache responses .
- You can implement a complete custom protocol here . Complete own
design . This feature will probably moved in the future .
- You can do static file serving .
- You can protect sensitive data not leaving the network by
intercepting it .
- You can call AI to make modications .
- You can call databases .
- You can save statistics .
"""
global streams
global counter
counter + = 1
print ( " Connection nr. " , counter )
u = socket . fromfd ( upstream , socket . AF_INET , socket . SOCK_STREAM )
#u = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#print("FD:",u.fileno())
2024-12-31 05:44:19 +00:00
peek = pgs . read ( downstream , 4096 ) . tobytes ( )
2024-12-31 01:57:48 +00:00
2024-12-31 05:44:19 +00:00
if pgs . is_ssh ( peek ) :
2024-12-31 01:57:48 +00:00
print ( " Forwarding to ssh molodetz " )
u . connect ( ( " molodetz.nl " , 22 ) )
2024-12-31 05:44:19 +00:00
elif pgs . is_http ( peek ) :
2024-12-31 01:57:48 +00:00
if b ' /random ' in peek or b ' random. ' in peek :
2024-12-31 05:44:19 +00:00
print ( " Forwarding to 127.0.0.1:3028. " )
2024-12-31 01:57:48 +00:00
peek = peek . replace ( b ' /random ' , b ' / ' )
peek = peek . replace ( b ' random. ' , b ' ' )
u . connect ( ( " 127.0.0.1 " , 3028 ) )
elif b ' molodetz.local ' in peek :
2024-12-31 05:44:19 +00:00
print ( " Forwarding to 127.0.0.1:8082. " )
2024-12-31 01:57:48 +00:00
peek = peek . replace ( b ' molodetz.local ' , b ' localhost ' )
u . connect ( ( " 127.0.0.1 " , 8082 ) )
2024-12-31 05:44:19 +00:00
elif b ' bench.local ' in peek :
print ( " Responding with bench page. " )
body = f """ <html> \n <head> \n <title>Benchmark page.</title> \n </head> \n <body> \n <h1>Bench</h1> \n <p> { counter } </p> \n </body> \n </html> \n """ . encode ( )
s = socket . fromfd ( downstream , socket . AF_INET , socket . SOCK_STREAM )
s . sendall (
b ' HTTP/1.1 200 Pretty Good Server. \r \n '
+ b ' Content-Length: ' + str ( len ( body ) ) . encode ( ) + b ' \r \n '
+ b ' Content-Type: text/html \r \n \r \n '
+ body
)
u = None
s . shutdown ( socket . SHUT_RDWR )
else :
# 404
if env == " prod " :
pgs . write ( downstream ,
b ' HTTP/1.1 403 Authorization Required. \r \n \r \n ' )
else :
pgscript_source = html . escape ( pathlib . Path ( __file__ ) . read_text ( ) )
content = f """ <pre>
Server : Pretty Good Server
Environment : { env }
Total connections : { counter }
Local hostname : { hostname }
Downstream FD : { downstream }
Upstream FD : { upstream }
Current time server : { datetime . now ( ) }
Server started on : { server_start }
Server uptime : { get_server_uptime ( ) }
< / pre >
< h3 > Source code of pgscript . py < / h3 >
< i > Location : { pathlib . Path ( __file__ ) . resolve ( ) } < / i >
< pre style = " color:blue; " >
{ pgscript_source }
< / pre >
"""
body = f """ <html> \n <head> \n <title>Debug page.</title> \n </head> \n <body> \n <h1>Pretty Good Server</h1> \n <h3>Debugging information</h3> \n <p> { content } </p> \n </body> \n </html> \n """
headers = [ " HTTP/1.1 200 Pretty Good Server. " ,
" Content-Length: { len(body)} " ,
" Content-Type: text/html " ,
" "
]
headers = " \r \n " . join ( headers )
response = f " { headers } { body } "
pgs . write ( downstream , response )
# Unset socket so the server will close it.
# Do not disconnect in python!
# Instead of a 404, we also could've displayed a custom page.
# Maybe some server statistics?
u = None
2024-12-31 01:57:48 +00:00
elif is_https ( peek ) and env == " prod " :
print ( " Forwarding to dev.to " )
u . connect ( ( " devrant.com " , 443 ) )
peek = peek . replace ( b ' localhost ' , b ' devrant.com ' )
peek = peek . replace ( b ' molodetz.nl ' , b ' devrant.com ' )
else :
# Error.
print ( " Could not find upstream for header content. " )
2024-12-31 05:44:19 +00:00
print ( f " Closing connection. Your current environment: { env } " )
2024-12-31 01:57:48 +00:00
# Don't have to close socket, pgs will do that himself.
# Socket handling is done at one place to avoid race conditions.
2024-12-31 05:44:19 +00:00
u = None
2024-12-31 01:57:48 +00:00
if not u :
return - 1
# Remove reference to the socket so it doesn't get garbage collected.
# This could break the connection. This way, it stays open.
u = None
os . write ( upstream , peek )
# Keep track of connections. Not sure if this is needed.
streams [ downstream ] = upstream
streams [ upstream ] = downstream
# Return exact same value as what is given as parameter.
return upstream