Showing posts with label http. Show all posts
Showing posts with label http. Show all posts

Monday, July 27, 2015

Parsing HTTP Query String in Go

Parsing HTTP Query String in Go

TL;DR

Code and examples can be found here: https://github.com/creack/httpreq

HTTP Server

Go provides a very easy way to create a http server:

package main

import (
        "fmt"
        "log"
        "net/http"
)

func handler(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintf(w, "hello world\n")
}

func main() {
        log.Fatal(http.ListenAndServe(":8080", http.HandlerFunc(handler)))
}
// curl http://localhost:8080

But how to deal with data?

JSON body

A common way to pass data is via a json encoded body:

package main

import (
        "encoding/json"
        "fmt"
        "log"
        "net/http"
)

type query struct {
        Name string
}

func handler(w http.ResponseWriter, req *http.Request) {
        q := &query{}
        if err := json.NewDecoder(req.Body).Decode(q); err != nil {
                log.Printf("Error decoding body: %s", err)
                return
        }
        fmt.Fprintf(w, "hello %s\n", q.Name)
}

func main() {
        log.Fatal(http.ListenAndServe(":8080", http.HandlerFunc(handler)))
}
// curl -d '{"Name": "Guillaume"}' http://localhost:8080/

Query String

But what if we want to pass data via query string? Typically, pagination and extra data.

Go, once again, expose everything necessary:

package main

import (
        "fmt"
        "log"
        "net/http"
        "strconv"
)

func handler(w http.ResponseWriter, req *http.Request) {
        if err := req.ParseForm(); err != nil {
                log.Printf("Error parsing form: %s", err)
                return
        }
        l := req.Form.Get("limit")
        limit, err := strconv.Atoi(l)
        if err != nil {
                log.Printf("Error parsing limit: %s", err)
                return
        }

        dr := req.Form.Get("dryrun")
        dryRun, _ := strconv.ParseBool(dr)
        fmt.Fprintf(w, "hello world. Limit: %d, Dryrun: %t\n", limit, dryRun)
}

func main() {
        log.Fatal(http.ListenAndServe(":8080", http.HandlerFunc(handler)))
}
// curl 'http://localhost:8080?limit=42&dryrun=true'

As we can see, it works as expected, however, if we add more and more fields to our query string, the type conversions quickly become cumbersome.

A Better Query String management

We know how to convert any string to any type.
We know what data we are expecting.
We should be able to do something similar to json.Unmarshal.

Conversion functions

Let’s start with our previous example: we need an int and a bool. However, the strconv functions have different prototypes and return a value.

It would be interesting to write a small helper that will set a value instead of returning it. That way, we could instantiate our query object and pass the fields to be set. In order to do so, we need to use pointers.

package main

import (
        "fmt"
        "log"
        "net/http"
        "strconv"
)

type query struct {
        Limit  int
        DryRun bool
}

func parseBool(s string, dest *bool) error {
        // assume error = false
        *dest, _ = strconv.ParseBool(s)
        return nil
}

func parseInt(s string, dest *int) error {
        n, err := strconv.Atoi(s)
        if err != nil {
                return err
        }
        *dest = n
        return nil
}

func handler(w http.ResponseWriter, req *http.Request) {
        if err := req.ParseForm(); err != nil {
                log.Printf("Error parsing form: %s", err)
                return
        }
        q := &query{}
        if err := parseBool(req.Form.Get("dryrun"), &q.DryRun); err != nil {
                log.Printf("Error parsing dryrun: %s", err)
                return
        }
        if err := parseInt(req.Form.Get("limit"), &q.Limit); err != nil {
                log.Printf("Error parsing limit: %s", err)
                return
        }
        fmt.Fprintf(w, "hello world. Limit: %d, Dryrun: %t\n", q.Limit, q.DryRun)
}

func main() {
        log.Fatal(http.ListenAndServe(":8080", http.HandlerFunc(handler)))
}

Make it generic

It is a bit better, but still could be improved. What if we’d like to have this in a generic way?

As we can see, the conversion helpers have a very similar prototype, let’s make it the same using interface{}

package main

import (
        "fmt"
        "log"
        "net/http"
        "strconv"
)

type query struct {
        Limit  int
        DryRun bool
}

func parseBool(s string, dest interface{}) error {
        d, ok := dest.(*bool)
        if !ok {
                return fmt.Errorf("wrong type for parseBool: %T", dest)
        }
        // assume error = false
        *d, _ = strconv.ParseBool(s)
        return nil
}

func parseInt(s string, dest interface{}) error {
        d, ok := dest.(*int)
        if !ok {
                return fmt.Errorf("wrong type for parseInt: %T", dest)
        }
        n, err := strconv.Atoi(s)
        if err != nil {
                return err
        }
        *d = n
        return nil
}

func handler(w http.ResponseWriter, req *http.Request) {
        if err := req.ParseForm(); err != nil {
                log.Printf("Error parsing form: %s", err)
                return
        }
        q := &query{}
        if err := parseBool(req.Form.Get("dryrun"), &q.DryRun); err != nil {
                log.Printf("Error parsing dryrun: %s", err)
                return
        }
        if err := parseInt(req.Form.Get("limit"), &q.Limit); err != nil {
                log.Printf("Error parsing limit: %s", err)
                return
        }
        fmt.Fprintf(w, "hello world. Limit: %d, Dryrun: %t\n", q.Limit, q.DryRun)
}

