<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: H.M.Ashis Rahman</title>
    <description>The latest articles on Forem by H.M.Ashis Rahman (@ashisrahman).</description>
    <link>https://forem.com/ashisrahman</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3473836%2F6e759de0-54ee-46f3-8650-bafec50fdd5f.jpg</url>
      <title>Forem: H.M.Ashis Rahman</title>
      <link>https://forem.com/ashisrahman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ashisrahman"/>
    <language>en</language>
    <item>
      <title>How I Built My Own Offline 2FA Authenticator App (A Weekend Android Project)</title>
      <dc:creator>H.M.Ashis Rahman</dc:creator>
      <pubDate>Thu, 23 Oct 2025 17:36:58 +0000</pubDate>
      <link>https://forem.com/ashisrahman/how-i-built-my-own-offline-2fa-authenticator-app-a-weekend-android-project-59b9</link>
      <guid>https://forem.com/ashisrahman/how-i-built-my-own-offline-2fa-authenticator-app-a-weekend-android-project-59b9</guid>
      <description>&lt;h2&gt;
  
  
  Introduction: The Offline Mystery of 2FA
&lt;/h2&gt;

&lt;p&gt;We all rely on 2-Factor Authentication (2FA) for enhanced security. Apps like Google Authenticator, Authy, or Microsoft Authenticator are staples on our phones. But have you ever stopped to wonder how they generate those constantly changing, time-sensitive codes without an internet connection?&lt;/p&gt;

&lt;p&gt;That question sparked my latest weekend project. I decided to pull back the curtain and build my very own 2FA authenticator app for Android, focused specifically on implementing the Time-based One-Time Password (TOTP) algorithm. It was a fascinating dive into cryptography, time synchronization, and mobile development.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is TOTP and How Does It Work?
&lt;/h2&gt;

&lt;p&gt;Before we jump into the code, let's quickly demystify TOTP. At its core, TOTP (Time-based One-Time Password) is a method that uses a shared secret key and the current time to generate a unique, temporary verification code.&lt;/p&gt;

&lt;p&gt;The magic relies on both the authentication server and your authenticator app having:&lt;/p&gt;

&lt;h2&gt;
  
  
  A Shared Secret Key:
&lt;/h2&gt;

&lt;p&gt;A unique, random string generated during the account setup (when you scan a QR code).&lt;/p&gt;

&lt;h2&gt;
  
  
  A Common Understanding of Time:
&lt;/h2&gt;

&lt;p&gt;While not perfectly synchronized to milliseconds, both need to agree on roughly the same "time window" (e.g., a 30-second interval).&lt;/p&gt;

&lt;p&gt;Using these two inputs, they can independently generate the same OTP at any given 30-second interval. When you enter the code from your app, the server performs the same calculation. If the codes match, you're authenticated!&lt;/p&gt;

&lt;h2&gt;
  
  
  My Project: Architecture Overview
&lt;/h2&gt;

&lt;p&gt;Here's a breakdown of the simple architecture I implemented for this project:&lt;/p&gt;

&lt;p&gt;Authentication Server (Conceptual/Simulated):&lt;/p&gt;

&lt;p&gt;Generates a unique secret key for a user.&lt;/p&gt;

&lt;p&gt;Stores this secret key securely in a database.&lt;/p&gt;

