One-liner for serving static files in Go explained

Whenever I want to set up a server for serving static files in Go, which is not often, I always have to spend some time to figure out the following one-liner.

http.Handle("/images/", http.StripPrefix("/images/", http.FileServer(http.Dir("./images"))))

What confuses me probably the most here is all three strings being practically the same.

So in this post I will explain what this does for my future-self so he won’t have to google about it and will have it explained here in a way he’ll like it. 🙂

For this particular example (where one-liner above applies) let’s say we are storing images in ./images folder on a server and accessing them from outside of the server with <server's domain>/images/article2.jpeg

  • We shouldn’t set up a file server at root endpoint if our server accepts other endpoints.
  • http.FileServer(http.Dir("./images"))) exposes folder ./images as a handler.
  • http.StripPrefix is used because images are stored in ./images` folder and not in it’s subfolder images (we want ./images/article2.jpeg not ./images/images/article2.jpeg). It strips prefix from request’s URL.

A friendly reminder

Serving static files is more efficient using dedicated web server (Nginx, Apache,..). This approach also aids to better security of your application.

Gut and Psychology Syndrome (GAPS)

In last year I kinda became obsessed with healthy foods. I decided to eliminate sugar (not counting fruit) and processed food and instead incorporate more healing foods into my diet. Examples include green tea, kefir, bone broths and other other inflammatory food.

I also became acquainted with so-called healing diets, namely Specific-Carbohydrate diet (SCD diet) and Gut and Psychology (GAPS) diet. I’ve studied them both but haven’t practiced them. Also for a SCD diet I think it’s a way too restrictive. But what’s great about both diets are the fact they are backed by books, written by authors of diets (SCD diet has Breaking the Vicious Cycle by Elaine Gottschall, GAPS diet has Gut and Psychology Syndrome by Natasha Campbell-McBride). I read both of them. Each of them was very informative.

I’ve enjoyed reading Gut and Psychology Syndrome because it has chapters which describe how human gut works (beneficial, opportunistic flora), how it’s state influences our immune system, what harms it. That’s what interests me very much in general. But other chapters (about genetics, vaccination) deserves attention too because we live in a world where it’s lifestyle (diet rich in sugar and processed food, medicine) does tremendous damage to our body and next generations and are responsible for diseases, which weren’t so widespread till the 20th century. The author, Natasha Campbell-McBride, the doctor and a mother of a recovered autistic child thanks to her GAPS diet, writes in thought-provoking style from time to time which also makes for an entertaining read.

The GAPS diet is explained in her book and that’s what I was looking too when borrowing the book from hometown library. It’s premise is to heal a disease by healing the gut. I intend to incorporate additional food to my diet from it.

I definitely recommend the book to parents who have children (whether they have health problems or not) and people who wants to lead a more normal life with less or free of digestive problems and better immunity.

Milk and bread back on the table

For the most part of the year 2017 I was on a diet without milk and bread because I found out I feel more comfortable without these two foods (apparently I have mild lactose intolerance and sensitivity to gluten). But after educating myself about fermented food and than preparing some of it at home, I’ve put them back on the table. Turns out kefir and sourdough bread don’t make me feel uncomfortable.

Kefir is a beverage you get from milk, which has been fermented with the help of kefir grains at room temperature for a day or two. Kefir grains are live culture, mix of beneficial bacteria and yeasts that feed off sugar in milk – lactose. That’s why kefir is a good option even for lactose intolerant people. Kefir is also a highly probiotic drink, more than a yoghurt and easy to prepare.

I already wrote an article about sourdough bread, how I made it. I have also mentioned the reason why it doesn’t make me uncomfortable. It’s because bacteria and yeasts in dough also break gluten apart during it’s long fermentation/rising time (it’s still not suited for people with celiac disease though).

These foods take long time to prepare, but 98% of work do bacteria and yeasts while bottle of milk/dough is just sitting on a kitchen counter. After you get the hang of the process, you can arrange it your way, so it fits your busy schedule. I’ll admit the learning wasn’t as smooth and quick as I’d like but I learned to enjoy the process and the result is so rewarding. Also the process teaches you to be patient which is a good life virtue to have.

I’ll be so definitely consuming kefir and sourdough bread more in 2018. I even come across recipes which use kefir as a sourdough starter and this looks like an intriguing idea to try.

Error handling in concurrent programs in Golang

Error handling in concurrent programs in Go consists of little more work than

if err != nil {
   return err
}

because the return value doesn’t reach intended receiver (for example in parent function where function was fired as a goroutine using go keyword). Just as we use channel for sending resulting data, we must also use channel for sending error. And we can do this using single channel of type Data which is a struct that will hold data and error for the data. And than when we range over channel we first check if there’s an error (and when we are done, when all tasks have been completed, we close the channel).

Using same channel for data and error simplifies concurrency, we don’t have to handle scenarios of completed tasks end error handling separately.

So here’s a simple example of what I have in mind (with sending HTTP request as a concurrent task).

type Data struct {
    URL      string
    IsAvailable bool
    Err      error
}

func getAvailableSites(urls []string) ([]string, error) {
    var wg sync.WaitGroup
    urlChan := make(chan *Data)

    go func() {
        wg.Wait()
        close(urlChan)
    }()

    for _, url := range urls {
        go checkIfSiteAvailable(&wg, urlChan, url)
    }

    availableSites := []string{}
    for data := range urlChan {
        if data.Err != nil {
            return nil, fmt.Errorf("problem getting url: %v", data.Err)
        }
        if (data.IsAvailable) {
            availableSites = append(availableSites, data.URL)
        }
    }
    return availableSites, nil
}

func checkIfSiteAvailable(wg *sync.WaitGroup, urlChan chan<- *Data, url string) {
    defer wg.Done()

    resp, err := http.Head(url)
    if err != nil {
        urlChan <- &Data{Err: err}
        return
    }

    if resp.StatusCode != http.StatusOK {
        urlChan <- &Data{Err: fmt.Errorf("status code not OK for %s", url)}
        return
    }

    urlChan <- &Data{URL: url, Available: true}
}

I won’t be describing logic here just a steps related to concurrency and error handling in it. In getAvailableSites we

  • set up a channel we’ll be using for sending the data for specific URL (with error as part of data),
  • a WaitGroup to specify how many goroutines we’ll fire and after all finish, Go will know to close channel,
  • define and fire a goroutine which waits for all goroutines to finish and than close a channel,
  • for each URL we call a function checkIfSiteAvailable as a goroutine (alonside URL we pass channel so we can send resulting data to it and a WaitGroup to be able to indicate task is finished)
  • iterate over a channel, which will finish after we close the channel (from 3rd point). Iterating over a channel is a blocking operation so we don’t have to worry data from channel will get lost. In for loop we check for error and return it as if no concurrency was involved.

In checkIfSiteAvailable we defer call to wg.Done to indicate task is finished. And whenever there’s an error we send instance of Data with only Err field specified because in this simple example we don’t need other fields. Lastly, we send the actual data (wrapped in the Data struct).

My first sourdough bread

This week I baked my first bread. It’s a special type of bread, called sourdough bread. It’s made from no industrial yeast, but from sourdough starter. Sourdough starter is a mixture of flour and water that has been left for some time on the counter and on warm, preferably more than 22 °C. During that time yeast and good bacteria from the air start fermenting the mixture (consuming the sugar and to a degree, breaking gluten), causing it to rise due to bubbles from CO2, produced during fermentation. When preparing the dough for this type of bread, it has to rise for longer period of time than usual.

I’m quite happy with the outcome. Starter I used to make a leaven was well made, the dough has risen sufficiently, but there is a room for improvement when it comes to shaping the dough after initial rise (also called bulk fermentation which I enjoyed very much though). It come out from the oven very misshapen – one loaf even broke apart because of using too much flour.

I’m definitely not disappointed because I’m allowing myself to make mistakes and making “shitty first drafts“. No doubt practice and experience will improve the process of making the bread. Because it tasted good and cannot wait to bake new bread, I’ve decided to equip myself with missing utensils from making a bread ASAP (namely bench scraper, baker’s lame, a bowel to store dough for final rise and which also gives actual shape to baked bread).

If you want to make your own sourdough bread, check out the following posts which I’ve been using as a reference while making it (or buy this book both resources are based on).

  • The Perfect Loaf
  • Phickle (first post in a series about making sourdough bread, more of them are linked at the bottom of the article)