Reading from a Reader multiple times - io

I'm building a simple caching proxy that intercepts HTTP requests, grabs the content in response.Body, then writes it back to the client. The problem is, as soon as I read from response.Body, the write back to the client contains an empty body (everything else, like the headers, are written as expected).
Here's the current code:
func requestHandler(w http.ResponseWriter, r *http.Request) {
client := &http.Client{}
r.RequestURI = ""
response, err := client.Do(r)
defer response.Body.Close()
if err != nil {
log.Fatal(err)
}
content, _ := ioutil.ReadAll(response.Body)
cachePage(response.Request.URL.String(), content)
response.Write(w)
}
If I remove the content, _ and cachePage lines, it works fine. With the lines included, requests return and empty body. Any idea how I can get just the Body of the http.Response and still write out the response in full to the http.ResponseWriter?

As in my comment you could implement the io.ReadCloser
As per Dewy Broto (Thanks) you can do this much simpler with:
content, _ := ioutil.ReadAll(response.Body)
response.Body = ioutil.NopCloser(bytes.NewReader(content))
response.Write(w)

As you have discovered, you can only read once from a request's Body.
Go has a reverse proxy that will facilitate what you are trying to do. Check out httputil.ReverseProxy and httputil.DumpResponse

You do not need to read from the response a second time. You already have the data in hand and can write it directly to the response writer.
The call
response.Write(w)
writes the response in wire format to the server's response body. This is not what you want for a proxy. You need to copy the headers, status and body to the server response individually.
I have noted other issues in the code comments below.
I recommend using the standard library's ReverseProxy or copying it and modifying it to meet your needs.
func requestHandler(w http.ResponseWriter, r *http.Request) {
// No need to make a client, use the default
// client := &http.Client{}
r.RequestURI = ""
response, err := http.DefaultClient.Do(r)
// response can be nil, close after error check
// defer response.Body.Close()
if err != nil {
log.Fatal(err)
}
defer response.Body.Close()
// Check errors! Always.
// content, _ := ioutil.ReadAll(response.Body)
content, err := ioutil.ReadAll(response.Body)
if err != nil {
// handle error
}
cachePage(response.Request.URL.String(), content)
// The Write method writes the response in wire format to w.
// Because the server handles the wire format, you need to do
// copy the individual pieces.
// response.Write(w)
// Copy headers
for k, v := range response.Header {
w.Header()[k] = v
}
// Copy status code
w.WriteHeader(response.StatusCode)
// Write the response body.
w.Write(content)
}

Related

While downloading file from Azure Blob Storage using Golang getting " curl Empty reply from server" , but file is downloaded in background

I am trying to download a file from Azure Blob Storage using http request. I am able to download the file but on a terminal curl returns "Empty reply from server". I tried to increase the timeout, but it didn't fix it. I referred other questions related to this response from curl, but it didn't help. For small files this code is working flawlessly but for big files say 75 MB it is not working.
containerURL := azblob.NewContainerURL(*URL, pipeline)
blobURL := containerURL.NewBlockBlobURL(splitArray[1])
ctx := context.Background()
downloadResponse, err := blobURL.Download(ctx, 0, azblob.CountToEnd, azblob.BlobAccessConditions{}, false)
if err != nil {
.
.
.
}
bodyStream := downloadResponse.Body(azblob.RetryReaderOptions{MaxRetryRequests: 20})
// read the body into a buffer
downloadedData := bytes.Buffer{}
_, err = downloadedData.ReadFrom(bodyStream)
file, err := os.OpenFile(
"/tmp/"+fileName,
os.O_RDWR|os.O_TRUNC|os.O_CREATE,
0777,
)
file.Write(downloadedData.Bytes())
file.Close()
filePath := "/tmp/" + fileName
file, err = os.Open(filePath)
return middleware.ResponderFunc(func(w http.ResponseWriter, r runtime.Producer) {
fn := filepath.Base(filePath)
w.Header().Set(CONTENTTYPE, "application/octet-stream")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", fn))
io.Copy(w, file)
err := defer os.Remove(filePath)
file.Close()
})
I am thinking of implementing the above logic using goroutines. Is there even a need of using goroutines?
Any constructive feedback will be helpful.
After analyzing packets from wireshark got to know it was getting disconnected from my side due to timeout as I am using go-swagger , I increased the timeout , in configure.go . GoSwagger provides in-built function for handling these scenarios like TLS , Timeout. Below is code for reference.
// As soon as server is initialized but not run yet, this function will be called.
// If you need to modify a config, store server instance to stop it individually later, this is the place.
// This function can be called multiple times, depending on the number of serving schemes.
// scheme value will be set accordingly: "http", "https" or "unix"
func configureServer(s *http.Server, scheme, addr string) {
s.WriteTimeout(time.Minute * 5)
}

