I have rest-ful interface written in golang. I need to do the authentication on each endpoint. once authentication is done I have to forward it back to a tcp server.
I created a tcp client and any value that is coming from the channel is to be send to tcp server. the channel is populated from the http request body.
The issue is that once I issue curl command the client is stuck with no response; so obviously I am doing something wrong not sure what is wrong. does anyone have any insights on what my problem might be?
package main
import (
"bufio"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/httputil"
"net/url"
"os"
"strconv"
auth "github.com/abbot/go-http-auth"
)
type Configuration struct {
Server string
Port int64
UserName string
Pwd string
Realm string
ProxyPort int64
Edeserver string
}
var (
Config *Configuration
logp = flag.Bool("log", false, "enable logging")
)
func ReadConfiguration() {
file, _ := os.Open("Config.json")
decoder := json.NewDecoder(file)
Config = &Configuration{}
err := decoder.Decode(&Config)
if err != nil {
fmt.Println("error:", err)
}
}
func Secret(user, realm string) string {
if user == Config.UserName {
// password is "hello"
return Config.Pwd
}
return ""
}
func reverseProxyTows(w http.ResponseWriter, authenticatedRequest *auth.AuthenticatedRequest) {
req := &authenticatedRequest.Request
if *logp {
log.Println(" Authenticated Username ", authenticatedRequest.Username)
log.Println(" Authenticated URL ", req.URL.RequestURI())
}
destinationURL := fmt.Sprintf("http://%s:%d", Config.Server, Config.Port)
u, err := url.Parse(destinationURL)
if err != nil {
log.Fatal(err)
}
if *logp {
log.Println("reverse_proxy", u)
}
reverseProxy := httputil.NewSingleHostReverseProxy(u)
reverseProxy.ServeHTTP(w, req)
}
func openConnectionTotcp(edechannel chan string) {
conn, _ := net.Dial("tcp", Config.Edeserver)
text := <-edechannel
fmt.Fprintf(conn, text+"\n")
message, _ := bufio.NewReader(conn).ReadString('\n')
fmt.Print("Message from server: " + message)
}
func main() {
ReadConfiguration()
flag.Parse()
c := make(chan string)
go openConnectionTotcp(c)
fmt.Printf("Started proxy to destination server %v:%d and is listening on %d ", Config.Server, Config.Port, Config.ProxyPort)
authenticator := auth.NewBasicAuthenticator(Config.Realm, Secret)
http.HandleFunc("/", authenticator.Wrap(reverseProxyTows))
http.HandleFunc("/tyrion/1", authenticator.Wrap(func(w http.ResponseWriter, authenticatedRequest *auth.AuthenticatedRequest) {
req := &authenticatedRequest.Request
bodyBytes, err2 := ioutil.ReadAll(req.Body)
if err2 != nil {
log.Fatal(err2)
}
bodyString := string(bodyBytes)
c <- bodyString
fmt.Fprintf(w, "success")
}))
http.ListenAndServe(":"+strconv.FormatInt(Config.ProxyPort, 10), nil)
}
Your code execution blocks at c <- bodyString because nothing appears to be reading from that unbuffered channel. That line will pause execution until another routine reads from the channel.
Related
I’m working on proxy server with gin and ServeHTTP.
Actually GET and OPTIONS request works well. But when I trying multiples POST request I get EOF error one in two request.
I’ve test to make repeat request without proxy service and its work well, so there is something not working in my code.
Edit :
I have test POST proxy with https://ptsv2.com/ and all request response return 200 status code.
// main.go
package main
import (
"fmt"
"../pkg/api"
)
func main() {
fmt.Println("Starting server")
api.InitServer()
}
// routes.go
package api
import (
"github.com/gin-gonic/gin"
)
const serviceUrl = "http://localhost:8732"
func InitServer() {
router := gin.Default()
defineRoutes(router)
router.Run()
}
func defineRoutes(router *gin.Engine) {
router.GET("/ping", Ping)
router.POST("/*any", Proxy(serviceUrl))
}
// controller.go
package api
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/http/httputil"
"net/url"
"strconv"
"github.com/gin-gonic/gin"
)
type transport struct {
http.RoundTripper
}
func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
resp, err = t.RoundTripper.RoundTrip(req)
if err != nil {
// EOF ERROR HERE
return nil, err
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = resp.Body.Close()
if err != nil {
return nil, err
}
b = bytes.Replace(b, []byte("server"), []byte("schmerver"), -1)
body := ioutil.NopCloser(bytes.NewReader(b))
response := resp
response.Body = body
response.ContentLength = int64(len(b))
response.Header.Set("Content-Length", strconv.Itoa(len(b)))
response.Header.Set("Access-Control-Allow-Origin", "*")
return response, nil
}
func Proxy(targetUrl string) gin.HandlerFunc {
fn := func(c *gin.Context) {
remote, err := url.Parse(targetUrl)
if err != nil {
panic(err)
}
proxy := httputil.NewSingleHostReverseProxy(remote)
proxy.Director = func(req *http.Request) {
req.Header = c.Request.Header
req.Host = remote.Host
req.URL.Scheme = remote.Scheme
req.URL.Host = remote.Host
req.Close = true // True / False same result
}
proxy.Transport = &transport{http.DefaultTransport}
proxy.ServeHTTP(c.Writer, c.Request)
}
return fn
}
conclusion
your code have no bug. it works. maybe your network setting is wrong.
explain
I download your code and test it with a local backend server. It works.
appendix
backend server code
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
fmt.Println("Starting server")
router := gin.Default()
router.POST("/*any", func(c *gin.Context) {
c.JSON(200, gin.H{
"mes": "hello",
})
})
router.Run(":8888")
}
I have created some Go functions that make HTTP GET calls to services that are out there on the internet and parse the results.
I am now working on writing test-cases for these functions.
In my test cases, I'm using the go package httptest to simulate calls to these external services. Below is my code. Error checking is purposefully removed for brevity. Here is the go-playground.
package main
import (
"fmt"
"io"
"context"
"net/http"
"net/http/httptest"
)
func handlerResponse() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"A":"B"}`))
})
}
func buildMyRequest(ctx context.Context, url string) *http.Request {
request, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
return request
}
func myPrint(response *http.Response) {
b := make([]byte, 60000)
for {
_, err := response.Body.Read(b)
if err == io.EOF {
break
}
}
fmt.Println(string(b))
}
func main() {
srv := httptest.NewServer(handlerResponse())
client := http.Client{}
myResponse1, _ := client.Do(buildMyRequest(context.Background(), srv.URL))
fmt.Println("myResponse1:")
myPrint(myResponse1)
myResponse2, _ := client.Do(buildMyRequest(context.Background(), srv.URL))
fmt.Println("myResponse2:")
myPrint(myResponse2)
}
This is the output it produces:
myResponse1:
{"A":"B"}
myResponse2:
{"A":"B"}
As you can see, I have created some dummy HTTP response data {"A":"B"} and when you send an HTTP request to srv.URL, it actually hits an ephemeral HTTP server which responds with the dummy data. Cool!
When you send the second HTTP request to srv.URL, it again responds with the same dummy data. But this is where my problem arises. I want the ephemeral HTTP server to return some different data the second time {"C":"D"} and third time {"E":"F"} it receives a request.
How can I change the first line of the main() function so that the server responds with my desired data on subsequent HTTP calls?
you could use a hack like follows ( playground : here)
package main
import (
"fmt"
"io"
"context"
"net/http"
"net/http/httptest"
"sync"
)
type responseWriter struct{
resp map[int]string
count int
lock *sync.Mutex
}
func NewResponseWriter()*responseWriter{
r := new(responseWriter)
r.lock = new(sync.Mutex)
r.resp = map[int]string{
0: `{"E":"F"}`,
1: `{"A":"B"}`,
2: `{"C":"D"}`,
}
r.count = 0
return r
}
func (r *responseWriter)GetResp()string{
r.lock.Lock()
defer r.lock.Unlock()
r.count ++
return r.resp[r.count%3]
}
func handlerResponse(rr *responseWriter) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(rr.GetResp()))
})
}
func buildMyRequest(ctx context.Context, url string) *http.Request {
request, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
return request
}
func myPrint(response *http.Response) {
b := make([]byte, 60000)
for {
_, err := response.Body.Read(b)
if err == io.EOF {
break
}
}
fmt.Println(string(b))
}
func main() {
rr := NewResponseWriter()
srv := httptest.NewServer(handlerResponse(rr))
client := http.Client{}
myResponse1, err := client.Do(buildMyRequest(context.Background(), srv.URL))
if err != nil{
fmt.Println(err)
return
}
defer myResponse1.Body.Close()
fmt.Println("myResponse1:")
myPrint(myResponse1)
myResponse2, err := client.Do(buildMyRequest(context.Background(), srv.URL))
if err != nil{
fmt.Println(err)
return
}
defer myResponse2.Body.Close()
fmt.Println("myResponse2:")
myPrint(myResponse2)
}
I have a websocket server in Go using the Gorilla websocket package. At this stage, I will have only one server serving 5 clients. I am getting some messages from upstream into the WebSocket server. My intention is to NOT BROADCAST all the messages to the connected clients. I would like to send only one copy of the message to the connected clients in a round robin fashion. It doesn't matter which client gets it as long as there is only one that gets it.
My attempted solution
I have a simple Go server, created a Pool of clients (websocket connections) that I am receiving. However, I do not see any options to round robin the messages as I mentioned above. All my clients are getting the message. How can I send only one copy of the message to the connected clients instead of broadcasting to all.
Discalimer
The code I have is taken from online sources and modified to my requirement. I am relatively new to Go and Websockets. Is this something even possible using Websockets?
main.go
package main
import (
"fmt"
"net/http"
"github.com/realtime-chat-go-react/backend/pkg/websocket"
)
func serveWs(pool *websocket.Pool, w http.ResponseWriter, r *http.Request) {
fmt.Println("WebSocket Endpoint Hit")
conn, err := websocket.Upgrade(w, r)
if err != nil {
fmt.Fprintf(w, "%+v\n", err)
}
client := &websocket.Client{
Conn: conn,
Pool: pool,
}
pool.Register <- client
client.Read()
}
func setupRoutes() {
pool := websocket.NewPool()
go pool.Start()
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
serveWs(pool, w, r)
})
}
func main() {
setupRoutes()
err := http.ListenAndServe(":8080",nil)
if err != nil {
fmt.Println(err)
}
}
websocket.go
package websocket
import (
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
var wsList []*websocket.Conn
func Upgrade(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error) {
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
conn, err := upgrader.Upgrade(w, r, nil)
wsList = append(wsList, conn) //Creating a list here to store all websocket clients.
if err != nil {
log.Println(err)
return nil, err
}
return conn, nil
}
pool.go
package websocket
import "fmt"
type Pool struct {
Register chan *Client
Unregister chan *Client
Clients map[*Client]bool
Broadcast chan Message
}
func NewPool() *Pool {
return &Pool{
Register: make(chan *Client),
Unregister: make(chan *Client),
Clients: make(map[*Client]bool),
Broadcast: make(chan Message),
}
}
func (pool *Pool) Start() {
for {
select {
case client := <-pool.Register:
pool.Clients[client] = true
fmt.Println("Size of Connection Pool: ", len(pool.Clients))
for client, _ := range pool.Clients {
fmt.Println(client)
client.Conn.WriteJSON(Message{Type: 1, Body: "New User Joined..."})
}
break
case client := <-pool.Unregister:
delete(pool.Clients, client)
fmt.Println("Size of Connection Pool: ", len(pool.Clients))
for client, _ := range pool.Clients {
client.Conn.WriteJSON(Message{Type: 1, Body: "User Disconnected..."})
}
break
case message := <-pool.Broadcast: //This is where I need to modify the code but not sure how
fmt.Println("Sending message to all clients in Pool")
for client, _ := range pool.Clients {
if err := client.Conn.WriteJSON(message); err != nil {
fmt.Println(err)
return
}
}
}
}
}
client.go
package websocket
import (
"fmt"
"log"
"sync"
"github.com/gorilla/websocket"
)
type Client struct {
ID string
Conn *websocket.Conn
Pool *Pool
mu sync.Mutex
}
type Message struct {
Type int `json:"type"`
Body string `json:"body"`
}
func (c *Client) Read() {
defer func() {
c.Pool.Unregister <- c
c.Conn.Close()
}()
for {
messageType, p, err := c.Conn.ReadMessage()
if err != nil {
log.Println(err)
return
}
message := Message{Type: messageType, Body: string(p)}
c.Pool.Broadcast <- message
fmt.Printf("Message Received: %+v\n", message)
}
}
Modify the pool to store clients in a slice instead of a map. Add field to record index of the previous client used.
type Pool struct {
Register chan *Client
Unregister chan *Client
Clients []*Client
Broadcast chan Message
PrevClientIndex int
}
Round robin instead of broadcasting:
case message := <-pool.Broadcast:
if len(pool.Clients) == 0 {
continue
}
pool.PrevClientIndex++
if pool.PrevClientIndex >= len(pool.Clients) {
pool.PrevClientIndex = 0
}
client := pool.Clients[pool.PrevClientIndex]
if err := client.Conn.WriteJSON(message); err != nil {
// handle error
...
Register appends to the slice:
case client := <-pool.Register:
pool.Clients = append(pool.Clients, client)
...
Unregister removes the client from the slice:
case client := <-pool.Unregister:
j := 0
for _, c := range pool.Clients {
if c != client {
c.Clients[j] = c
j++
}
}
pool.Clients = pool.Clients[:j]
...
I have a problem launching multiple go routines over 90000 to do http requests via POST to the same server, both the server and the client are locally, some of the requests are executed successfully and some of them giving me this response read: connection reset by peer
Please notice I am posting a small amount of data in the provided example
but actually I am sending a huge amount of data using protobuf.
This is the server https://play.golang.org/p/r1-rYNuAos
package main
import (
"net/http"
"log"
"encoding/json"
)
var port string
type problem struct{
}
func main() {
p := &problem{}
p.server(":9090")
}
func (self *problem)server(port string) {
s := &http.Server{
Addr: port,
Handler: self,
}
log.Println("Server started")
// Should be last line as it is a blocking.
log.Fatal(s.ListenAndServe())
}
func (self *problem) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
if err := json.NewEncoder(w).Encode(map[string]interface{}{"created": true}); err != nil {
log.Fatal(err.Error())
}
}
And this is the client https://play.golang.org/p/Xx5qQoqrYm
package main
import (
"net/http"
"bytes"
"io/ioutil"
"log"
"fmt"
)
type problem struct{
}
func main() {
p := &problem{}
p.client(":9090")
}
func (self *problem)client(port string) {
var k int
for k=0;k<90000;k++ {
go func(){
nativeRequest, err := http.NewRequest(
"POST",
"http://127.0.0.1" + port + "/",
bytes.NewBuffer([]byte(`testing`)),
)
nativeRequest.Close = true
if err != nil {
fmt.Println(err.Error())
}
client := &http.Transport{
}
nativeResponse, err := client.RoundTrip(nativeRequest)
if err != nil {
log.Println(err.Error())
}
if nativeResponse != nil {
defer nativeResponse.Body.Close()
if err != nil {
fmt.Println(err.Error())
}
body, err := ioutil.ReadAll(nativeResponse.Body)
if err != nil {
fmt.Println(err.Error())
}
fmt.Println(string(body))
}
}()
}
}
Server.go
package main
import (
"fmt"
"net/http"
//"strings"
"encoding/json"
"io/ioutil"
"strconv"
"net"
"bufio"
)
type Message struct {
Text string
}
func Unmarshal(data []byte, v interface{}) error
func main() {
//http.HandleFunc("/", handler)
server,_ := net.Listen("tcp", ":" + strconv.Itoa(8080))
if server == nil {
panic("couldn't start listening: ")
}
conns := clientConns(server)
for {
go handleConn(<-conns)
}
}
func clientConns(listener net.Listener) chan net.Conn {
ch := make(chan net.Conn)
i := 0
go func() {
for {
client, _ := listener.Accept()
if client == nil {
fmt.Printf("couldn't accept: ")
continue
}
i++
fmt.Printf("%d: %v <-> %v\n", i, client.LocalAddr(), client.RemoteAddr())
ch <- client
}
}()
return ch
}
func handleConn(client net.Conn) {
b := bufio.NewReader(client)
fmt.Println("Buffer")
for {
line, err := b.ReadBytes('\n')
if err != nil { // EOF, or worse
break
}
client.Write(line)
}
}
Client.go
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
"flag"
//"io"
// "net"
// "net/rpc"
// "sync"
)
func Unmarshal(data []byte, v interface{}) error
func Marshal(v interface{}) ([]byte, error)
type Message struct {
Text string
}
func main(){
var flagtext = flag.String("flagtext", "Hello!", "Flag")
flag.Parse()
var text string
text = *flagtext
m := Message{text}
var m1 Message
b, err := json.Marshal(m)
if err == nil{
resp, err := http.Post("http://127.0.0.1:8080","application/json", strings.NewReader(string(b)))
if err != nil{
log.Fatal("Error while post: %v",err)
}
fmt.Println(resp)
err = json.Unmarshal(b, &m1)
}
}
Error I get when I run client.go is this:
Error while post: %vmalformed HTTP status code "/"
Though, the server registers a channel for each post, it shows a malformed HTTP status code. Is it because I'm listening in the wrong channel? I'm confused why this error is occurring.
This line in the server code:
client.Write(line)
sends the request line back to the client. Since the client is posting something like GET / HTTP/1.1, this means that the server is responding with something like GET / HTTP/1.1, instead of something like HTTP/1.1 200 OK. The error-message you're seeing is because / appears in the status-code position.
In server.go it seems you are trying to write your own HTTP server from the TCP socket level up. This is unnecessary work - take the easy route and use the built-in HTTP server API.
The general outline of such a server is like this:
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
and is described further in this article. More documentation is in net/http.