&lt;p&gt;Generates a QR Code that encodes this secret key (often using a otpauth:// URI format). This QR code is what the user scans to "enroll" their device.&lt;/p&gt;

&lt;p&gt;Android Authenticator App (The Star of the Show):&lt;/p&gt;

&lt;p&gt;QR Code Scanner: Captures the &lt;code&gt;otpauth:// URI&lt;/code&gt;&lt;br&gt;
 containing the secret key.&lt;/p&gt;

&lt;p&gt;Secret Key Storage: Securely stores the captured secret key on the device.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw0stp7gxavzsnd6v0cao.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw0stp7gxavzsnd6v0cao.jpg" alt=" " width="715" height="1016"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;TOTP Generator: Uses the stored secret key and the device's current time to continuously calculate and display the current 6-digit OTP, refreshing every 30 seconds. Here's a breakdown of the simple architecture I implemented for this project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4yhqc0ai897dom6klpgk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4yhqc0ai897dom6klpgk.jpg" alt=" " width="720" height="1444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Authentication Server (Conceptual/Simulated):&lt;/p&gt;

&lt;p&gt;Generates a unique secret key for a user.&lt;/p&gt;

&lt;p&gt;Stores this secret key securely in a database.&lt;/p&gt;

&lt;p&gt;Generates a QR Code that encodes this secret key (often using a otpauth:// URI format). This QR code is what the user scans to "enroll" their device.&lt;/p&gt;

&lt;p&gt;Android Authenticator App (The Star of the Show):&lt;/p&gt;

&lt;h2&gt;
  
  
  1.QR Code Scanner: Captures the
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqaehmynwv2m4uj8hij6a.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqaehmynwv2m4uj8hij6a.jpg" alt=" " width="720" height="1435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;otpauth:// URI&lt;/code&gt;&lt;br&gt;
 containing the secret key.&lt;/p&gt;

&lt;p&gt;Secret Key Storage: Securely stores the captured secret key on the device.&lt;/p&gt;

&lt;p&gt;TOTP Generator: Uses the stored secret key and the device's current time to continuously calculate and display the current 6-digit OTP, refreshing every 30 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Implementations
&lt;/h2&gt;

&lt;p&gt;I leveraged a popular library for QR code scanning (e.g., ZXing or ML Kit's Barcode Scanning API). The critical part here is parsing the &lt;code&gt;otpauth:// URI&lt;/code&gt;, which typically looks something like this:&lt;br&gt;
&lt;code&gt;otpauth://totp/Example:user@example.com?secret=JBSWY3DPEHPK3PXP&amp;amp;issuer=Example&amp;amp;digits=6&amp;amp;period=30&lt;/code&gt;&lt;br&gt;
From this, the app extracts the &lt;code&gt;secret&lt;/code&gt; parameter, which is our vital shared key.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Secure Secret Key Storage
&lt;/h2&gt;

&lt;p&gt;Storing the secret key directly in plain text is a huge security risk. For this project, I opted for Android's &lt;code&gt;EncryptedSharedPreferences&lt;/code&gt; (from the AndroidX Security library), which stores data securely using the Android Keystore system. This encrypts the secret key at rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. TOTP Generation Logic
&lt;/h2&gt;

&lt;p&gt;This is where the core algorithm comes into play. I implemented the TOTP algorithm using a standard Java cryptography library (or a custom implementation if you prefer to go deeper).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;generateTOTP&lt;/code&gt; function typically takes:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;secretKey&lt;/code&gt; (the base32 decoded secret)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;time&lt;/code&gt; (current Unix timestamp divided by the time step, e.g., 30 seconds)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;digits&lt;/code&gt; (number of digits for the OTP, usually 6 or 8)&lt;/p&gt;

&lt;h2&gt;
  
  
  The algorithm involves:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;HMAC-SHA1 Calculation: Computing an HMAC-SHA1 hash using the secret key as the HMAC key and the current time step as the message. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dynamic Truncation: Taking a specific byte from the HMAC result and using it to dynamically select a 4-byte sequence.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Modulo Operation: Converting this 4-byte sequence into an integer and taking its modulo by &lt;code&gt;10^digits&lt;/code&gt; to get the final OTP.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To make the app dynamic, I used a &lt;code&gt;Handler&lt;/code&gt; or a &lt;code&gt;Flow/LiveData&lt;/code&gt; to periodically trigger the TOTP calculation and update the UI every second, visually showing the 30-second countdown before a new code appears.&lt;/p&gt;

</description>
      <category>androiddev</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Building A Real-Time Communication System Using Go and WebSocket</title>
      <dc:creator>H.M.Ashis Rahman</dc:creator>
      <pubDate>Fri, 10 Oct 2025 19:18:18 +0000</pubDate>
      <link>https://forem.com/ashisrahman/building-a-real-time-communication-system-using-go-and-websocket-3ocf</link>
      <guid>https://forem.com/ashisrahman/building-a-real-time-communication-system-using-go-and-websocket-3ocf</guid>
      <description>&lt;p&gt;Real-time applications like chat systems, dashboards, or multiplayer games rely on persistent, low-latency communication between client and server. Traditional HTTP is request-response only, but it can’t push data to the client unless the client keeps polling. That’s where &lt;strong&gt;WebSockets&lt;/strong&gt; come in.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore how** WebSockets** work, how connections are established and managed in Go using the &lt;strong&gt;Gorilla WebSocket&lt;/strong&gt; library, and how to safely handle multiple concurrent clients with &lt;strong&gt;Go’s concurrency primitives&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We’ll build on a simple Go project using only the standard net/http router and Gorilla WebSocket.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a WebSocket?
&lt;/h2&gt;

&lt;p&gt;A WebSocket is a full-duplex communication channel over a single TCP connection. Once established, both client and server can send messages to each other anytime without re-establishing the connection.&lt;/p&gt;

&lt;p&gt;Unlike HTTP:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WebSockets stay open until one side closes it.&lt;/li&gt;
&lt;li&gt;Communication happens through lightweight frames, not HTTP requests.&lt;/li&gt;
&lt;li&gt;Data can flow in both directions independently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How a WebSocket Connection is Formed?&lt;br&gt;
The WebSocket handshake starts as a standard HTTP GET request with an Upgrade header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /ws HTTP/1.1
Host: localhost:8080
Upgrade: websocket
Connection: Upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the server accepts it, it responds with a 101 Switching Protocols status, upgrading the connection to WebSocket. From that point, the connection is no longer HTTP, it’s a persistent socket.&lt;/p&gt;

&lt;p&gt;In Go, this upgrade is handled by Gorilla’s Upgrader:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin:     func(r *http.Request) bool { return true },
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CheckOrigin function lets us control which clients can connect. Setting it to always return true allows all origins, good for development, not for production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connection Establishment and Storage
&lt;/h2&gt;

&lt;p&gt;When a client connects to /ws, the handler upgrades the request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
    log.Println("Upgrade error:", err)
    return
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once upgraded, conn is a *websocket.Conn, which represents an open connection to a client.&lt;br&gt;
We store each connection in memory along with the client’s username (sent in the request header):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mu.Lock()
Connections = append(Connections, map[string]*websocket.Conn{username: conn})
mu.Unlock()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use a global slice of maps to keep track of all connected users.&lt;br&gt;
A sync.Mutex ensures that concurrent reads and writes to the Connections slice are thread-safe, since multiple clients can connect or disconnect at the same time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Reading and Writing Messages
&lt;/h2&gt;

&lt;p&gt;After establishing the connection, the server starts listening for messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for {
    messageType, msg, err := conn.ReadMessage()
    if err != nil {
        log.Printf("[%s] read error: %v", username, err)
        break
    }

    log.Printf("[%s] says: %s", username, msg)

    resp := fmt.Sprintf("Response to %s: %s", username, msg)
    conn.WriteMessage(messageType, []byte(resp))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ReadMessage() blocks until a new message arrives. When it does, it returns the message type, the payload, and any error.&lt;/p&gt;

&lt;p&gt;The messageType is important because WebSockets can carry different kinds of messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebSocket Message Types
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;TextMessage (1): UTF-8 encoded text.&lt;/li&gt;
&lt;li&gt;BinaryMessage (2): Arbitrary binary data.&lt;/li&gt;
&lt;li&gt;PingMessage (9): Sent by one peer to check if the connection is alive.&lt;/li&gt;
&lt;li&gt;PongMessage (10): Sent automatically in response to a ping.&lt;/li&gt;
&lt;li&gt;CloseMessage (8): Sent when either side wants to terminate the connection.
Most applications only use text and binary frames, but ping/pong frames are crucial for keeping the connection healthy.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Detecting and Closing Idle Connections
&lt;/h2&gt;

&lt;p&gt;In a real-world system, we don’t want idle connections hanging around forever.&lt;br&gt;
We can use SetReadDeadline() to close connections that haven’t received messages for a specific time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const idleTimeout = 60 * time.Second
conn.SetReadDeadline(time.Now().Add(idleTimeout))
conn.SetPongHandler(func(string) error {
    conn.SetReadDeadline(time.Now().Add(idleTimeout))
    return nil
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means:&lt;/p&gt;

&lt;p&gt;The server expects a message or pong frame every 60 seconds.&lt;br&gt;
Each time one arrives, the deadline resets.&lt;br&gt;
If the timer expires without activity, ReadMessage() returns a timeout error, and we close the connection.&lt;br&gt;
This is how we automatically disconnect idle clients while keeping active ones alive.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cleaning Up Disconnected Clients
&lt;/h2&gt;

&lt;p&gt;When a connection closes, we must remove it from our global store to prevent memory leaks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func removeConnection(username string) {
    mu.Lock()
    defer mu.Unlock()

    var updated []map[string]*websocket.Conn
    for _, m := range Connections {
        if _, ok := m[username]; !ok {
            updated = append(updated, m)
        }
    }
    Connections = updated
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures that only active connections remain in memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending Messages to Specific Clients
&lt;/h2&gt;

&lt;p&gt;Sometimes, you need to send data to a particular user. We can look up the connection by username and send directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func SendMessageToSpecificUser(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    targetUser := string(body)

    mu.Lock()
    defer mu.Unlock()

    for _, item := range Connections {
        for username, conn := range item {
            if username == targetUser {
                conn.WriteMessage(websocket.TextMessage, []byte("hi "+username))
                log.Printf("Sent message to %s", username)
                w.Write([]byte("sent"))
                return
            }
        }
    }
    w.Write([]byte("user not found"))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can easily be extended to support broadcasting messages to all clients or to a subset (like chat rooms).&lt;/p&gt;

&lt;h2&gt;
  
  
  Concurrency and Multithreading in WebSocket Handling
&lt;/h2&gt;

&lt;p&gt;Each HTTP request in Go runs in its own goroutine.&lt;br&gt;
That means every WebSocket connection gets a dedicated goroutine when the handler runs:&lt;br&gt;
&lt;code&gt;mux.HandleFunc("/ws", handlers.WebsocketHandler)&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inside that goroutine:&lt;/li&gt;
&lt;li&gt;The for loop reading messages is blocking, but isolated to that connection.&lt;/li&gt;
&lt;li&gt;sync.Mutex ensures shared data structures (like Connections) remain safe when multiple goroutines modify them simultaneously.&lt;/li&gt;
&lt;li&gt;The Go scheduler handles concurrency efficiently, thousands of WebSocket connections can run in parallel on a single server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This concurrency model is what makes Go such a great fit for real-time applications.&lt;/p&gt;
&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;Let’s walk through building and testing the complete WebSocket setup from scratch.&lt;/p&gt;
&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;Create your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir websocket-chat-appp
cd websocket-chat-appp
go mod init websocket-chat-appp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install Gorilla WebSocket:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go get github.com/gorilla/websocket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Project structure:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;websocket-chat-app/
│── go.mod
├── main.go
└── handlers/
    └── websocket_handlers.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;main.go&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "log"
    "net/http"
    "websocket-chat-app/handlers"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/ws", handlers.WebsocketHandler)
    mux.HandleFunc("/show", handlers.PrintConnections)
    mux.HandleFunc("POST /send", handlers.SendMessageToSpecificUser)

    log.Printf("Server started on :8080")
    http.ListenAndServe(":8080", mux)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;handlers/websocket_handlers.go&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
## package handlers

import (
    "fmt"
    "io"
    "log"
    "net/http"
    "sync"
    "time"

    "github.com/gorilla/websocket"
)

// Global variables to store all active connections
var (
    Connections []map[string]*websocket.Conn
    mu          sync.Mutex
)

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin:     func(r *http.Request) bool { return true },
}

// WebsocketHandler handles new WebSocket connections
func WebsocketHandler(w http.ResponseWriter, r *http.Request) {
    username := r.URL.Query().Get("username")
    if username == "" {
        http.Error(w, "username query param required", http.StatusBadRequest)
        return
    }

    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println("Upgrade error:", err)
        return
    }

    mu.Lock()
    Connections = append(Connections, map[string]*websocket.Conn{username: conn})
    mu.Unlock()

    log.Printf("[%s] connected", username)

    go handleConnection(username, conn)
}

// handleConnection reads messages and handles connection lifecycle
func handleConnection(username string, conn *websocket.Conn) {
    defer func() {
        removeConnection(username)
        conn.Close()
        log.Printf("[%s] disconnected", username)
    }()

    const idleTimeout = 60 * time.Second
    conn.SetReadDeadline(time.Now().Add(idleTimeout))
    conn.SetPongHandler(func(string) error {
        conn.SetReadDeadline(time.Now().Add(idleTimeout))
        return nil
    })

    for {
        msgType, msg, err := conn.ReadMessage()
        if err != nil {
            log.Printf("[%s] read error: %v", username, err)
            break
        }

        log.Printf("[%s] says: %s", username, msg)
        resp := fmt.Sprintf("Response to %s: %s", username, msg)
        conn.WriteMessage(msgType, []byte(resp))
    }
}

// removeConnection cleans up disconnected clients
func removeConnection(username string) {
    mu.Lock()
    defer mu.Unlock()

    var updated []map[string]*websocket.Conn
    for _, m := range Connections {
        if _, ok := m[username]; !ok {
            updated = append(updated, m)
        }
    }
    Connections = updated
}

// PrintConnections displays all connected usernames
func PrintConnections(w http.ResponseWriter, _ *http.Request) {
    mu.Lock()
    defer mu.Unlock()

    fmt.Fprintln(w, "Connected users:")
    for _, conn := range Connections {
        for username := range conn {
            fmt.Fprintln(w, "-", username)
        }
    }
}

// SendMessageToSpecificUser sends a message to a specific user
func SendMessageToSpecificUser(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    targetUser := string(body)

    mu.Lock()
    defer mu.Unlock()

    for _, item := range Connections {
        for username, conn := range item {
            if username == targetUser {
                conn.WriteMessage(websocket.TextMessage, []byte("hi "+username))
                log.Printf("Sent message to %s", username)
                w.Write([]byte("sent"))
                return
            }
        }
    }
    w.Write([]byte("user not found"))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running the Server
&lt;/h2&gt;

&lt;p&gt;Start the Go server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go run main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2025/10/09 22:10:41 Server started on:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing with Postman
&lt;/h2&gt;

&lt;p&gt;Now, let's see how our app works.&lt;br&gt;
Open Postman, then make a WebSocket connection to localhost:8080/ws. Here I am using passing "ashis" as the username, in the header.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xhpkziucvygymocs9e0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xhpkziucvygymocs9e0.jpg" alt=" " width="800" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's make another WebSocket request. Here I am using "barbie" as the username in the header.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffjxz6br3abo52xm7jkad.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffjxz6br3abo52xm7jkad.jpg" alt=" " width="800" height="327"&gt;&lt;/a&gt;&lt;br&gt;
Let's see the connected users using HTTP REST API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9nh5bzd9kkgj0sn08c65.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9nh5bzd9kkgj0sn08c65.jpg" alt=" " width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now I am sending messages from the connection "ashis"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdumj3tksthfgh267g545.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdumj3tksthfgh267g545.jpg" alt=" " width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But I'm not sending any messages from "barbie". So the connection is closed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgcwgvuws6gr0k4yl6a49.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgcwgvuws6gr0k4yl6a49.jpg" alt=" " width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, establish both connections again and send a message to a specific user. I am sending a message to "barbie".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnhdlcnhun3dz3t853f21.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnhdlcnhun3dz3t853f21.jpg" alt=" " width="800" height="352"&gt;&lt;/a&gt;&lt;br&gt;
Let's see if barbie received the message or not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1rztusawdync642smi80.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1rztusawdync642smi80.jpg" alt=" " width="800" height="331"&gt;&lt;/a&gt;&lt;br&gt;
As you can see, barbie received the message successfully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Here’s what we covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How WebSockets work and how they differ from HTTP&lt;/li&gt;
&lt;li&gt;How to establish and upgrade a connection using Gorilla WebSocket&lt;/li&gt;
&lt;li&gt;Message types and how to read/write frames&lt;/li&gt;
&lt;li&gt;How to detect idle clients using SetReadDeadline and close them cleanly&lt;/li&gt;
&lt;li&gt;Storing and managing connections safely with sync.Mutex&lt;/li&gt;
&lt;li&gt;Concurrency in Go each WebSocket runs in its own goroutine&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>websocket</category>
      <category>softwareengineering</category>
      <category>go</category>
    </item>
    <item>
      <title>From concept to code: Building an algorithm visualizer with React and Next.js.</title>
      <dc:creator>H.M.Ashis Rahman</dc:creator>
      <pubDate>Tue, 02 Sep 2025 17:12:28 +0000</pubDate>
      <link>https://forem.com/ashisrahman/from-concept-to-code-building-an-algorithm-visualizer-with-react-and-nextjs-1ak0</link>
      <guid>https://forem.com/ashisrahman/from-concept-to-code-building-an-algorithm-visualizer-with-react-and-nextjs-1ak0</guid>
      <description>&lt;p&gt;I used to find studying algorithms a real struggle. The textbooks were dry, and I just couldn't visualize what was happening with the data. To solve this, I decided to build my own interactive algorithm visualizer. I chose React and Next.js for the project, and it turned out to be one of the best ways I've ever learned.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ashisrahman.dev/" rel="noopener noreferrer"&gt;https://ashisrahman.dev/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>reactjsdevelopment</category>
    </item>
  </channel>
</rss>
