Output and meaning of PostForm requests in Go - go

In the code below, I am serving a PostForm request, and when I run the code I am getting an output of Age=20&Name=Mike, instead of a map like {"Name":Mike,"Age":"20"}. Is that output appropriate or am I missing something?
Also what is the difference between a PostForm request and a Post request?
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
)
func server() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
content, _ := ioutil.ReadAll(r.Body)
fmt.Println(string(content))
})
log.Fatal(http.ListenAndServe("", nil))
}
func client() {
data := url.Values{}
data.Add("Name", "Mike")
data.Add("Age", "20")
response, err := http.PostForm("http://localhost/", data)
if err != nil {
return
}
defer response.Body.Close()
}
func main() {
go server()
client()
}

Related

How to simulate multiple different HTTP responses using Go's httptest?

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)
}

golang http server send r.URL.Path over socket

I have a http server, and I want to send the r.URL.Path text to a client using a socket
I get a error: undefined: conn in conn.Write
This is becauase conn is defined in another function
What I have tried:
package main
import (
"net"
"io"
"net/http"
)
ln, _ := net.Listen("tcp", ":8081")
conn, _ := ln.Accept()
func hello(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello world!")
conn.Write([]byte(r.URL.Path + "\n")) //Here I'm attemping to send it
}
func main() {
http.HandleFunc("/", hello)
http.ListenAndServe(":8000", nil)
}
Your problem is actually in the way you try to declare variables.
If you want your conn to be on global scope, use var
package main
import (
"io"
"net/http"
"net"
)
var ln, _ = net.Listen("tcp", ":8081")
var conn, _ = ln.Accept()
func hello(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello world!")
conn.Write([]byte(r.URL.Path + "\n")) //Here I'm attemping to send it
}
func main() {
http.HandleFunc("/", hello)
http.ListenAndServe(":8000", nil)
}

How to test reverse proxy with martini in go

I'm writing test code for martini app working as a reverse proxy in go, and want to test it using httptest.ResponseRecorder, but I got an error the following.
[martini] PANIC: interface conversion: *httptest.ResponseRecorder is not http.CloseNotifier: missing method CloseNotify
httptest.ResponseRecorder has no method CloseNotify()
How should I test it?
package main
import (
"github.com/go-martini/martini"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"net/http/httputil"
"net/url"
"testing"
)
func TestReverseProxy(t *testing.T) {
// Mock backend
backendResponse := "I am the backend"
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(backendResponse))
}))
defer backend.Close()
backendURL, _ := url.Parse(backend.URL)
// Frontend
m := martini.Classic()
m.Get("/", func(w http.ResponseWriter, r *http.Request) {
proxy := httputil.NewSingleHostReverseProxy(backendURL)
proxy.ServeHTTP(w, r)
})
// Testing
req, _ := http.NewRequest("GET", "/", nil)
res := httptest.NewRecorder()
m.ServeHTTP(res, req)
assert.Equal(t, 200, res.Code, "should be equal")
}
First, please note that the martini framework is no longer maintained as said in their README.
Then, about your issue, it's because Martini does something that looks pretty bad to me: it takes an http.ResponseWriter and assumes it is also an http.CloseNotifier, while there is absolutely no guarantee of this. They should take a custom interface wrapping both of them, something like that:
type ResponseWriterCloseNotifier interface {
http.ResponseWriter
http.CloseNotifier
}
You can see in their source code that they had the same issue for their own tests, and used some workaround: https://github.com/go-martini/martini/commit/063dfcd8b0f64f4e2c97f0bc27fa422969baa23b#L13
Here is some working code made with it:
package main
import (
"net/http"
"net/http/httptest"
"net/http/httputil"
"net/url"
"testing"
"github.com/go-martini/martini"
"github.com/stretchr/testify/assert"
)
type closeNotifyingRecorder struct {
*httptest.ResponseRecorder
closed chan bool
}
func newCloseNotifyingRecorder() *closeNotifyingRecorder {
return &closeNotifyingRecorder{
httptest.NewRecorder(),
make(chan bool, 1),
}
}
func (c *closeNotifyingRecorder) close() {
c.closed <- true
}
func (c *closeNotifyingRecorder) CloseNotify() <-chan bool {
return c.closed
}
func TestReverseProxy(t *testing.T) {
// Mock backend
backendResponse := "I am the backend"
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(backendResponse))
}))
defer backend.Close()
backendURL, _ := url.Parse(backend.URL)
// Frontend
m := martini.Classic()
m.Get("/", func(w http.ResponseWriter, r *http.Request) {
proxy := httputil.NewSingleHostReverseProxy(backendURL)
proxy.ServeHTTP(w, r)
})
// Testing
req, _ := http.NewRequest("GET", "/", nil)
res := newCloseNotifyingRecorder()
m.ServeHTTP(res, req)
assert.Equal(t, 200, res.Code, "should be equal")
}

malformed HTTP status code "/" error in Go

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.

Print to Log using Go Language Simple HTTP Server

I am trying to log the IP address of the requestor, what METHOD they are using and what file they are requesting. But for some reason it only outputs on the terminal and doesn't save it to logfile.txt...
package main
import (
"fmt"
"net/http"
"log"
"encoding/json"
"io/ioutil"
)
type Options struct {
Path string
Port string
}
func Log(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("%s %s %s\n", r.RemoteAddr, r.Method, r.URL)
handler.ServeHTTP(w, r)
})
}
func main() {
op := &Options{Path: "./", Port: "8001"}
data, _ := ioutil.ReadFile("./config.json")
json.Unmarshal(data, op)
http.Handle("/", http.FileServer(http.Dir(op.Path)))
err := http.ListenAndServe(":" + op.Port, Log(http.DefaultServeMux))
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
In your Log function, you are using fmt.Printf not fmt.Fprintf.
For example,
package main
import (
"fmt"
"log"
"net/http"
"os"
)
var logFile *os.File
func Log(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(logFile, "%s %s %s\n", r.RemoteAddr, r.Method, r.URL)
handler.ServeHTTP(w, r)
})
}
func main() {
var err error
logFile, err = os.Create("logfile.txt")
if err != nil {
log.Fatal("Log file create:", err)
return
}
defer logFile.Close()
}

Resources