func main() {
        log.Fatal(http.ListenAndServe(":8080", http.HandlerFunc(handler)))
}

Parsing object

Now that we have generic helpers, we can easily write a small object that will simplify the way we use it:

We need to store N parsing functions, so we’ll need a slice (or a map). In order to parse a field, we need the helper function, but we also need the original string and the destination.

We have our object!

type parsingMap []parsingMapElem

type parsingMapElem struct {
        Field string
        Fct   func(string, interface{}) error
        Dest  interface{}
}

Once our paringMap constructed, we then need to execute it, let’s write the loop logic:

func (p parsingMap) parse(form url.Values) error {
        for _, elem := range p {
                if err := elem.Fct(elem.Field, elem.Dest); err != nil {
                        return err
                }
        }
        return nil
}

We know can put everything together:

package main

import (
        "fmt"
        "log"
        "net/http"
        "net/url"
        "strconv"
)

// conversion helpers
func parseBool(s string, dest interface{}) error {
        d, ok := dest.(*bool)
        if !ok {
                return fmt.Errorf("wrong type for parseBool: %T", dest)
        }
        // assume error = false
        *d, _ = strconv.ParseBool(s)
        return nil
}

func parseInt(s string, dest interface{}) error {
        d, ok := dest.(*int)
        if !ok {
                return fmt.Errorf("wrong type for parseInt: %T", dest)
        }
        n, err := strconv.Atoi(s)
        if err != nil {
                return err
        }
        *d = n
        return nil
}

// parsingMap
type parsingMap []parsingMapElem

type parsingMapElem struct {
        Field string
        Fct   func(string, interface{}) error
        Dest  interface{}
}

func (p parsingMap) parse(form url.Values) error {
        for _, elem := range p {
                if err := elem.Fct(elem.Field, elem.Dest); err != nil {
                        return err
                }
        }
        return nil
}

// http server
type query struct {
        Limit  int
        DryRun bool
}

func handler(w http.ResponseWriter, req *http.Request) {
        if err := req.ParseForm(); err != nil {
                log.Printf("Error parsing form: %s", err)
                return
        }
        q := &query{}
        if err := (parsingMap{
                {"limit", parseInt, &q.Limit},
                {"dryrun", parseBool, &q.DryRun},
        }).parse(req.Form); err != nil {
                log.Printf("Error parsing query string: %s", err)
                return
        }

        fmt.Fprintf(w, "hello world. Limit: %d, Dryrun: %t\n", q.Limit, q.DryRun)
}

func main() {
        log.Fatal(http.ListenAndServe(":8080", http.HandlerFunc(handler)))
}

Going Further

I wrote this small library: https://github.com/creack/httpreq which provides more helpers and a cleaner API. It fits my current use case, but feel free to add any helper that can be missing :)

Thursday, July 23, 2015

HTTP and Error management in Go



HTTP and Error management in Go

Go comes with a great standard library including net/http which allow a developer to create a reliable http server very easily.

TL;DR

Code and example can be found here: https://github.com/creack/ehttp

Basic http server

To provide context, let’s take a look at a basic http server in Go:

package main

import (
        "fmt"
        "log"
        "net/http"
)

func handler(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintln(w, "hello world")
}

func main() {
        http.HandleFunc("/", handler)
        log.Fatal(http.ListenAndServe(":8080", nil))
}

The first step is to define a handler. In order to be understood by the Go’s http library, the handler needs to follow this specific prototype: func(http.ResponseWriter, *http.Request) which is defined as the http.HandlerFunc type.
Once we have our handler, we can register it on a specific route and then start the server.

This is great! In very few lines of code, we have a working web server ready to go!

Error management

You might have noticed: our handler does not return an error… But luckily, the net/http package exposes the http.Error function in order to report an error to the client. This will set the Content-Type, send a custom http status header and write the error as the body.

Small example:

func handler(w http.ResponseWriter, req *http.Request) {
        if err := doSomething(); err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
        }
        fmt.Fprintln(w, "hello world")
}

As you can imagine, this can become cumbersome pretty fast. We could imagine writing a wrapper for http.Error which is going to log the error, send instrumentations and then call http.Error, but when doing a lot in a handler, we always need to have that call + return.
A nice way to go would be to consider the handler as a simple entrypoint and avoid doing any logic directly in the handler. This is a good approach, especially when you don’t want to be tight to http and able to switch to other protocols.

Custom Handler

A solution is to create a custom handler, let’s try:

type HandlerFunc func(http.ResponseWriter, *http.Request) error

Pretty straight forward for now, but wait, http.HandleFunc expects a different prototype, so we won’t be able to use it anymore, right?
Kind of.. Right in a sense that we can’t use it directly, but we always can work around anything ;)

http.HandlerFunc

In order to “convert” our custom handler to the native http one, we need to write a middleware and we are going to use that to handle our error management.

