How to make a long connection with http.Client? - http

I try to connect a http server as long connection, like below:
func main() {
request, err := http.NewRequest("GET", "http://long.connection.org:8080/", nil)
request.SetBasicAuth("xxx", "oooo")
http_client := &http.Client{}
response, _ := http_client.Do(request)
var buf []byte
for {
_, err := response.Body.Read(buf)
if err == io.EOF { break }
fmt.Printf("%s", string(buf))
}
}
But read from response.Body always empty. And seems I can't use response.Body to send data to server.
Any one can help?

This seems to work:
package main
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
request, err := http.NewRequest("GET", "http://www.example.com/", nil)
if err != nil {
log.Fatal(err)
}
http_client := &http.Client{}
response, err := http_client.Do(request)
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 4096) // any non zero value will do, try '1'.
for {
n, err := response.Body.Read(buf)
if n == 0 && err != nil { // simplified
break
}
fmt.Printf("%s", buf[:n]) // no need to convert to string here
}
fmt.Println()
}
Edit: Added forgotten error handling of NewRequest.

Related

How to reqeust using Http/2.0 in http library with DialTLS?

i want to request with http library in Golang using http2, i tried almost everything and made a lot of research without any success so i am asking here if any one can help me and give me the right answer.
package main
import (
"fmt"
"net"
"net/http"
"strings"
tls "github.com/refraction-networking/utls"
)
func main() {
req, _ := http.NewRequest("GET", "https://http2.pro/api/v1", nil)
tlsspec, _ := ParseJA3("771,49195-49196-52393-49199-49200-52392-49161-49162-49171-49172-156-157-47-53,65281-0-23-35-13-5-16-11-10,29-23-24,0")
transport := &http.Transport{
DialTLS: func(network, addr string) (net.Conn, error) {
conn, err := net.Dial(network, addr)
if err != nil {
return nil, err
}
host, _, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
config := &tls.Config{ServerName: host}
uconn := tls.UClient(conn, config, tls.HelloCustom)
//tlsspec is just custom tls
if uconn.ApplyPreset(tlsspec); err != nil {
return nil, err
}
if uconn.Handshake(); err != nil {
return nil, err
}
return uconn, nil
},
}
client := http.Client{
Transport: transport,
}
resp, _ := client.Do(req)
fmt.Println(resp)
}

Keep WebSocket connection alive after upgrade in Go

I am having issue in keeping websocket connection alive in go. In my code below, I assign 2 different ports to handle websocket (:8080) and for API request (:3300).
There is no issue when I am using websocket handler directly, but using API handler request and making new external request to the websocker handler, the connection closed directly. Any help is appreciated.
package main
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"time"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
)
func main() {
go websocket()
http.HandleFunc("/ws", func(rw http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
url := fmt.Sprintf("http://127.0.0.1:8080?%s", r.URL.RawQuery)
req, err := http.NewRequest(r.Method, url, bytes.NewReader(body))
if err != nil {
fmt.Println(err)
panic(err)
}
req.Header = make(http.Header)
for h, val := range r.Header {
req.Header[h] = val
}
httpClient := &http.Client{Timeout: time.Second * 10}
httpClient.Do(req)
})
http.ListenAndServe(":3300", nil)
}
func websocket() {
http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
conn, _, _, err := ws.UpgradeHTTP(r, w)
if err != nil {
fmt.Println(err)
return
}
go func() {
defer conn.Close()
for {
msg, op, err := wsutil.ReadClientData(conn)
if err != nil {
fmt.Println(err)
return
}
err = wsutil.WriteServerMessage(conn, op, msg)
if err != nil {
fmt.Println(err)
return
}
}
}()
}))
}
The code in the question connects to the websocket endpoint using an HTTP request. Upgrade fails as a result.
Use the standard library reverse proxy to proxy the request.
A simpler approach is to is to call the websocket handler directly. Move the handler to a top-level function:
func handleWS(w http.ResponseWriter, r *http.Request) {
conn, _, _, err := ws.UpgradeHTTP(r, w)
if err != nil {
fmt.Println(err)
return
}
go func() {
defer conn.Close()
for {
msg, op, err := wsutil.ReadClientData(conn)
if err != nil {
fmt.Println(err)
return
}
err = wsutil.WriteServerMessage(conn, op, msg)
if err != nil {
fmt.Println(err)
return
}
}
}()
}
Use the handler in both servers.
func main() {
go websocket()
http.HandleFunc("/ws", handleWS)
http.ListenAndServe(":3300", nil)
}
func websocket() {
http.ListenAndServe(":8080", http.HandlerFunc(handleWS))
}

request.FormValue is empty in the 'net/http' in Go