bufio scanner and handling new lines

I've got 2 processes communicating over TCP sockets. Side A sends a string to side B, which is sometimes encrypted using standard crypto/cipher package. The resulting string may include a new line character but Side B's bufio scanner is interpreting it as the end of the request. I want side B to continue accepting lines, append them and wait for a known end-of-command character before further processing it. Side B will return a response to Side A, so the connection remains open and therefore cannot use a close-connection event as a command delimiter.
Everything is working fine for single-line commands, but these new line characters in the encrypted output cause issues (about 10% of the time).
Side A will send in the following formats (the third is a legitimate example of a problem string I'm trying to process correctly):
callCommand()
callCommand("one","two","three")
callCommand("string","encrypted-data-to-follow","[7b��Cr��l��G���bH�#x��������� �(z�$�a��0��ڢ5Y7+��U�QT�ΐl�K�(�n�U��J����QK�BX�+�l\8H��-g�y.�.�1�f��I�C�Ȓ㳿���o�xz�8?��c�e ��Tb��?4�hDW���
�<���Е�gc�������N�V���ۓP8 �����O3")
We can fairly reliably say the end-of-command keys are a close parentheses ")" and a new line character.
Side A's function to send to side B:
func writer(text string) string {
conn, err := net.Dial("tcp", TCPdest)
t := time.Now()
if err != nil {
if _, t := err.(*net.OpError); t {
fmt.Println("Some problem connecting.\r\n")
} else {
fmt.Println("Unknown error: " + err.Error()+"\r\n")
}
} else {
conn.SetWriteDeadline(time.Now().Add(1 * time.Second))
_, err = conn.Write([]byte(text+"\r\n"))
if err != nil {
fmt.Println("Error writing to stream.\r\n")
} else {
timeNow := time.Now()
if timeNow.Sub(t.Add(time.Duration(5*time.Second))).Seconds() > 5 {
return "timeout"
}
scanner := bufio.NewScanner(conn)
for {
ok := scanner.Scan()
if !ok {
break
}
if strings.HasPrefix(scanner.Text(), "callCommand(") && strings.HasSuffix(scanner.Text(), ")") {
conn.Close()
return scanner.Text()
}
}
}
}
return "unspecified error"
}
Side B's handling of incoming connections:
src := "192.168.68.100:9000"
listener, _ := net.Listen("tcp", src)
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Some connection error: %s\r\n", err)
}
go handleConnection(conn)
}
func handleConnection(conn net.Conn) {
remoteAddr := conn.RemoteAddr().String()
fmt.Println("Client connected from " + remoteAddr + "\r\n")
scanner := bufio.NewScanner(conn)
wholeString := ""
for {
ok := scanner.Scan()
if !ok {
break
}
//Trying to find the index of a new-line character, to help me understand how it's being processed
fmt.Println(strings.Index(scanner.Text(), "\n"))
fmt.Println(strings.Index(wholeString, "\n"))
//for the first line received, add it to wholeString
if len(wholeString) == 0 {
wholeString = scanner.Text()
}
re := regexp.MustCompile(`[a-zA-Z]+\(.*\)\r?\n?`)
if re.Match([]byte(wholeString)) {
fmt.Println("Matched command format")
handleRequest(wholeString, conn)
} else if len(wholeString) > 0 && !re.Match([]byte(wholeString)) {
//Since we didn't match regex, we can assume there's a new-line mid string, so append to wholeString
wholeString += "\n"+scanner.Text()
}
}
conn.Close()
fmt.Println("Client at " + remoteAddr + " disconnected.\r\n")
}
func handleRequest(request string, conn net.Conn) {
fmt.Println("Received: "+request)
}
I'm not really sure this approach on Side B is correct but included my code above. I've seen a few implementations but a lot seem to rely on a close of connection to begin processing the request, which doesn't suit my scenario.
Any pointers appreciated, thanks.
Your communication "protocol" (one line being one message, not quite a protocol) clearly cannot handle binary data. If you want to send text data in your protocol, you could convert your binary data to text, using a Base64 encoding for example. You would also need some semantics to indicate that some text was converted from binary.
Or you could change your protocol to handle binary data natively. You could prepend the length of the binary data to follow, so that you know you have to read this data as binary and not interpret a newline character as the end of the message.
There are many protocols doing this very well, perhaps you don't need to come up with your custom one. If you want to send text messages, HTTP is very simple to use, you could format your data as JSON, using Base64 to convert your binary data to text:
{
"command": "string",
"args": [
"binaryDataAsBase64"
]
}