So, what is a middleware? It a simple “layer” that comes in between the client’s request and our final handler.

// MWError is the main middleware. When an error is returned, it send
// the data to the client if the header hasn't been sent yet, otherwise, log them.
func MWError(hdlr HandlerFunc) http.HandlerFunc {
        return func(w http.ResponseWriter, req *http.Request) {
                if err := hdlr(w, req); err != nil {
                        http.Error(w, err.Error(), http.StatusInternalError)
                        return
                }
        }
}

So, we have a function that takes our custom handler function as a parameter and return a native http HandlerFunc.

router.HandleFunc("/", MWError(hdlr))

When the client calls our server, it ends up in that newly generated native http Handler which in turn calls our custom one and then handle the error.

http.Handler

Alternatively, we can implement the http.Handler interface on our custom type so it can be used by all the http functions expecting that interface (I am thinking mainly about http.Handle and http.ListenAndServe

type HandlerFunc func(http.ResponseWriter, *http.Request) error

func (hdlr HandlerFunc) ServeHTTP(w http.ResponseWriter, req *http.Request) {
        if err := hdlr(w, req); err != nil {
                http.Error(w, err.Error(), http.StatusInternalError)
                return
        }
}

Going further

Now that we have a custom http handler and we can return error, we can think about improvements:
- smarter error management
- custom error type holding the HTTP Status
- panic recovery
- adaptors for non-standard library routers (gorilla, httprouter, etc)
- headers detection (we can’t send error headers if they have already been sent)

I started a small library that implements all this: https://github.com/creack/ehttp, it is very very simple and close to the standard library.

Saturday, June 20, 2015

Privileged Listen in Go

Introduction

Go does not play well with Forks and User permission. The reason is because it is a threaded runtime and there is no mechanism for clean fork or clean setuid.

To work around the fork issue, Go exposes syscall.ForkExec() which perform the fork (locked) but always perform an Exec() in the forked process, resulting in the calling one to disappear (overridden).

The Issue.

https://github.com/golang/go/issues/1435

$> GOMAXPROCS=4 ./test 65534 65534

and note output:

goroutine 1: uid=0 euid=0 gid=0 egid=0
goroutine 2: uid=0 euid=0 gid=0 egid=0
goroutine 3: uid=0 euid=0 gid=0 egid=0
goroutine 4: uid=0 euid=0 gid=0 egid=0
goroutine 5: uid=0 euid=0 gid=0 egid=0
goroutine 6: uid=0 euid=0 gid=0 egid=0
goroutine 7: uid=0 euid=0 gid=0 egid=0
goroutine 8: uid=0 euid=0 gid=0 egid=0
goroutine 9: uid=0 euid=0 gid=0 egid=0
goroutine 0: uid=65534 euid=65534 gid=65534 egid=65534
goroutine 1: uid=0 euid=0 gid=0 egid=0
goroutine 2: uid=0 euid=0 gid=0 egid=0
goroutine 3: uid=0 euid=0 gid=0 egid=0
goroutine 4: uid=0 euid=0 gid=0 egid=0
goroutine 5: uid=0 euid=0 gid=0 egid=0
goroutine 6: uid=0 euid=0 gid=0 egid=0
goroutine 7: uid=0 euid=0 gid=0 egid=0
goroutine 8: uid=0 euid=0 gid=0 egid=0
goroutine 9: uid=0 euid=0 gid=0 egid=0
goroutine 0: uid=65534 euid=65534 gid=65534 egid=65534

It would be annoying if our http Handler was ran as root!

The solution

To solve this issue, I wrote a small util: https://github.com/creack/golisten.

The idea is simple: perform the listen as root and then override the whole process with yourself as non-root.

package main

import (
    "fmt"
    "log"
    "net/http"
    "os/user"

    "github.com/creack/golisten"
)

func handler(w http.ResponseWriter, req *http.Request) {
    u, err := user.Current()
    if err != nil {
        log.Printf("Error getting user: %s", err)
        return
    }
    fmt.Fprintf(w, "%s\n", u.Uid)
}

func main() {
    http.HandleFunc("/", handler)
    log.Fatal(golisten.ListenAndServe("guillaume", ":80", nil))
}

Note

golisten is intended to be used with the http lib, but the concept can be used for any privilege de-escalation.
It is safe because we override the whole process, so all our active thread are from the child process.

Caveat

As we re-exec ourself, we need to be careful with what is done prior the call the golisten.
Maybe a bit more cumbersome, but it is best to use golisten.Listen at the beginning and pass around the listener to http.Serve() later on.

package main

import (
        "fmt"
        "log"
        "net/http"
        "os/user"

        "github.com/creack/golisten"
)

func handler(w http.ResponseWriter, req *http.Request) {
        u, err := user.Current()
        if err != nil {
                log.Printf("Error getting user: %s", err)
                return
        }
        fmt.Fprintf(w, "%s\n", u.Uid)
}

func main() {
        ln, err := golisten.Listen("guillaume", "tcp", ":80")
        if err != nil {
                log.Fatal(err)
        }
        http.HandleFunc("/", handler)
        println("ready")
        log.Fatal(http.Serve(ln, nil))
}