I have written a simple client/server in Go that will do an HTTP GET over TLS, but I'm trying to also make it capable of doing an HTTP POST over TLS.
In the example below index.html just contains the text hello, and the HTTP GET is working fine. I want the client to get the HTTP GET and write back, hello world to the server.
client
package main
import (
"crypto/tls"
"fmt"
"io/ioutil"
"net/http"
"strings"
)
func main() {
link := "https://10.0.0.1/static/index.html"
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
response, err := client.Get(link)
if err != nil {
fmt.Println(err)
}
defer response.Body.Close()
content, _ := ioutil.ReadAll(response.Body)
s := strings.TrimSpace(string(content))
fmt.Println(s)
// out := s + " world"
// Not working POST...
// resp, err := client.Post(link, "text/plain", &out)
}
server
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/static/", func (w http.ResponseWriter, r *http.Request) {
fmt.Println("Got connection!")
http.ServeFile(w, r, r.URL.Path[1:])
})
log.Fatal(http.ListenAndServeTLS(":443", "server.crt", "server.key", nil))
}
I also currently have nothing to handle the POST on the server side, but I just want it to print it out to the screen so when I run the client I will see the server print hello world.
How should I fix my client code to do a proper POST? And what should the corresponding server code look like to accept the POST? Any help would be appreciated, I'm having trouble finding HTTPS/TLS POST examples.
You didn't share the error message, but I assume the client.Post call wasn't allowing a string as its third parameter, because it requires an io.Reader. Try this instead:
out := s + " world"
resp, err := client.Post(link, "text/plain", bytes.NewBufferString(out))
On the server side, you already have the right code set up to handle the POST request. Just check the method:
http.HandleFunc("/static/", func (w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
// handle POST requests
} else {
// handle all other requests
}
})
I noticed one other issue. Using index.html probably won't work here. http.ServeFile will redirect that path. See https://golang.org/pkg/net/http/#ServeFile:
As a special case, ServeFile redirects any request where r.URL.Path
ends in "/index.html" to the same path, without the final
"index.html". To avoid such redirects either modify the path or use
ServeContent.
I'd suggest just using a different file name to avoid that issue.
Related
Whenever a request is made from localhost:9000/ I want to redirect the user to localhost:9000/#/trade/gem/GEM . The problem I am getting is that I get infinite redirects because "/" preceeds every url. How do I make it so that the user is only redirected if they visit the exact url of localhost:9000/ ? My code is below:
var newUrl string = "/#/trade/gem/GEM"
func handleRedirect(rw http.ResponseWriter, req *http.Request) {
http.Redirect(rw, req, newUrl, http.StatusSeeOther)
}
func main() {
http.Handle("/#/trade/", fs)
http.HandleFunc("/", handleRedirect) //This is the exact url I want to redirect from
http.ListenAndServe(":9000", nil)
}
When the / is registered then all the url will redirect to this until the other pattern is not registered.To solve this and handling only specific patterns you can write a custom handler and you can implement a logic where it redirects to the specific url whenever the localhost:9000/ is opened.
package main
import (
"fmt"
"net/http"
)
type Handler struct{}
var NewUrl string = "/trade/gem/GEM"
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
uri := r.URL.Path
if uri == "/" {
http.Redirect(w, r, NewUrl, http.StatusSeeOther)
}
fmt.Fprintf(w, uri)
return
}
func main() {
handler := new(Handler)
http.ListenAndServe(":9000", handler)
}
For more details check http.handler
Note: You can not use # in the uri.
go version go1.8.1 windows/amd64
"net/http" package used to build http request.
req, err := http.NewRequest("GET",`http://domain/_api/Web/GetFolderByServerRelativeUrl('` + root_folder_url + `')?$expand=Folders,Files`, nil)
Here if I print url it shows
http://domain/_api/Web/GetFolderByServerRelativeUrl%28%27rooturl%27%29?$expand=Folders,Files
Not understanding why url parser is replacing ' to %27 here.
Whereas I need ' to be sent as is while requesting.
The http.NewRequest function calls url.Parse to set the Request.URL. The URL.RequestURI method is called to get the request URI written to the network.
An application can override any transformation made by Parse/RequestURI by setting the request URL Opaque field:
req, err := http.NewRequest("GET", "http://domain/", nil)
if err != nil {
// handle error
}
req.URL.Opaque = `/_api/Web/GetFolderByServerRelativeUrl('` + root_folder_url + `')?$expand=Folders,Files`
In this snippet, the argument to NewRequest specifies the protocol and host for the request. The opaque value specifies the request URI written to the network. The request URI does not include host or protocol.
Using a URL that has worked in the past, I know receive a parsing error from net/url. What's wrong with it?
parse postgres://user:abc{DEf1=ghi#example.com:5432/db?sslmode=require: net/url: invalid userinfo
Sample application
See https://play.golang.com/p/mQZaN5JN3_q to run.
package main
import (
"fmt"
"net/url"
)
func main() {
dsn := "postgres://user:abc{DEf1=ghi#example.com:5432/db?sslmode=require"
u, err := url.Parse(dsn)
fmt.Println(u, err)
}
Well, you can just
url.QueryEscape("your#$%^&*(proper$#$%%^(password")
and use this one to parse your url.
It turns out up until Go v1.9.3 net/url didn't validate the user info when parsing a url. This may break existing applications when compiled using v1.9.4 if the username or password contain special characters.
It now expects the user info to be percent encoded string in order to handle special characters. The new behaviour got introduced in ba1018b.
Fixed sample application
package main
import (
"fmt"
"net/url"
)
func main() {
dsn1 := "postgres://user:abc{DEf1=ghi#example.com:5432/db?sslmode=require" // this works up until 1.9.3 but no longer in 1.9.4
dsn2 := "postgres://user:abc%7BDEf1=ghi#example.com:5432/db?sslmode=require" // this works everywhere, note { is now %7B
u, err := url.Parse(dsn1)
fmt.Println("1st url:\t", u, err)
u, err = url.Parse(dsn2)
fmt.Println("2nd url:\t", u, err)
}
Run the code on https://play.golang.com/p/jGIQgbiKZwz.
Use url.UserPassword func :
package main
import (
"fmt"
"net/url"
)
func main() {
dsn := "postgres://example.com:5432/db?sslmode=require"
u, err := url.Parse(dsn)
if err != nil {
fmt.Printf("ERROR: %v\n", err)
return
}
u.User = url.UserPassword("user", "abc{DEf1=ghi")
fmt.Println("url:\t", u)
}
So, I'm using the net/http package. I'm GETting a URL that I know for certain is redirecting. It may even redirect a couple of times before landing on the final URL. Redirection is handled automatically behind the scenes.
Is there an easy way to figure out what the final URL was without a hackish workaround that involves setting the CheckRedirect field on a http.Client object?
I guess I should mention that I think I came up with a workaround, but it's kind of hackish, as it involves using a global variable and setting the CheckRedirect field on a custom http.Client.
There's got to be a cleaner way to do it. I'm hoping for something like this:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
// Try to GET some URL that redirects. Could be 5 or 6 unseen redirections here.
resp, err := http.Get("http://some-server.com/a/url/that/redirects.html")
if err != nil {
log.Fatalf("http.Get => %v", err.Error())
}
// Find out what URL we ended up at
finalURL := magicFunctionThatTellsMeTheFinalURL(resp)
fmt.Printf("The URL you ended up at is: %v", finalURL)
}
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
resp, err := http.Get("http://stackoverflow.com/q/16784419/727643")
if err != nil {
log.Fatalf("http.Get => %v", err.Error())
}
// Your magic function. The Request in the Response is the last URL the
// client tried to access.
finalURL := resp.Request.URL.String()
fmt.Printf("The URL you ended up at is: %v\n", finalURL)
}
Output:
The URL you ended up at is: http://stackoverflow.com/questions/16784419/in-golang-how-to-determine-the-final-url-after-a-series-of-redirects
I would add a note that http.Head method should be enough to retrieve the final URL. Theoretically it should be faster comparing to http.Get as a server is expected to send back just a header:
resp, err := http.Head("http://stackoverflow.com/q/16784419/727643")
...
finalURL := resp.Request.URL.String()
...
Here is what I have so far which returns a "400 error". Am I doing something wrong? I can't figure it out why is not working as the request is pretty straightforward
package main
import (
"code.google.com/p/goauth2/oauth"
"fmt"
"log"
)
func main() {
cachefile := "cache.json"
code := "4/xxxxx.8uFT5Z0slpMbJvIeHux6iLY_9k7ajw" //the code received from the URL redirect
// Set up a configuration.
config := &oauth.Config{
ClientId: "xx.apps.googleusercontent.com",
ClientSecret: "cWP3HudD3XmaP33j8",
RedirectURL: "https://crm.com/sender/gmail/auth/callBack",
Scope: "https://www.googleapis.com/auth/gmail.compose",
AuthURL: "https://accounts.google.com/o/oauth2/auth",
TokenURL: "https://accounts.google.com/o/oauth2/token",
AccessType: "offline",
TokenCache: oauth.CacheFile(cachefile),
}
// Set up a Transport using the config.
transport := &oauth.Transport{Config: config}
token, err := config.TokenCache.Token()
if err != nil {
token, err = transport.Exchange(code)
if err != nil {
log.Fatal("Exchange:", err)
}
}
// (The Exchange method will automatically cache the token.)
transport.Token = token
fmt.Println(token)
}
Result
Exchange:OAuthError: updateToken: Unexpected HTTP status 400 Bad Request
I would recommend to use 'one-time code flow', as described in the documentation:
To take advantage of all of the benefits of Google+ Sign-In you must use a hybrid server-side flow where a user authorizes your app on the client side using the JavaScript API client and you send a special one-time authorization code to your server. Your server exchanges this one-time-use code to acquire its own access and refresh tokens from Google for the server to be able to make its own API calls, which can be done while the user is offline. This one-time code flow has security advantages over both a pure server-side flow and over sending access tokens to your server.
As code can be used just once there are less chances of compromising user's account.
Client code is pretty straight forward, follow the example in step 3.
For server-side, I would recommend using the package oauth2 instead of goauth2.
$ go get code.google.com/p/google-api-go-client/plus/v1
$ go get github.com/golang/oauth2
$ go get google.golang.org/appengine
For some reason, oauth2 package requires also appengine package.
Exchanging the one-time code for a reusable token can be done using function NewTransportWithCode:
func exchangeCode(code string) (*oauth2.Token, *oauth2.Transport, error) {
config, err := google.NewConfig(&oauth2.Options{
ClientID: CLIENT_ID,
ClientSecret: CLIENT_SECRET,
RedirectURL: "postmessage",
Scopes: []string{"https://www.googleapis.com/auth/plus.login"},
})
if err != nil {
return &oauth2.Token{}, &oauth2.Transport{}, err
}
transport, err := config.NewTransportWithCode(code)
if err != nil {
return &oauth2.Token{}, &oauth2.Transport{}, err
}
token := transport.Token()
return token, transport, nil
}
And finally the code you created in step 3 can submit one time code to a handler listening at /oauth2:
func oauth2Handler(w http.ResponseWriter, r *http.Request) {
// TODO Check request has...
// - Method: POST
// - Content-Type: application/octet-stream; charset=utf-8
// - CSRF Token http://goo.gl/mNCjJm
body, err := ioutil.ReadAll(r.Body)
defer r.Body.Close()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
code := string(body[:])
token, transport, err := exchangeCode(code)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// From here you can use the transport
client := http.Client{Transport: transport}
service, err := plus.New(&client)
if err != nil {
return nil, err
}
// https://www.googleapis.com/plus/v1/people/me
person, err := service.People.Get("me").Do()
// ...
}
func main() {
http.HandleFunc("/oauth2", oauth2Handler)
log.Fatal(http.ListenAndServe(":8000", nil))
}
Some error handling is missing, but you get the idea.