I'm trying to create a simple http service with the endpoint to download file to the local system in Go. The link comes in ?uri tag, but when I want to get it I receive an empty string. I tried to parse the form of my request but it didn't help. Here is my code:
func main() {
http.HandleFunc("/download", DownloadHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func DownloadHandler(writer http.ResponseWriter, request *http.Request) {
prsErr := request.ParseForm()
if prsErr != nil{
panic(prsErr)
}
uri := request.FormValue("?uri")
_, _ = writer.Write([]byte(uri))
err := DownloadFile("img.png", uri)
if err != nil {
panic(err)
}
}
func DownloadFile(filepath string, url string) error {
// Create the file
out, err := os.Create(filepath)
if err != nil {
return err
}
defer out.Close()
// Get the data
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
// Write the body to file
_, err = io.Copy(out, resp.Body)
if err != nil {
return err
}
return nil
}
I will appreciate any help! Thank you!
invalid at request.FormValue("?uri")
uri := request.FormValue("uri")

Connection reset by peer GO

I'm trying to do a request in GO but I always receive "Connection reset by peer" error. The following code shows how I'm doing the request:
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
client := = &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
fmt.Println(resp.Body)
...and I receive:
Get https://example.com: read tcp 1.2.3.4:1234->5.6.7.8:5678: read: connection reset by peer
When I do curl https://example.com I receive response form the server.
Why can't I do the request in GO?
Your code works if I use it against a URL like https://example.com. Are you sure you are passing it the correct URL?
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
url := "https://example.com"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println(err)
return
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("OK")
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
str := string(bytes[:])
fmt.Printf("%s", str)
}

Can I post with Content-Type: multipart/form-data

How do I POST to an API with Content-Type: multipart/form-data, []byte parameters and string arguments? I have tried, but it is failing.
Error message:
details: "[301 301 Moved Permanently]<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n<html>\r\n301 Moved Permanently\r\n<body bgcolor=\"white\">\r\n301 Moved Permanently\r\n<p>The requested resource has been assigned a new permanent URI.</p >\r\n<hr/>Powered by Tengine/2.1.0</body>\r\n</html>\r\n"
Go code:
func NewPost2(url string) ([]byte, error) {
m := make(map[string]interface{}, 0)
m["fileName"] ="good"
m["name"] = Base64ToByte("/9j/4AAQSkZJRgABAQEAeAB4AAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDHooor+wD+Zz//2Q==")
b, _ := json.Marshal(m)
httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(b))
httpReq.Header.Set("Content-Type", "multipart/form-data;charset=UTF-8")
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
b, _ := ioutil.ReadAll(resp.Body)
return nil, fmt.Errorf("[%d %s]%s", resp.StatusCode, resp.Status, string(b))
}
respData, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return respData, nil
}
Now, I am very happy with the mood to share my solution
func NewPostFile(url string, paramTexts map[string]interface{}, paramFile FileItem) ([]byte, error) {
// if paramFiles ==nil {
// return NewPost(url,paramTexts,header,transport)
// }
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
for k, v := range paramTexts {
bodyWriter.WriteField(k, v.(string))
}
fileWriter, err := bodyWriter.CreateFormFile(paramFile.Key, paramFile.FileName)
if err != nil {
fmt.Println(err)
//fmt.Println("Create form file error: ", error)
return nil, err
}
fileWriter.Write(paramFile.Content)
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
fmt.Println(bodyBuf.String())
resp, err := http.Post(url, contentType, bodyBuf)
if err != nil {
return nil, err
}
defer resp.Body.Close()
fmt.Println(resp)
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
b, _ := ioutil.ReadAll(resp.Body)
return nil, fmt.Errorf("[%d %s]%s", resp.StatusCode, resp.Status, string(b))
}
respData, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
fmt.Println(string(respData))
return respData, nil
}
type FileItem struct {
Key string //image_content
FileName string //test.jpg
Content []byte //[]byte
}
I wrapped the multipart code in a function, as you need to Close it before you
can make a request. Also my method is using # as a heuristic, similar to
cURL [1]:
package main
import (
"bytes"
"io"
"mime/multipart"
"os"
"strings"
)
func createForm(form map[string]string) (string, io.Reader, error) {
body := new(bytes.Buffer)
mp := multipart.NewWriter(body)
defer mp.Close()
for key, val := range form {
if strings.HasPrefix(val, "#") {
val = val[1:]
file, err := os.Open(val)
if err != nil { return "", nil, err }
defer file.Close()
part, err := mp.CreateFormFile(key, val)
if err != nil { return "", nil, err }
io.Copy(part, file)
} else {
mp.WriteField(key, val)
}
}
return mp.FormDataContentType(), body, nil
}
Example:
package main
import "net/http"
func main() {
form := map[string]string{"profile": "#portrait.jpg", "name": "John"}
ct, body, err := createForm(form)
if err != nil {
panic(err)
}
http.Post("https://stackoverflow.com", ct, body)
}
https://curl.se/docs/manpage.html#-F
On a 301 response, the new url is specified in the headers of the response, not its body
(see https://en.wikipedia.org/wiki/HTTP_301)
try printing :
resp.Header["Location"]
If you have this error as a final response, this also means that the http.Client chose to not follow this redirection.
The doc says that the dafult policy for a Client is to follow up to 10 redirects.
In order to debug redirects, you can write your own CheckRedirect function, which can for instance print the sequence of urls

Resources