DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Key Takeaways From Integrating a RAG Application With LangSmith
  • Improving Java Application Reliability with Dynatrace AI Engine
  • Why Your "Stateless" Services Are Lying to You
  • Enabling Single-Sign-On in SaaS Application

Trending

  • Detecting Bugs and Vulnerabilities in Java With SonarQube
  • Integrating AI-Driven Decision-Making in Agile Frameworks: A Deep Dive into Real-World Applications and Challenges
  • A Deep Dive into Tracing Agentic Workflows (Part 1)
  • Skills, Java 17, and Theme Accents
  1. DZone
  2. Software Design and Architecture
  3. Performance
  4. Transforming TCP Sockets to HTTP With Go

Transforming TCP Sockets to HTTP With Go

Use Go to help your apps communicate!

By 
Gonzalo Ayuso user avatar
Gonzalo Ayuso
·
May. 02, 21 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
11.9K Views

Join the DZone community and get the full member experience.

Join For Free

Sometimes, we need to work with legacy applications, and legacy applications can be hard to rewrite and change. Imagine, for example, we have an application that is using raw TCP sockets to communicate with another process. Raw TCP sockets are fast, but they have various problems. For example, all data is sent in plain text over the network and without authentication (if we don’t implement a protocol).

One solution is to use HTTPS connections instead. We can also authenticate those requests with an Authentication Bearer. For example, I’ve created a simple HTTP server with Python and Flask:

Python
xxxxxxxxxx
1
38
 
1
import logging
2
import os
3
from functools import wraps
4
 
5
from flask import Flask, request, abort
6
from flask import jsonify
7
 
8
logging.basicConfig(level=logging.DEBUG)
9
 
10
logger = logging.getLogger(__name__)
11
app = Flask(__name__)
12
 
13
 
14
def authorize_bearer(bearer):
15
    def authorize(f):
16
        @wraps(f)
17
        def decorated_function(*args, **kws):
18
            if 'Authorization' not in request.headers:
19
                abort(401)
20
 
21
            data = request.headers['Authorization']
22
 
23
            if str.replace(str(data), 'Bearer ', '') != bearer:
24
                abort(401)
25
 
26
            return f(*args, **kws)
27
 
28
        return decorated_function
29
 
30
    return authorize
31
 
32
 
33
@app.route('/register', methods=['POST'])
34
@authorize_bearer(bearer=os.getenv('TOKEN'))
35
def hello_world():
36
    req_data = request.get_json()
37
    logger.info(req_data)
38
    return jsonify({"status": "OK", "request_data": req_data})


Now we only need to change our legacy application to use one HTTP client instead of raw TCP sockets. But sometimes, that's not possible. Imagine, for example, if this application runs on an old OS without HTTPS support or we cannot find and compile an HTTP client in the legacy application.

One possible solution is to isolate the application and change only the destination of the TCP socket. Instead of the original IP address, we can use localhost. and we can create a proxy at localhost that listens to TCP sockets and sends the information to the HTTP server.

We’re going to build this proxy in Go, but we can do it with any language (Python, C#, JavaScript, …). My Kung Fu in Go is not so good (I’m more comfortable with Python) but it’s not very difficult, and we can build a binary with our proxy for Windows, Linux, and Mac without any problems. Then, we only need to copy the binary into the target host, and it works (no installation, no SDK, nothing. Just copy and run).

Go
 




x
96


 
1
package main
2
 
3
import (
4
    "bufio"
5
    "encoding/json"
6
    "flag"
7
    "log"
8
    "net"
9
    "net/http"
10
    "os"
11
    "strings"
12
)
13
 
14
func main() {
15
    port, closeConnection, url := parseFlags()
16
    openSocket(*port, *closeConnection, *url, onMessage)
17
}
18
 
19
func onMessage(url string, buffer string) {
20
    bearer := os.Getenv("TOKEN")
21
    client := &http.Client{}
22
    req, _ := http.NewRequest("POST", url, strings.NewReader(buffer))
23
    req.Header.Add("Authorization", "Bearer "+bearer)
24
    req.Header.Add("content-type", "application/json")
25
    resp, err := client.Do(req)
26
 
27
    if err != nil {
28
        log.Println(err)
29
    } else {
30
        if resp.Status == "200" {
31
            var result map[string]interface{}
32
            json.NewDecoder(resp.Body).Decode(&result)
33
            log.Println(result["status"])
34
        } else {
35
            log.Println("Response status: " + resp.Status)
36
        }
37
        defer resp.Body.Close()
38
    }
39
}
40
 
41
func parseFlags() (*string, *bool, *string) {
42
    port := flag.String("port", "7777", "port number")
43
    closeConnection := flag.Bool("close", true, "Close connection")
44
    url := flag.String("url", "http://localhost:5000/register", "Destination endpoint")
45
    flag.Parse()
46
    return port, closeConnection, url
47
}
48
 
49
func openSocket(port string, closeConnection bool, url string, onMessage func(url string, buffer string)) {
50
    PORT := "localhost:" + port
51
    l, err := net.Listen("tcp4", PORT)
52
    log.Printf("Serving %s\n", l.Addr().String())
53
    if err != nil {
54
        log.Fatalln(err)
55
    }
56
    defer l.Close()
57
 
58
    for {
59
        c, err := l.Accept()
60
        if err != nil {
61
            log.Fatalln(err)
62
        }
63
        go handleConnection(c, closeConnection, url, onMessage)
64
    }
65
}
66
 
67
func handleConnection(c net.Conn, closeConnection bool, url string, onMessage func(url string, buffer string)) {
68
    log.Printf("Accepted connection from %s\n", c.RemoteAddr().String())
69
    for {
70
        ip, port, err := net.SplitHostPort(c.RemoteAddr().String())
71
        netData, err := bufio.NewReader(c).ReadString('\n')
72
        if err != nil {
73
            log.Println(err)
74
        }
75
 
76
        message := map[string]interface{}{
77
            "body":   strings.TrimSpace(netData),
78
            "ipFrom": ip,
79
            "port":   port,
80
        }
81
 
82
        log.Printf("Making request with %s\n", message)
83
        bytesRepresentation, err := json.Marshal(message)
84
        if err != nil {
85
            log.Println(err)
86
        } else {
87
            //buffer := bytes.NewBuffer(bytesRepresentation)
88
            onMessage(url, string(bytesRepresentation))
89
        }
90
 
91
        if closeConnection {
92
            c.Close()
93
            return
94
        }
95
    }
96
    c.Close()



And that’s all. We can upgrade our legacy application almost without changing the code.

The source code is available in my GitHub.

Transmission Control Protocol application

Published at DZone with permission of Gonzalo Ayuso. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Key Takeaways From Integrating a RAG Application With LangSmith
  • Improving Java Application Reliability with Dynatrace AI Engine
  • Why Your "Stateless" Services Are Lying to You
  • Enabling Single-Sign-On in SaaS Application

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook