Fun with Go
Posted on November 14th, 2009 in General | No Comments »
So I spent some time on Go and there was one frustrating thing I found. I could not make an http request and specify headers with the simple http client. I finally looked at the package source for the http package and luckily it was so simple that I was able to create my own Get method that allowed headers. Basically all this code does is pull your friend status activity from twitter. You pass your twitter user and pass at the command line and it dumps the xml from twitter api. Next I will add formatted output and the ability to post your status to twitter. This was just an exercise for fun so I’m not sure how much more I’ll put into it. I think I’ll try it in python next.
I was able to play with many features of the language but I didn’t get to threading yet. I have command line parsing, string encoding to base 4 and simple http client with basic authentication headers. Here is the code including the modification to the http.Get function which turned out to be trivial. Let me know if you want the full source code.
usage:
twittergo -user username -pass password
package main
import (”os”;
“fmt”;
“flag”;
“http”;
“io”;
“encoding/base64″;
“strings”;
“bytes”;
“net”;
“bufio”;
“strconv”;
)
func main()
{
var user string;
var pass string;
flag.StringVar(&user, “user”, “username”, “twitter user name”);
flag.StringVar(&pass, “pass”, “password”, “twitter password”);
flag.Parse();
userpass := user + “:” + pass;
var userPassEnc []byte;
buffer := bytes.NewBuffer(userPassEnc);
enc := base64.NewEncoder(base64.StdEncoding, buffer);
enc.Write(strings.Bytes(userpass));
enc.Close();
fmt.Printf(”user: “);
fmt.Printf(user);
fmt.Printf(”\npass: “);
fmt.Printf(pass);
fmt.Printf(”\nuserpass: “);
fmt.Printf(userpass);
fmt.Printf(”\nuserpassenc: “);
fmt.Printf(buffer.String());
fmt.Printf(”\n”);
headers := map[string]string{
“Host” : “twitter.com”,
“Authorization”: “Basic ” + buffer.String(),
};
var r *http.Response;
r,url,error := GetWithHeaders(”http://twitter.com/statuses/friends_timeline.xml”, headers);
fmt.Printf(url);
bytes,error :=io.ReadAll(r.Body);
r.Body.Close();
os.Stdout.WriteString(”response: “);
os.Stdout.Write(bytes);
os.Stdout.WriteString(”\n”);
if (error != nil)
{
os.Stdout.WriteString(”error: “);
os.Stdout.WriteString(error.String());
os.Stdout.WriteString(”\n”);
}
}
// Get issues a GET to the specified URL. If the response is one of the following
// redirect codes, it follows the redirect, up to a maximum of 10 redirects:
//
// 301 (Moved Permanently)
// 302 (Found)
// 303 (See Other)
// 307 (Temporary Redirect)
//
// finalURL is the URL from which the response was fetched — identical to the input
// URL unless redirects were followed.
//
// Caller should close r.Body when done reading it.
func GetWithHeaders(url string, headers map[string]string) (r *http.Response, finalURL string, err os.Error) {
// TODO: if/when we add cookie support, the redirected request shouldn’t
// necessarily supply the same cookies as the original.
// TODO: set referrer header on redirects.
for redirect := 0; ; redirect++ {
if redirect >= 10 {
err = os.ErrorString(”stopped after 10 redirects”);
break;
}
var req http.Request;
req.Header = headers;
if req.URL, err = http.ParseURL(url); err != nil {
break
}
if r, err = send(&req); err != nil {
break
}
if shouldRedirect(r.StatusCode) {
r.Body.Close();
if url = r.GetHeader(”Location”); url == “” {
err = os.ErrorString(fmt.Sprintf(”%d response missing Location header”, r.StatusCode));
break;
}
continue;
}
finalURL = url;
return;
}
err = &http.URLError{”Get”, url, err};
return;
}