when does Go http.Get reuse the tcp connection? - sockets

in GO net/http Response Body annotation says:
It is the caller's responsibility to close Body. The default HTTP client's Transport does not attempt to reuse HTTP/1.0 or HTTP/1.1 TCP connections ("keep-alive") unless the Body is read to completion and is
closed.
It's mean: if I use http.Get and don't call resp.Body.Close() then it will not resue HTTP/1.0 or HTTP/1.1 TCP connections ("keep-alive") yeah?
so I write some code:
package main
import (
"time"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
resp, err := http.Get("http://127.0.0.1:8588")
if err != nil {
panic(err)
}
_, err = ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
resp2, err := http.Get("http://127.0.0.1:8588")
if err != nil {
panic(err)
}
_, err = ioutil.ReadAll(resp2.Body)
if err != nil {
panic(err)
}
fmt.Println("before time sleep")
time.Sleep(time.Second * 35)
}
and I only see ONE tcp connection build in wireshark, why?
I don't close res.Body so the http client should't be reuse the tcp connection.

this problem has been solved in
https://github.com/golang/go/issues/22954.

You have read it till the end in first occurence of line:
_, err = ioutil.ReadAll(resp.Body)
So the connection is ready to be resused. Try not to read and run again.

Related

RunTimeError: invalid memory adress or nil pointer deference in golang mailer

