The Presentation Layer, which is Layer 6 of the OSI model, translates data between the application layer and the network transmission format. This ensures that data sent from one device can be correctly interpreted by another, even if they use different data representations. For example, it handles conversions between character encodings such as ASCII and EBCDIC, translates between different data structures, and converts multimedia formats such as audio or video streams.
By providing this translation function, the Presentation Layer enables applications on heterogeneous systems to communicate effectively.
In addition to data translation, the Presentation Layer is responsible for data encryption and compression. Encryption transforms data into a secure format to protect it during transmission, while compression reduces data size to improve efficiency and save bandwidth.
These functions ensure that data is interpretable, secure, and optimized for network transport. By handling these tasks independently of the application layer, the Presentation Layer standardizes data handling and promotes interoperability between different software and hardware systems.
Although conceptually important, the Presentation Layer is often not explicitly implemented in modern network protocol stacks. Many of its functions, such as encryption, compression, and data formatting, are integrated into the application layer or handled by specific libraries and frameworks. This is why some protocol stacks omit a distinct Presentation Layer, but the responsibilities it defines remain essential for consistent, secure, and interpretable data exchange.
The optional functions of the Presentation Layer include:
- Encryption: Converting plaintext to ciphertext using a secret key
- Decryption: Converting ciphertext back to plaintext using the same secret key
- Compression: Reducing the number of symbols required to represent data
- Decompression: Restoring compressed data to its original form
- Formatting: Converting data into a standardized format for easy transmission
GNU ZIP (GZIP)
GNU ZIP (GZIP) is a widely used compression technology designed to reduce file size for efficient storage and transfer over networks, especially the internet. It uses the DEFLATE algorithm, which combines LZ77 compression and Huffman coding to achieve high compression ratios while maintaining reasonable processing speed.
GZIP files are easily recognizable by their magic header 1F 8B, which identifies the format and allows software to interpret and decompress the data correctly. Commonly used in web servers, APIs, and data archival, GZIP helps reduce bandwidth usage and speeds up data transmission, making it a key tool for improving network efficiency and performance.
To compress data using GZIP
from gzip import compress
compressed = compress(b"Test")
print("Output: {}\nHex: {}".format(compressed,compressed.hex(' ')))
Output
Output: b'\x1f\x8b\x08\x00\xc0\xc9\xc2f\x02\xff\x0bI-.\x01\x002\xd1Mx\x04\x00\x00\x00'
Hex: 1f 8b 08 00 c0 c9 c2 66 02 ff 0b 49 2d 2e 01 00 32 d1 4d 78 04 00 00 00
To decompress data using GZIP
from gzip import decompress
compressed = decompress(b'\x1f\x8b\x08\x00\xc0\xc9\xc2f\x02\xff\x0bI-.\x01\x002\xd1Mx\x04\x00\x00\x00')
print("Output: {}\nHex: {}".format(compressed,compressed.hex(' ')))
Output
Output: b'Test'
Hex: 54 65 73 74
PCAP Example
The client requested web content from the server and indicated that it accepts GZIP content, the server responds with content that is compressed in GZIP, the client receives the GZIP and gets handled in the presentation layer
Client Sends Data
The client uses the HTTP Accept-Encoding header to indicate that GZIP is allowed
| Layer | Protocol | PDU | Info | Ports | IPs | MACs |
| Application Layer | HTTP | Data | GET / HTTP/1.1 Host: 10.0.0.2 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Connection: keep-alive Upgrade-Insecure-Requests: 1 | |||
| Presentation Layer | Raw | Data | GET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1 | |||
| Session Layer | Socket | Data | GET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1 | |||
| Transport Layer | TCP | Segments | GET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1 | Src Port: 35310 Dst Port: 80 | ||
| Network Layer | IP | Packets | GET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1 | Src Port: 35310 Dst Port: 80 | Src IP: 10.0.0.3 Dst IP: 10.0.0.2 | |
| Data Link Layer | Ethernet | Frames | GET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1 | Src Port: 35310 Dst Port: 80 | Src IP: 10.0.0.3 Dst IP: 10.0.0.2 | Src MAC: bc:35:db:cf:1b:03 Dst MAC: bc:f2:b8:57:86:02 |
| Physical Layer | Coax | Bits | 01001000 01010100 01010100 01010000 00101111 00110001 00101110 00110000 00100000 | 01001000 01010100 | 01001000 01010100 | 01001000 01010100 |

