2

I would like to implement an HTTP request and response system of this for

client ------> A ----------> B --------> (request) HTTPserver
client <------ A <---------- B <-------- (response) HTTPserver
  1. client send HTTP request with any HTTP method (POST,PUT, etc) to A
  2. A then reads the request body encrypt it and then forward it to B 3 B then decrypt the reads the HTTP request body
  3. B then with the received decrypt body as payload prepare an HTTP request and and send to the HTTP server. 5 The HTTP server then respond to B.
  4. B then encrypt the response from the HTTP server and then forward to A.
  5. A also decrypt the response from B and then and send the response back to the client.

I have implements the following base on earlier suggestions.

ProxyA:

const (
  ServerB = "<address of B>"
  Port = "<proxy A port>"
)

func main() {
  // start server
  http.HandleFunc("/", proxyPass)
  log.Fatal(http.ListenAndServe(":" + Port, nil))
}

func proxyPass(res http.ResponseWriter, req  *http.Request) {
 
 // read request body from client
bodybytes, _ := ioutil.ReadAll(req.Body)

defer req.Body.Close()

// encrypt req.Body
object, _ := enc.Encrypt(bodybytes)

// serialize object
serialized, _ := object.CompactSerialize()

// prepare forwarding message
msg := message{reformatedData: serialized}

// encode message 
msgbytes, _ := json.Marshal(&msg)

req.ContentLength = int64(len(msgbytes))
req.Body = ioutil.NopCloser(bytes.NewBuffer(msgbytes))


// How do I read the response data from proxy server B and then send
// response to the client
....
 
  url, _ := url.Parse(ServerB)
  proxy := httputil.NewSingleHostReverseProxy(url)
  proxy.ServeHTTP(res, req)
}

For proxy B:

const (
  Server = "<address of server>"
  Port = "<proxy B port>"
)

func main() {
  // start server
  http.HandleFunc("/", proxyPass)
  log.Fatal(http.ListenAndServe(":" + Port, nil))
}

func proxyPass(res http.ResponseWriter, req  *http.Request) {

  var msg message
  HTTPServerurl := http://xxxxx

  // read request body
  bodybytes, _ := ioutil.ReadAll(req.Body)
  
  req.ContentLength = int64(len(bodybytes))
  req.Body = ioutil.NopCloser(bytes.NewBuffer(bodybytes))


// decode message 
err = json.Unmarshal(bodybytes, &msg)

// decrypt message
object, _ := jose.ParseEncrypted(msg)
decrypted, _ := object.Decrypt("phrasetodecryptmessage")

//send HTTP request to HTTP server
resp, _ := HandlemessageToHTTPServer(decrypted, "POST", HTTPServerurl)

//read response body
RespBody, _ := ioutil.ReadAll(resp.Body)

// encrypt response body
object, _ = enc.Encrypt(producerRespBody)
serialized, _ := object.CompactSerialize()

// prepare response JSON message 
resmsg := resmessage {resmessage: serialized}

// marshall response message 
respmsgbytes, _ := json.Marshal(&resmsg)


// How do I write the "respmsgbytes" to proxyServHTTP "res" back to proxy A


  url, _ := url.Parse(Server)
  proxy := httputil.NewSingleHostReverseProxy(url)
  proxy.ServeHTTP(res, req)
} 

My question is

  1. How do I write the "respmsgbytes" to proxyServHTTP "res" in proxy B back to proxy A ?

  2. How do I read the response data from proxy server B and then send response to the client?

Any help? I have left error checking to make the code short.

van
  • 77
  • 1
  • 6

1 Answers1

4

You can use httputil
You can do something like following.
For proxy A:

const (
  ServerB = "<address of B>"
  Port = "<proxy A port>"
)

func main() {
  // start server
  http.HandleFunc("/", proxyPass)
  log.Fatal(http.ListenAndServe(":" + Port, nil))
}

func proxyPass(res http.ResponseWriter, req  *http.Request) {
  // Encrypt Request here
  // ...

  url, _ := url.Parse(ServerB)
  proxy := httputil.NewSingleHostReverseProxy(url)
  proxy.ServeHTTP(res, req)
} 

For proxy B:

const (
  Server = "<address of server>"
  Port = "<proxy B port>"
)

func main() {
  // start server
  http.HandleFunc("/", proxyPass)
  log.Fatal(http.ListenAndServe(":" + Port, nil))
}

func proxyPass(res http.ResponseWriter, req  *http.Request) {
  // Decrypt Request here
  // ...

  url, _ := url.Parse(Server)
  proxy := httputil.NewSingleHostReverseProxy(url)
  proxy.ServeHTTP(res, req)
} 

EDIT:
To handle the request body at each proxy, you can have a look at this. Alternatively, I think there should be no harm in construction of new req based on current req as following:

func proxyPass(res http.ResponseWriter, req  *http.Request) {
    body, _ := ioutil.ReadAll(req.Body)
    data := string(body)

    // process data here

    req, _ = http.NewRequest(req.Method, req.URL.String(), strings.NewReader(data))
    
    u, _ := url.Parse(Server)
    proxy := httputil.NewSingleHostReverseProxy(u)
    proxy.ServeHTTP(res, req)
}

This can be done at both proxies.

EDIT:
The proxy response can be updated using ReverseProxy.ModifyResponse.
You can use it like this:

func proxyPass(res http.ResponseWriter, req *http.Request) {
    ....
    
    proxy := httputil.NewSingleHostReverseProxy(url)
    
    proxy.ModifyResponse = func(response *http.Response) error {
        // Read and update the response here

        // The response here is response from server (proxy B if this is at proxy A)
        // It is a pointer, so can be modified to update in place
        // It will not be called if Proxy B is unreachable
    }

    proxy.ServeHTTP(res, req)
}
advay rajhansa
  • 1,129
  • 9
  • 17
  • Am getting error: httputil: ReverseProxy read error during body copy: read tcp 127.0.0.1:52484->127.0.0.1:2020: read: connection reset by peer – van Apr 22 '21 at 14:31
  • how are you trying to connect? the error is `connection reset by peer` meaning for some reason, the client seems to send `RST` – advay rajhansa Apr 22 '21 at 14:47
  • Sorry it was my fault to use http.ListenAndServeTLS instead of http.ListenAndServe. – van Apr 22 '21 at 15:19
  • However, after encryption of the request, how do i send it to proxy B? – van Apr 22 '21 at 15:23
  • You'll have to use the request body and update the request itself. I would directly update the current request. – advay rajhansa Apr 22 '21 at 16:09
  • Yes, i have updated the request body and was able to get the data at B. Currently my problem is how to write the encrypted response message from the server back to A. I tried writing to the res as "res.Write([]byte(messagefrom Server))" but i get error mesage: "http: superfluous response.WriteHeader ... " basically its because i cannot write twice to res. Any help on how to do that. – van Apr 23 '21 at 12:00
  • I have edited my post with what i want to do and what i have done so far. Any idea about how to solve my problem? – van Apr 26 '21 at 09:47
  • Yes, it help. I was able to modify the response and get the message at the client. Thanks!! – van Apr 26 '21 at 15:14