Hello,
I’m making an attempt to get an https server working by overwriting the settle for/learn/write strategies within the http module. I’m utilizing go-wolfssl for TLS.
The server will settle for a TLS 1.2 connection, then ship some random knowledge to the consumer connects, then wait for an additional connection.
The difficulty that I’m seeing is that at any time when the server sends giant payloads, it’ll do this efficiently but it surely then can’t ship knowledge to the following consumer that connects (the ship errors out with the TCP error EBADF). If coping with smaller payloads (<30k), the server can ship knowledge efficiently to every consumer that connects. Then as quickly as a big one is shipped, the following transmission fails.
If I disable the GOGC with debug.SetGCPercent(-1)
, the problem goes away and I can ship as many giant payloads as required. From the debugging I’ve finished thus far, this appears like a difficulty with the GO rubbish collector dropping C pointers. go-wolfssl depends on the wolfSSL C library so it makes use of CGO. Does anybody have another concepts or enter?
Code is beneath. See this repo to really run it GitHub – lealem47/go-wolfssl-https-server
Thanks!
package deal most important
import (
"bytes"
"crypto/rand"
"encoding/base64"
"fmt"
log "github.com/sirupsen/logrus"
wolfSSL "github.com/wolfssl/go-wolfssl"
"web"
"web/http"
"os"
"strconv"
"sync"
"time"
)
const defaultPort = "8443"
kind wolfSSLListener struct {
listener web.Listener
ctx *wolfSSL.WOLFSSL_CTX
}
// Settle for waits for and returns the following connection to the listener.
func (cl *wolfSSLListener) Settle for() (web.Conn, error) {
conn, err := cl.listener.Settle for()
if err != nil {
return nil, err
}
fmt.Println("Accepted new connection from:", conn.RemoteAddr())
ssl := wolfSSL.WolfSSL_new(cl.ctx)
if ssl == nil {
fmt.Println("WolfSSL_new Failed")
os.Exit(1)
}
file, err := conn.(*web.TCPConn).File()
if err != nil {
panic(err)
}
fd := file.Fd()
wolfSSL.WolfSSL_set_fd(ssl, int(fd))
ret := wolfSSL.WolfSSL_accept(ssl)
if ret != wolfSSL.WOLFSSL_SUCCESS {
fmt.Println("WolfSSL_accept error ", ret)
} else {
fmt.Println("Shopper Efficiently Linked!")
}
return &wolfSSLConn{
conn: conn,
ssl: ssl,
}, nil
}
// Shut closes the listener, making it cease accepting new connections.
func (cl *wolfSSLListener) Shut() error {
fmt.Println("Closing listener...")
return cl.listener.Shut()
}
// Addr returns the listener's community handle.
func (cl *wolfSSLListener) Addr() web.Addr {
return cl.listener.Addr()
}
kind wolfSSLConn struct {
conn web.Conn
ssl *wolfSSL.WOLFSSL
buffer bytes.Buffer
mu sync.Mutex
closed bool
}
func (w *wolfSSLConn) Learn(b []byte) (int, error) {
log.Infof("Calling learn: %d", len(b))
ret := wolfSSL.WolfSSL_read(w.ssl, b, uintptr(len(b)))
if ret < 0 {
errCode := wolfSSL.WolfSSL_get_error(w.ssl, int(ret))
return 0, fmt.Errorf("learn error: %d", errCode)
}
log.Infof("Learn bytes: %s", string(b[:ret]))
return int(ret), nil
}
func (w *wolfSSLConn) Write(b []byte) (int, error) {
log.Infof("Calling write: %d", len(b))
sz := uintptr(len(b))
ret := wolfSSL.WolfSSL_write(w.ssl, b, sz)
if ret < 0 {
errCode := wolfSSL.WolfSSL_get_error(w.ssl, int(ret))
return 0, fmt.Errorf("write error: %d", errCode)
}
return int(ret), nil
}
func (w *wolfSSLConn) Shut() error {
log.Infof("Closing connection")
wolfSSL.WolfSSL_shutdown(w.ssl)
wolfSSL.WolfSSL_free(w.ssl)
return w.conn.Shut()
}
func (w *wolfSSLConn) LocalAddr() web.Addr {
return w.conn.LocalAddr()
}
func (w *wolfSSLConn) RemoteAddr() web.Addr {
return w.conn.RemoteAddr()
}
func (w *wolfSSLConn) SetDeadline(t time.Time) error {
return w.conn.SetDeadline(t)
}
func (w *wolfSSLConn) SetReadDeadline(t time.Time) error {
return w.conn.SetReadDeadline(t)
}
func (w *wolfSSLConn) SetWriteDeadline(t time.Time) error {
return w.conn.SetWriteDeadline(t)
}
// Handler for producing and base64 encoding 5KB of random knowledge
func randomDataHandler(w http.ResponseWriter, r *http.Request) {
// Get the "measurement" question parameter from the request
sizeParam := r.URL.Question().Get("measurement")
measurement := 500000 // default measurement
// If the "measurement" parameter is offered, convert it to an integer
if sizeParam != "" {
parsedSize, err := strconv.Atoi(sizeParam)
if err != nil || parsedSize <= 0 {
http.Error(w, "Invalid measurement parameter", http.StatusBadRequest)
return
}
measurement = parsedSize
}
// Generate random knowledge of the desired measurement
knowledge := make([]byte, measurement)
_, err := rand.Learn(knowledge)
if err != nil {
http.Error(w, "Couldn't generate random knowledge", http.StatusInternalServerError)
return
}
// Base64 encode the random knowledge
encodedData := base64.StdEncoding.EncodeToString(knowledge)
// Set content material kind and write the base64 encoded knowledge
w.Header().Set("Content material-Kind", "utility/base64")
w.Write([]byte(encodedData))
}
func most important() {
port := defaultPort
// Set logging degree
log.SetLevel(log.InfoLevel)
log.SetFormatter(&log.TextFormatter{
DisableColors: false,
FullTimestamp: true,
})
// Arrange the HTTP server and routes
http.HandleFunc("https://discussion board.golangbridge.org/", randomDataHandler)
CERT_FILE := "./certs/server-cert.pem"
KEY_FILE := "./certs/server-key.pem"
/* Initialize wolfSSL */
wolfSSL.WolfSSL_Init()
/* Create WOLFSSL_CTX with tlsv12 */
ctx := wolfSSL.WolfSSL_CTX_new(wolfSSL.WolfTLSv1_2_server_method())
if ctx == nil {
fmt.Println(" WolfSSL_CTX_new Failed")
os.Exit(1)
}
/* Load server certificates into WOLFSSL_CTX */
ret := wolfSSL.WolfSSL_CTX_use_certificate_file(ctx, CERT_FILE, wolfSSL.SSL_FILETYPE_PEM)
if ret != wolfSSL.WOLFSSL_SUCCESS {
fmt.Println("Error: WolfSSL_CTX_use_certificate Failed")
os.Exit(1)
}
/* Load server key into WOLFSSL_CTX */
ret = wolfSSL.WolfSSL_CTX_use_PrivateKey_file(ctx, KEY_FILE, wolfSSL.SSL_FILETYPE_PEM)
if ret != wolfSSL.WOLFSSL_SUCCESS {
fmt.Println("Error: WolfSSL_CTX_use_PrivateKey Failed")
os.Exit(1)
}
baseListener, err := web.Hear("tcp", ":"+port)
if err != nil {
fmt.Println("Error beginning listener:", err)
return
}
defer baseListener.Shut()
wolfSSLListener := &wolfSSLListener{
listener: baseListener,
ctx: ctx,
}
log.Printf("Server listening on https://localhost:%s", port)
err = http.Serve(wolfSSLListener, nil)
if err != nil {
fmt.Println("Error beginning HTTP server:", err)
}
}