Server Receives Data
The server receives the client’s HTTP Accept-Encoding request
| Layer | Protocol | PDU | Info | Ports | IPs | MACs |
| Physical Layer | Coax | Bits | 01001000 01010100 01010100 01010000 00101111 00110001 00101110 00110000 00100000 | 01001000 01010100 | 01001000 01010100 | 01001000 01010100 |
| Data Link Layer | Ethernet | Frames | GET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1 | Src Port: 35310 Dst Port: 80 | Src IP: 10.0.0.3 Dst IP: 10.0.0.2 | Src MAC: bc:35:db:cf:1b:03 Dst MAC: bc:f2:b8:57:86:02 |
| Network Layer | IP | Packets | GET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1 | Src Port: 35310 Dst Port: 80 | Src IP: 10.0.0.3 Dst IP: 10.0.0.2 | |
| Transport Layer | TCP | Segments | GET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1 | Src Port: 35310 Dst Port: 80 | ||
| Session Layer | Socket | Data | GET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1 | |||
| Presentation Layer | Raw | Data | GET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1 | |||
| Application Layer | HTTP | Data | GET / HTTP/1.1 Host: 10.0.0.2 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Connection: keep-alive Upgrade-Insecure-Requests: 1 |
from http.server import SimpleHTTPRequestHandler # Import the built-in HTTP request handler
from socketserver import TCPServer # Import a basic TCP server implementation
from io import BytesIO # Import BytesIO to handle bytes in memory (for gzip compression)
from gzip import GzipFile # Import GzipFile to compress HTTP response
from datetime import datetime # Import datetime to generate timestamps for logging
from contextlib import suppress # Import suppress to prevent crasheswith suppress(Exception): # Try importing network interface details
from netifaces import gateways, ifaddresses, AF_INET, AF_LINK # Network interface utilities
print(“The default network interface is: “, gateways()[‘default’][AF_INET][1]) # Display default network interface name
print(“The default network interface mac address is: “, ifaddresses(gateways()[‘default’][AF_INET][1])[AF_LINK]) # Display MAC address of the default network interfaceclass Server(SimpleHTTPRequestHandler): # Define a custom HTTP server
def do_GET(self): # Handle HTTP GET requests
compressed = False # Track whether gzip compression is used
content = b'<HTML><h1>Hello World!</h1></HTML>’ # HTTP response body (bytes)if len(content) > 0: # Only attempt compression if content exists
if ‘accept-encoding’ in self.headers: # Check if client sent Accept-Encoding header
if ‘gzip’ in self.headers[‘accept-encoding’]: # Client supports gzip
bytes_ = BytesIO() # Create an in-memory byte buffer
with GzipFile(fileobj=bytes_, mode=’w’, compresslevel=5) as f: # Gzip wrapper
f.write(content) # Compress the response body
content = bytes_.getvalue() # Replace content with compressed bytes
compressed = True # Mark response as compressedself.send_response(200) # Send HTTP 200 OK status
if compressed:
self.send_header(‘content-encoding’, ‘gzip’) # Notify client of gzip encoding
self.send_header(‘content-length’, len(content)) # Send content length header
self.end_headers() # End HTTP headers
self.wfile.write(content) # Write response body to clientdef log_message(self, format, *args): # Override default request logging
print(“[{}] – {}:{} – {} {}”.format( # Custom log format
datetime.now().strftime(“%m/%d/%Y %H:%M:%S”), # Timestamp
self.client_address[0], # Client IP address
self.client_address[1], # Client source port
args[0], # HTTP method
args[1] # Requested path
))TCPServer((‘0.0.0.0’, 80), Server).serve_forever() # Start server on all interfaces, port 80
from http.server import SimpleHTTPRequestHandler
from socketserver import TCPServer
from io import BytesIO
from gzip import GzipFile
from datetime import datetime
from contextlib import suppress
with suppress(Exception):
from netifaces import gateways, ifaddresses, AF_INET, AF_LINK
print("The default network interface is: ",gateways()['default'][AF_INET][1])
print("The default network interface mac address is: ",ifaddresses(gateways()['default'][AF_INET][1])[AF_LINK])
class Server(SimpleHTTPRequestHandler):
def do_GET(self):
compressed = False
content = b'<HTML><h1>Hello World!</h1></HTML>'
if len(content) > 0:
if 'accept-encoding' in self.headers:
if 'gzip' in self.headers['accept-encoding']:
bytes_ = BytesIO()
with GzipFile(fileobj=bytes_, mode='w', compresslevel=5) as f:
f.write(content)
f.close()
content = bytes_.getvalue()
compressed = True
self.send_response(200)
if compressed:
self.send_header('content-encoding', 'gzip')
self.send_header('content-length', len(content))
self.end_headers()
self.wfile.write(content)
def log_message(self, format, *args):
print("[{}] - {}:{} - {} {}".format(datetime.now().strftime("%m/%d/%Y %H:%M:%S"), self.client_address[0],self.client_address[1],args[0],args[1]))
TCPServer(('0.0.0.0', 80), Server).serve_forever()
Server Sends Data
The server compresses and sends the compressed web content
| Layer | Protocol | PDU | Info | Ports | IPs | MACs |
| Application Layer | HTTP | Data | HTTP/1.0 200 OK Server: SimpleHTTP/0.6 Python/3.11.6 Date: Sun, 18 Aug 2024 02:54:42 GMT content-encoding: gzip content-length: 49 <HTML><h1>Hello World!</h1></HTML> | |||
| Presentation Layer | GZIP | Data | HTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″… | |||
| Session Layer | Socket | Data | HTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″… | |||
| Transport Layer | TCP | Segments | HTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″… | Src Port: 80 Dst Port: 35310 | ||
| Network Layer | IP | Packets | HTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″… | Src Port: 80 Dst Port: 35310 | Src IP: 10.0.0.2 Dst IP: 10.0.0.3 | |
| Data Link Layer | Ethernet | Frames | HTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″… | Src Port: 80 Dst Port: 35310 | Src IP: 10.0.0.2 Dst IP: 10.0.0.3 | Src MAC: bc:f2:b8:57:86:02 Dst MAC: bc:35:db:cf:1b:03 |
| Physical Layer | Coax | Bits | 01001000 01010100 01010100 01010000 00101111 00110001 00101110 00110000 00100000 | 01001000 01010100 | 01001000 01010100 | 01001000 01010100 |
from http.server import SimpleHTTPRequestHandler # Import the built-in HTTP request handler
from socketserver import TCPServer # Import a basic TCP server implementation
from io import BytesIO # Import BytesIO to handle bytes in memory (for gzip compression)
from gzip import GzipFile # Import GzipFile to compress HTTP response
from datetime import datetime # Import datetime to generate timestamps for logging
from contextlib import suppress # Import suppress to prevent crasheswith suppress(Exception): # Try importing network interface details
from netifaces import gateways, ifaddresses, AF_INET, AF_LINK # Network interface utilities
print(“The default network interface is: “, gateways()[‘default’][AF_INET][1]) # Display default network interface name
print(“The default network interface mac address is: “, ifaddresses(gateways()[‘default’][AF_INET][1])[AF_LINK]) # Display MAC address of the default network interfaceclass Server(SimpleHTTPRequestHandler): # Define a custom HTTP server
def do_GET(self): # Handle HTTP GET requests
compressed = False # Track whether gzip compression is used
content = b'<HTML><h1>Hello World!</h1></HTML>’ # HTTP response body (bytes)if len(content) > 0: # Only attempt compression if content exists
if ‘accept-encoding’ in self.headers: # Check if client sent Accept-Encoding header
if ‘gzip’ in self.headers[‘accept-encoding’]: # Client supports gzip
bytes_ = BytesIO() # Create an in-memory byte buffer
with GzipFile(fileobj=bytes_, mode=’w’, compresslevel=5) as f: # Gzip wrapper
f.write(content) # Compress the response body
content = bytes_.getvalue() # Replace content with compressed bytes
compressed = True # Mark response as compressedself.send_response(200) # Send HTTP 200 OK status
if compressed:
self.send_header(‘content-encoding’, ‘gzip’) # Notify client of gzip encoding
self.send_header(‘content-length’, len(content)) # Send content length header
self.end_headers() # End HTTP headers
self.wfile.write(content) # Write response body to clientdef log_message(self, format, *args): # Override default request logging
print(“[{}] – {}:{} – {} {}”.format( # Custom log format
datetime.now().strftime(“%m/%d/%Y %H:%M:%S”), # Timestamp
self.client_address[0], # Client IP address
self.client_address[1], # Client source port
args[0], # HTTP method
args[1] # Requested path
))TCPServer((‘0.0.0.0’, 80), Server).serve_forever() # Start server on all interfaces, port 80
from http.server import SimpleHTTPRequestHandler
from socketserver import TCPServer
from io import BytesIO
from gzip import GzipFile
from datetime import datetime
from contextlib import suppress
with suppress(Exception):
from netifaces import gateways, ifaddresses, AF_INET, AF_LINK
print("The default network interface is: ",gateways()['default'][AF_INET][1])
print("The default network interface mac address is: ",ifaddresses(gateways()['default'][AF_INET][1])[AF_LINK])
class Server(SimpleHTTPRequestHandler):
def do_GET(self):
compressed = False
content = b'<HTML><h1>Hello World!</h1></HTML>'
if len(content) > 0:
if 'accept-encoding' in self.headers:
if 'gzip' in self.headers['accept-encoding']:
bytes_ = BytesIO()
with GzipFile(fileobj=bytes_, mode='w', compresslevel=5) as f:
f.write(content)
f.close()
content = bytes_.getvalue()
compressed = True
self.send_response(200)
if compressed:
self.send_header('content-encoding', 'gzip')
self.send_header('content-length', len(content))
self.end_headers()
self.wfile.write(content)
def log_message(self, format, *args):
print("[{}] - {}:{} - {} {}".format(datetime.now().strftime("%m/%d/%Y %H:%M:%S"), self.client_address[0],self.client_address[1],args[0],args[1]))
TCPServer(('0.0.0.0', 80), Server).serve_forever()
Client Receives Data
Wireshark decompresses the content (The web browser does that as well in the background, and there will be a tab at the bottom that has the decompressed content)
| Layer | Protocol | PDU | Info | Ports | IPs | MACs |
| Physical Layer | Coax | Bits | 01001000 01010100 01010100 01010000 00101111 00110001 00101110 00110000 00100000 | 01001000 01010100 | 01001000 01010100 | 01001000 01010100 |
| Data Link Layer | Ethernet | Frames | HTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″… | Src Port: 80 Dst Port: 35310 | Src IP: 10.0.0.2 Dst IP: 10.0.0.3 | Src MAC: bc:f2:b8:57:86:02 Dst MAC: bc:35:db:cf:1b:03 |
| Network Layer | IP | Packets | HTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″… | Src Port: 80 Dst Port: 35310 | Src IP: 10.0.0.2 Dst IP: 10.0.0.3 | |
| Transport Layer | TCP | Segments | HTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″… | Src Port: 80 Dst Port: 35310 | ||
| Session Layer | Socket | Data | HTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″… | |||
| Presentation Layer | GZIP | Data | HTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″… | |||
| Application Layer | HTTP | Data | HTTP/1.0 200 OK Server: SimpleHTTP/0.6 Python/3.11.6 Date: Sun, 18 Aug 2024 02:54:42 GMT content-encoding: gzip content-length: 49 <HTML><h1>Hello World!</h1></HTML> |