Im Using https://github.com/kataras/go-mailer and https://github.com/gin-gonic/gin
I somehow get this error:
[GIN] 2022/02/19 - 13:23:24 | 500 | 10.211394ms | ::1 | POST "/new-code?userid=kiddo&email=POG_CHAMP_EMAIL!"
Error Occuredcontext deadline exceeded
2022/02/19 13:23:35 [Recovery] 2022/02/19 - 13:23:35 panic recovered:
POST /new-code?userid=kiddo&email=HI_EMAIL_HERE? HTTP/1.1
Host: localhost:8080
Accept: */*
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 0
Postman-Token: 07942c52-c661-46e9-a4c0-4eeb3c5bb6d1
User-Agent: PostmanRuntime/7.28.4
runtime error: invalid memory address or nil pointer dereference
/usr/local/go/src/runtime/panic.go:221 (0x104a406)
panicmem: panic(memoryError)
/usr/local/go/src/runtime/signal_unix.go:735 (0x104a3d6)
sigpanic: panicmem()
/Users/gaurish/Desktop/Coding/SayHeyToMe-Emails/main.go:73 (0x169bfc7)
main.func1: "code": Insert.InsertedID,
/Users/gaurish/go/pkg/mod/github.com/gin-gonic/gin#v1.7.7/context.go:168 (0x14da441)
(*Context).Next: c.handlers[c.index](c)
/Users/gaurish/go/pkg/mod/github.com/gin-gonic/gin#v1.7.7/recovery.go:99 (0x14da42c)
CustomRecoveryWithWriter.func1: c.Next()
/Users/gaurish/go/pkg/mod/github.com/gin-gonic/gin#v1.7.7/context.go:168 (0x14d96a6)
(*Context).Next: c.handlers[c.index](c)
/Users/gaurish/go/pkg/mod/github.com/gin-gonic/gin#v1.7.7/logger.go:241 (0x14d9689)
LoggerWithConfig.func1: c.Next()
/Users/gaurish/go/pkg/mod/github.com/gin-gonic/gin#v1.7.7/context.go:168 (0x14d8bf0)
(*Context).Next: c.handlers[c.index](c)
/Users/gaurish/go/pkg/mod/github.com/gin-gonic/gin#v1.7.7/gin.go:555 (0x14d8858)
(*Engine).handleHTTPRequest: c.Next()
/Users/gaurish/go/pkg/mod/github.com/gin-gonic/gin#v1.7.7/gin.go:511 (0x14d8391)
(*Engine).ServeHTTP: engine.handleHTTPRequest(c)
/usr/local/go/src/net/http/server.go:2879 (0x12c393a)
serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
/usr/local/go/src/net/http/server.go:1930 (0x12befe7)
(*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req)
/usr/local/go/src/runtime/asm_amd64.s:1581 (0x1065040)
goexit: BYTE $0x90 // NOP
[GIN] 2022/02/19 - 13:23:35 | 500 | 3.428745ms | ::1 | POST "/new-code?userid=kiddo&email=I_HAD_EMAIL_HERE_KEKW?"
Here's mailer code: https://sourceb.in/25I37EgiF8 (It was 500 lines coz of html)
Here's the Router code:
package main
import (
"context"
"fmt"
"time"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
func ping(client *mongo.Client, ctx context.Context) error {
if err := client.Ping(ctx, readpref.Primary()); err != nil {
return err
}
fmt.Println("[MONGODB] Connected Successfully")
return nil
}
func connect(uri string) (*mongo.Client, context.Context,
context.CancelFunc, error) {
ctx, cancel := context.WithTimeout(context.Background(),
30*time.Second)
client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri))
return client, ctx, cancel, err
}
func close(client *mongo.Client, ctx context.Context,
cancel context.CancelFunc) {
defer cancel()
defer func() {
if err := client.Disconnect(ctx); err != nil {
panic(err)
}
}()
}
func main() {
r := gin.Default()
client, ctx, cancel, err := connect("MONGODB>>>>>>")
if err != nil {
panic(err)
}
defer close(client, ctx, cancel)
ping(client, ctx)
Database := client.Database("codes")
CodesCollection := Database.Collection("Codes")
r.POST("/new-code", func(c *gin.Context) {
email := c.Query("email")
Insert, err := CodesCollection.InsertOne(ctx, bson.D{
{
Key: "userid",
Value: c.Query("userid"),
},
{
Key: "used",
Value: false,
},
{
Key: "expired",
Value: time.Now().Add(time.Hour * 6).Unix(),
},
})
if err != nil {
fmt.Print("Error Occured")
fmt.Print(err.Error())
}
c.JSON(200, gin.H{
"success": true,
"code": Insert.InsertedID,
})
EmailUrl := "https://example.com"
SendMail(email, EmailUrl)
})
r.Run()
}
What should i be doing? I'm using postman and that doesn't seem to a problem tho, Till yesterday i was just using it without the mail system and it worked properly, So probably won't be a mongodb problem at all.
For some reason, the line here returns an error. So the Insert is nil.
Insert, err := CodesCollection.InsertOne(...)
That's why when you are calling Insert.InsertedID here,
c.JSON(200, gin.H{
"success": true,
"code": Insert.InsertedID,
})
The call to Insert.InsertedID is panicing with nil pointer reference exception.
To fix please add return to your error handling snippet to stop execution further:
if err != nil {
fmt.Print("Error Occured")
fmt.Print(err.Error())
return
}

Go Mongo DB doesn't connect

I'm trying to connect to my mongo db atlas database, but I'm getting an error that I can't solve it within mongo+go forums.
The code:
package main
import (
"context"
"log"
"time"
"fmt"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
"go.mongodb.org/mongo-driver/bson"
)
func main(){
client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://my-user:<my-pass>#datalake0-lesz0.a.query.mongodb.net/my-db?ssl=true&authSource=admin"))
if err != nil {
log.Fatal(err)
}
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
err = client.Connect(ctx)
if err != nil {
log.Fatal(err)
}
defer client.Disconnect(ctx)
err = client.Ping(ctx, readpref.Primary())
if err != nil {
log.Fatal(err)
}
databases, err := client.ListDatabaseNames(ctx, bson.M{})
if err != nil {
log.Fatal(err)
}
fmt.Println(databases)
}
The error:
connection() error occured during connection handshake: auth error: sasl conversation error: unable to authenticate using mechanism "SCRAM-SHA-1": (AuthenticationFailed) authentication failed, correlationID = 167bc5ba18415510a4144b7a
exit status 1
If the URI in the connect string is verbatim, then this is your problem:
mongodb://my-user:<my-pass> is incorrect, <my-pass> should be substituted with your password
You've probably cut and pasted the provided URI on completion of DB setup but this connect string does not include items like your password, also you gave my-user which, unless you set up the username my-user that also needs changing.

Intermittent error getsockopt: connection refused error on Http Post

There are two go apps, one is stapi listening on port 8050 and providing RESTful APIs, another is client to consume those APIs.
Both are running on different servers, client is often getting error when calling APIs with HTTP POST method. Below are few lines from client log (real IP replaced with imaginary one)
2018/02/17 11:42:58 ERROR: [DoLogin] API Error: [Post https://123.123.123.123:8050/v1/st/verifyuser: dial tcp 123.123.123.123:8050: getsockopt: connection refused]
2018/02/17 11:47:14 ERROR: [CreateAttempt] Error: [Post https://123.123.123.123:8050/v1/userattempts/createattempt: dial tcp 123.123.123.123:8050: getsockopt: connection refused]
It is intermittent and making the app unreliable, out of approx 1k request i got such error for approx 50+ request.
Initially stapi was listening on all IPs
httpSrv := http.Server{
Addr: ":8050",
Handler: router, // < gin router
...
}
But after reading the workaroung in Golang HTTP Post error: connection refused i modified the stapi app and make it listening on different IPs, as shown below
$ sudo lsof -i -P -n | grep LISTEN
stapi 4775 samtech 10u IPv4 2388179 0t0 TCP 123.123.123.123:8050 (LISTEN)
stapi 4775 samtech 11u IPv6 2388181 0t0 TCP [::1]:8050 (LISTEN)
stapi 4775 samtech 12u IPv4 2388183 0t0 TCP 127.0.0.1:8050 (LISTEN)
But still the issue is same, what else i should check and fix ? Please suggest.
API is protected with JWT, here is how client is making POST requests
func (w *OST) DoLogin(c *gin.Context) {
...
ud := stapimodels.UserLogin{}
err := c.BindJSON(&ud)
...
//call api to save user response
url := config.AppConfig.APIBaseURL + "st/verifyuser"
res, err := api.JwtApi.APIPost(url, &ud)
if err != nil {
g.Logger.Errorm("DoLogin", "Error: %v", err)
t.Error("Error", err.Error())
return
}
...
}
//APIPost - call given apiurl with POST method and pass data
func (j *JwtAPI) APIPost(apiurl string, postdata interface{}) (*APIResult, error) {
if postdata == nil {
return nil, fmt.Errorf("postdata is nil")
}
jsondata, err := toJSON(postdata)
if err != nil {
return nil, err
}
resp, err := j.makeRequest(http.MethodPost, apiurl, jsondata)
if err != nil {
return nil, err
}
defer resp.Body.Close()
res := APIResult{}
json.NewDecoder(resp.Body).Decode(&res)
return &res, nil
}
//makeRequest makes http request for given url with given method
// also inject Authorization Header
func (j *JwtAPI) makeRequest(method, apiurl string, body io.Reader) (*http.Response, error) {
retry := 0
//Create []byte buffer from body - so it can be passed in further retries
var buf []byte
if body != nil {
buf, _ = ioutil.ReadAll(body)
}
r, err := http.NewRequest(method, apiurl, bytes.NewReader(buf))
if err != nil {
return nil, err
}
r.Header.Set("Authorization", "bearer "+j.token.AccessToken)
r.Header.Set("Content-Type", "application/json")
client := j.getClient()
resp, err := client.Do(r)
if err != nil {
return nil, err
}
return resp, nil
}
func (j *JwtAPI) getClient() *http.Client {
// default timeout (if not set by client)
timeoutInSec := 10
if j.Timeout.Seconds() > 0 {
// client sets timeout, so use it
timeoutInSec = int(j.Timeout.Seconds())
}
client := &http.Client{
Timeout: time.Second * time.Duration(timeoutInSec),
}
return client
}
To make your code more resilient you should add some retries with back-offs, so even when the connection was refused it is still working.
Connection refused means that the port is not opened. Is there any firewall or proxies in between? The authentication part shouldn't matter here because it doesn't even get to this point.
Some things that you can check:
Make sure the service is running
Check for firewall configuration
Implement retries for resilience
Is the IP-Address fixed? Is Dynamic DNS used and maybe not updated?
Package for back-off retrying
As for implementing the back-off you might try this package:
https://github.com/cenkalti/backoff
It is listing examples on how to use it and it's pretty much exactly what you need:
// An operation that may fail.
operation := func() error {
// do the request here and check the response code (or check the response body depending on your need) . e.g. above 500 should retry, above 400 and below 500, it should be a client side error and retrying might not help much
return nil // or an error
}
err := Retry(operation, NewExponentialBackOff())
if err != nil {
// Handle error.
return
}
// Operation is successful.

SSL Socket client in Golang

I'm trying to connect to a server through a socket using an ssl certificate. I have the private key and the certificate needed to connect to the server, i have looked around for something to help me write this code in Go but i have not found anything helpful. Here is the code i write so far, but it does not seem to get me anywhere, it seems to be sending data(tls.dial) before getting to the actual data, which forces the server to reply with some encrypted data which i am unable to check.
func main() {
cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
config := tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}
conn, err := tls.Dial("tcp", "1.2.3.4:1234", &config)
if err != nil {
log.Fatalf("client: dial: %s", err)
}
defer conn.Close()
log.Println("client: connected to: ", conn.RemoteAddr())
state := conn.ConnectionState()
for _, v := range state.PeerCertificates {
fmt.Println(x509.MarshalPKIXPublicKey(v.PublicKey))
fmt.Println(v.Subject)
}
log.Println("client: handshake: ", state.HandshakeComplete)
log.Println("client: mutual: ", state.NegotiatedProtocolIsMutual)
message := "data"
n, err := io.WriteString(conn, message)
if err != nil {
log.Fatalf("client: write: %s", err)
}
log.Printf("client: wrote %q (%d bytes)", message, n)
reply := make([]byte, 256)
n, err = conn.Read(reply)
log.Printf("client: read %q (%d bytes)", string(reply[:n]), n)
log.Print("client: exiting")
}

socket programming, getting getsockopt: connection refused

I'm having trouble with socket programming.
I have a program that reads from localhost:7777 and writes to localhost:8000.
I use netcat from the command line to write and read to 7777 and 8000 respectively.
This is the reader:
netcat -l -p 8000
And this is the writer:
printf "asti||" | netcat localhost 7777
But my program gets network errors when it tries to write to port 8000 for the second
time. The error is Fatal error: dial tcp 127.0.0.1:8000: getsockopt: connection refused.
What's happening? Why on the second write the error appears?
Furthermore, I noticed that if I kill the netcat reader and restart it then there's no network errors. So to reiterate, the program writes once to 8000 and netcat reads it. Then I kill netcat reader and restart it. At this point the program can write again to 8000. But if the program tries to write two successive times to 8000 without me restarting netcat, then the error appears.
Here is the entire program (it's short). If you like, you experience this mystical behaviour yourself:
package main
import (
"fmt"
"net"
"os"
"strings"
// "io/ioutil"
)
func main() {
end_of_message_terminator := "||"
beginning_of_next_message := ""
request := make([]byte, 512)
service_port := ":7777"
tcpAddr, err := net.ResolveTCPAddr("tcp4", service_port)
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
conn, err := listener.Accept()
if err != nil {
continue
}
read_len, err := conn.Read(request)
if read_len == 0 {
continue
}
request_string := string(request[:read_len])
fmt.Printf("Request String %s\\END", request_string)
messages := strings.Split(request_string, end_of_message_terminator)
fmt.Printf("%q\n", messages)
messages[0] = beginning_of_next_message + messages[0]
if messages[len(messages) - 1] != "" {
beginning_of_next_message = messages[len(messages) - 1]
messages[len(messages) - 1] = ""
fmt.Printf("was here 00\n")
}
if len(messages) == 1 {
continue
}
for i := range messages {
go func(){
fmt.Printf("was here 04\n")
respond_to_message(messages[i])
}()
fmt.Printf("was here 01\n")
}
conn.Close()
}
}
func respond_to_message(message string){
message_parameters := strings.Split(message, "|")
response_port := "localhost:8000"
tcpAddr_res, err := net.ResolveTCPAddr("tcp4", response_port)
checkError(err)
response_writer, err := net.DialTCP("tcp", nil, tcpAddr_res)
for i := range message_parameters {
fmt.Printf("was here03\n")
param_parts := strings.Split(message_parameters[i], "=")
fmt.Printf("message: %s\n", message)
fmt.Printf("message_parameters%q\n", message_parameters)
fmt.Printf("params_parts: %q\n", param_parts)
//param_name := param_parts[0]
//param_value := param_parts[1]
checkError(err)
response_writer.Write([]byte("asti de crhis"))
checkError(err)
//result, err := ioutil.ReadAll(response_writer)
//checkError(err)
//fmt.Println(string(result))
}
//response_writer.Close()
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)
}
}