Reputation: 13908
I'm using a channel to pass messages from an HTTP handler:
package server
import (
"bytes"
"errors"
"io/ioutil"
"log"
"net/http"
)
type Server struct {}
func (s Server) Listen() chan interface{} {
ch := make(chan interface{})
http.HandleFunc("/", handle(ch))
go http.ListenAndServe(":8080", nil)
return ch
}
func handle(ch chan interface{}) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
b, err := ioutil.ReadAll(r.Body)
defer r.Body.Close()
if err != nil {
ch <- errors.New(string(500))
return
}
w.Write([]byte("Hello World"))
log.Print("about to pass to handler channel")
ch <- bytes.NewBuffer(b)
log.Print("passed to handler channel")
}
}
When I make a request to the server running on port 8080, the thread blocks on this line:
ch <- bytes.NewBuffer(b)
Why is this happening? If you notice, I'm running the listener in a goroutine. I also figured that HTTP handles happen in a separate thread. If I delete the above line, the thread becomes unblocked and the program works as expected. What am I doing wrong?
To clarify, I want to be able to pass the body of a POST request to a channel. Help.
UPDATE: I'm reading from the channel on the main thread:
listenerChan := n.Listen()
go SendRequest("POST", "http://localhost:8080", []byte("hello"))
for listenedMsg := range listenerChan {
log.Print("listened message>>>> ", listenedMsg)
}
But the thread still blocks on the same line. For clarification, there is nothing wrong with how im sending the request. If I remove the channel send line above, the thread doesnt block.
Upvotes: 0
Views: 412
Reputation: 302
I added missing parts in your code and run it, everything works well. I don't see any block. Here's the code:
package main
import (
"bytes"
"errors"
"io/ioutil"
"log"
"net/http"
"time"
)
type Server struct{}
func (s *Server) Listen() chan interface{} {
ch := make(chan interface{})
http.HandleFunc("/", handle(ch))
go http.ListenAndServe(":8080", nil)
return ch
}
func handle(ch chan interface{}) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
b, err := ioutil.ReadAll(r.Body)
defer r.Body.Close()
if err != nil {
ch <- errors.New(string(500))
return
}
w.Write([]byte("Hello World"))
log.Print("about to pass to handler channel")
ch <- bytes.NewBuffer(b)
log.Print("passed to handler channel")
}
}
// SendRequest send request
func SendRequest(method string, url string, data []byte) {
tr := &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
}
client := &http.Client{Transport: tr}
reader := bytes.NewReader(data)
req, err := http.NewRequest(method, url, reader)
if err != nil {
panic(err)
}
client.Do(req)
}
func main() {
n := new(Server)
listenerChan := n.Listen()
go SendRequest("POST", "http://localhost:8080", []byte("hello"))
for listenedMsg := range listenerChan {
log.Print("listened message>>>> ", listenedMsg)
}
}
And the output are:
2018/06/28 17:22:10 about to pass to handler channel
2018/06/28 17:22:10 passed to handler channel
2018/06/28 17:22:10 listened message>>>> hello
Upvotes: 0
Reputation: 4351
I think @bereal gave a good explanation about using an unbuffered or synchronous channel.
Another way to make things work is to make the channel buffered by changing the line that creates the channel to:
ch := make(chan interface{}, 1) // added the 1
This will prevent the function from being blocked.
Upvotes: 0
Reputation: 34282
Because the channel is unbuffered, the send operation blocks until there's someone who is ready to receive from them. Making the channel buffered will only defer the blocking, so you always need some reading goroutine.
Update to your update: the control flow of the program would go like this:
main
sends the request and waits for the responsemain
reads from the channel4 may happen only after 2, which is blocked by 3 which is blocked because 4 is not happening yet. A classical deadlock.
Upvotes: 2