how to gzip a template in golang web

I have already set the value of contest-encoding, but how can I gzip the template, since the file is still to big.
func indexPageHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Encoding", "gzip")
r.Header.Set("Accept-Encoding", "gzip")
tmpl, err := template.New("index.html").ParseGlob("./templates/*")
if err != nil {
log.Println(err)
return
}
err = tmpl.Execute(w, nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
Is there any function that can gzip the response?]
Followed by the advise, I changed my code as this:
func indexPageHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Encoding", "gzip")
r.Header.Set("Accept-Encoding", "gzip")
gz := gzip.NewWriter(w)
defer gz.Close()
tmpl, err := template.New("index.html").ParseGlob("./templates/*")
if err != nil {
log.Println(err)
return
}
err = tmpl.Execute(gz, nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
But when I query the url, I only download the gzip file. What's wrong with it?
You can use package compress/gzip:
func NewWriter(w io.Writer) *Writer
NewWriter returns a new Writer.
Writes to the returned writer are compressed and written to w.
It is the caller's responsibility to call Close on the WriteCloser
when done. Writes may be buffered and not flushed until Close.
In your code, it might look like this:
gz := gzip.NewWriter(w)
defer gz.Close()
err = tmpl.Execute(gz, nil)
if err != nil {
http.Error(gz, err.Error(), http.StatusInternalServerError)
return
}
Ps.
You might want to check the Accept-Encoding header in the request to see if the browser accepts gzip encoded content.
r.Header.Set("Accept-Encoding", "gzip")
Setting the headers on the request has no effect. You would want to read this header to determine whether to use gzip.
The reason the browser downloads the file instead of rendering it is that it's missing a content type. When you write to an http.ResponseWriter without setting the Content-Type there's an algorithm that determines the Content-Type and sets the correct header. But with your body gzipped, it can't determine the correct content type. Add this before you write any data back to the client:
w.Header().Set("Content-Type", "text/html")
This is a bit to late but i hope someone else finds this usefull.
The code works but you need to set the header to be properly interpreted by the browser:
w.Header().Add("Content-Type","text/html")
w.Header().Add("Content-Encoding","gzip")
Anyway, I have not solved this problem. As I mentioned, when I add this code, restart the server and query this url, I actually download the zip file!?
In the end, I used Nginx to make a reverse proxy to compress the template. It works.
Still thanks to #Anisus

making go http client work with non-standard http servers

Shoutcast servers basically speak http, with one important difference: they respond to GET requests with ICY 200 OK instead of HTTP/1.1 200 OK.
Go won't have a bar of it, and correctly fails with the error malformed HTTP version "ICY".
However I would like to make things work and am wondering what the best approach is. My ideas so far:
use a custom http.Transport.Proxy to change ICY to HTTP/1.1 in-flight
an out of process proxy that does the same thing
overload http.ParseHTTPVersion (but golang doesn't have function overloading)
duplicate the entire http package, just to modify ParseHTTPVersion
Number 1. seems the most attractive attractive, but I have no idea how to respect the http "scope" and actually modify all responses on a given http version. Is this the kind of thing http.Transport.Proxy can handle?
Can anyone give me any pointers?
I got this working by creating a custom Dial function that returns a wrapped connection. My wrapper intercepts the first read on the connection and replaces ICY with HTTP/1.1. Not super robust, but proves the concept:
package main
import (
"fmt"
"net"
"net/http"
)
type IcyConnWrapper struct {
net.Conn
haveReadAny bool
}
func (i *IcyConnWrapper) Read(b []byte) (int, error) {
if i.haveReadAny {
return i.Conn.Read(b)
}
i.haveReadAny = true
//bounds checking ommitted. There are a few ways this can go wrong.
//always check array sizes and returned n.
n, err := i.Conn.Read(b[:3])
if err != nil {
return n, err
}
if string(b[:3]) == "ICY" {
//write Correct http response into buffer
copy(b, []byte("HTTP/1.1"))
return 8, nil
}
return n, nil
}
func main() {
tr := &http.Transport{
Dial: func(network, a string) (net.Conn, error) {
realConn, err := net.Dial(network, a)
if err != nil {
return nil, err
}
return &IcyConnWrapper{Conn: realConn}, nil
},
}
client := &http.Client{Transport: tr}
http.DefaultClient = client
resp, err := http.Get("http://178.33.230.189:8100") //random url I found on the internet
fmt.Println(err)
fmt.Println(resp.StatusCode)
}

What's the optimal way to execute a nodejs script from golang, that returns a string, and then communicate that string back to a golang variable?

I'm currently doing it with os/exec and Stdout on golang's side, and console.log("string") on nodejs's side.
Basically I need to generate a string but can only do so within nodejs but the majority of my code is in golang, so I'm trying to make this little blip in my code as seamless, secure, and reliable as possible and I'm a little uneasy about resting such an important part of my program on "console.log" and reading from shell output.
In short: I'm wondering if there exists a better and more standard communication line between my node and go code then console.log + shell output, or is that perhaps optimal enough?
Oh and the function of this particular part of my program is to take a markdown text file and convert it to HTML using markdown-it.
Some ideas:
Communicate through HTTP (send the data/string to a golang http listener)
Communicate through the filesystem (write the string to a temporary file and read it with golang)
Communicate through "something similiar to HTTP but specific to local application data sharing"
P.S.
I can't use otto, since markdown-it doesn't run there.
Actual code:
parser.go
package main
import (
"os"
"os/exec"
"fmt"
"bytes"
)
func main() {
cmd := "node"
args := []string{"parser.js", "/home/user1/dev/current/wikis/Bob's Pain/markup/index.md"}
process := exec.Command(cmd, args...)
stdin, err := process.StdinPipe()
if err != nil {
fmt.Println(err)
}
defer stdin.Close()
buf := new(bytes.Buffer) // THIS STORES THE NODEJS OUTPUT
process.Stdout = buf
process.Stderr = os.Stderr
if err = process.Start(); err != nil {
fmt.Println("An error occured: ", err)
}
process.Wait()
fmt.Println("Generated string:", buf)
}
parser.js
var md = require('markdown-it')();
var yaml = require('js-yaml');
var fs = require('fs');
if (process.argv.length < 3) {
console.log('Usage: node ' + process.argv[1] + ' FILENAME');
process.exit(1);
}
var filename = process.argv[2];
fs.readFile(filename, 'utf8', function(err, data) {
if (err) {
throw err;
}
parse(data)
});
function parse(data) {
data = data.split("---")
yamlData = data[1];
markData = data[2];
y = yamlProcess(yamlData);
markData = "# "+y.title+"\n\n"+markData
html = markdownToHTML(markData);
console.log(html) // SEND THE DATA BACK TO GOLANG
}
function yamlProcess(data) {
try {
var doc = yaml.safeLoad(data);
return doc;
} catch (e) {
console.log(e);
return {};
}
}
function markdownToHTML(data) {
return md.render(data);
}
The easiest way to do this with os/exec:
command := "node parser.js /path/to/some/file.md"
parts := strings.Fields(command)
data, err := exec.Command(parts[0], parts[1:]...).Output()
if err != nil {
panic(err)
}
output := string(data)
"output" is the data that is printed from your NodeJS script. "command" is any command as a string.
I've approached similar requirement both ways.
For a build pipeline extension, I'd write a Python script that takes arguments from command line and outputs results to stdout. It's a simple interface, for a "run once", "everything succeeds otherwise fail fast" usage. If that's the same in your case, I'd keep the implementation as-is.
For a web application, I had Java service for just a specific function (in my case, Natty date recognition from a natural language string). This has the benefit that the application is already "warmed up" at the time of the call, and will definitely respond faster rather than booting up each time the request comes in. Going with a rest interface will probably reveal more benefits over time - e.g. simpler client implementation, monitoring, deployment options, switch implementation without changing clients, etc. It's just more conventional this way.

Resources