I am trying to send an HTTP request through a TCP/TLS socket in GOLang, but when I try to send it, no matter what the host is, it always returns (bad request 400), so I think it must be something wrong with the way I use to send a request by TCP/TLS, I searched, and I looked for an way to do what I want to do, but no results:
package main
import (
"crypto/rand"
"crypto/tls"
"fmt"
"net"
"strings"
)
func main() {
headers := make(map[string]string)
headers["Accept"] = "application/json"
request := parseRequest("httpbin.org", "/get", "get", headers, nil, "")
println(request)
tcpConn, err := net.Dial("tcp", "httpbin.org:443")
if err != nil {
panic(err)
}
cf := &tls.Config{Rand: rand.Reader, InsecureSkipVerify: true}
ssl := tls.Client(tcpConn, cf)
if err != nil {
panic(err)
}
defer ssl.Close()
n, err := ssl.Write([]byte(request))
if err != nil {
fmt.Println(n, err)
return
}
buf := make([]byte, 1024)
n, err = ssl.Read(buf)
if err != nil {
fmt.Println(n, err)
return
}
println(string(buf[:n]))
}
func parseRequest(host string, path string, method string, headers map[string]string, data map[string]string, rawData string) string {
method = strings.ToUpper(method)
rawRequest := fmt.Sprintf(`%s %s HTTP/1.1\r\n`, strings.ToUpper(method), path)
if method == `GET` && (data != nil || rawData != ``) {
var rawQuery string
if rawData != `` {
rawQuery = rawData
} else {
i := 0
for key, value := range data {
if i == len(data)-1 {
rawQuery += fmt.Sprintf(`%s=%s`, key, value)
} else {
rawQuery += fmt.Sprintf(`%s=%s&`, key, value)
}
i++
}
}
query := fmt.Sprintf(`%s?%s`, path, rawQuery)
rawRequest = fmt.Sprintf(`%s %s HTTP/1.1\r\n`, strings.ToUpper(method), query)
}
var rawHeaders string
for key, value := range headers {
rawHeaders += fmt.Sprintf(`%s: %s\r\n`, key, value)
}
if !strings.Contains(strings.ToLower(rawHeaders), `host`) {
rawRequest += fmt.Sprintf(`Host: %s\r\n`, host)
}
rawRequest += fmt.Sprintf(`%s\r\n`, rawHeaders)
if method == `POST` && (data != nil || rawData != ``) {
if rawData != `` {
rawRequest += rawData
} else {
i := 0
for key, value := range data {
if i == len(data)-1 {
rawRequest += fmt.Sprintf(`%s=%s`, key, value)
} else {
rawRequest += fmt.Sprintf(`%s=%s&`, key, value)
}
i++
}
}
}
return rawRequest
}
In Go, Back quote `
is used to denote a raw string, as per spec:
Raw string literals are character sequences between back quotes, as in `foo`. Within the quotes, any character may appear except back quote. The value of a raw string literal is the string composed of the uninterpreted (implicitly UTF-8-encoded) characters between the quotes; in particular, backslashes have no special meaning and the string may contain newlines. Carriage return characters ('\r') inside raw string literals are discarded from the raw string value.
That means, all \r\n
in your code is not interpreted as carriage returns or line breaks. You should change to use double quote "
.
And as a general style guideline, one should use double quote unless there is need for a lot of escaping.