Skip to content
This repository has been archived by the owner on Jul 6, 2023. It is now read-only.

Commit

Permalink
Merge pull request #40 from lpabon/async_handlefunc
Browse files Browse the repository at this point in the history
Support for a handler function in Async API
  • Loading branch information
Luis Pabón committed Jul 1, 2015
2 parents ed6ffa2 + 61d7b97 commit 479c448
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 0 deletions.
18 changes: 18 additions & 0 deletions handlers/asynchttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,24 @@ func (a *AsyncHttpManager) NewHandler() *AsyncHttpHandler {
return handler
}

func (a *AsyncHttpManager) AsyncHttpRedirectFunc(w http.ResponseWriter,
r *http.Request,
handlerfunc func() (string, error)) {

handler := a.NewHandler()
go func() {
url, err := handlerfunc()
if err != nil {
handler.CompletedWithError(err)
} else if url != "" {
handler.CompletedWithLocation(url)
} else {
handler.Completed()
}
}()
http.Redirect(w, r, handler.Url(), http.StatusAccepted)
}

func (a *AsyncHttpManager) HandlerStatus(w http.ResponseWriter, r *http.Request) {
// Get the id from the URL
vars := mux.Vars(r)
Expand Down
138 changes: 138 additions & 0 deletions handlers/asynchttp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,94 @@ func TestHandlerCompletions(t *testing.T) {

}

func TestAsyncHttpRedirectFunc(t *testing.T) {
// Setup asynchronous manager
route := "/x"
manager := NewAsyncHttpManager(route)

// Setup the route
router := mux.NewRouter()
router.HandleFunc(route+"/{id}", manager.HandlerStatus).Methods("GET")
router.HandleFunc("/result", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "HelloWorld")
}).Methods("GET")

// Start testing error condition
handlerfunc := func() (string, error) {
return "", errors.New("Test Handler Function")
}

// The variable 'handlerfunc' can be changed outside the scope to
// point to a new function. Isn't Go awesome!
router.HandleFunc("/app", func(w http.ResponseWriter, r *http.Request) {
manager.AsyncHttpRedirectFunc(w, r, handlerfunc)
}).Methods("GET")

// Setup the server
ts := httptest.NewServer(router)
defer ts.Close()

// Get /app url
r, err := http.Get(ts.URL + "/app")
tests.Assert(t, r.StatusCode == http.StatusAccepted)
tests.Assert(t, err == nil)
location, err := r.Location()
tests.Assert(t, err == nil)

// Expect the error
r, err = http.Get(location.String())
tests.Assert(t, err == nil)
tests.Assert(t, r.StatusCode == http.StatusInternalServerError)
body, err := ioutil.ReadAll(r.Body)
r.Body.Close()
tests.Assert(t, err == nil)
tests.Assert(t, string(body) == "Test Handler Function\n")

// Set handler function to return a url to /result
handlerfunc = func() (string, error) {
return "/result", nil
}

// Get /app url
r, err = http.Get(ts.URL + "/app")
tests.Assert(t, r.StatusCode == http.StatusAccepted)
tests.Assert(t, err == nil)
location, err = r.Location()
tests.Assert(t, err == nil)

// Should have the content from /result. http.Get() automatically
// retreives the content when a status of SeeOther is set and the
// Location header has the next URL.
r, err = http.Get(location.String())
tests.Assert(t, err == nil)
tests.Assert(t, r.StatusCode == http.StatusOK)
body, err = ioutil.ReadAll(r.Body)
r.Body.Close()
tests.Assert(t, err == nil)
tests.Assert(t, string(body) == "HelloWorld")

// Test no redirect, simple completion
handlerfunc = func() (string, error) {
return "", nil
}

// Get /app url
r, err = http.Get(ts.URL + "/app")
tests.Assert(t, r.StatusCode == http.StatusAccepted)
tests.Assert(t, err == nil)
location, err = r.Location()
tests.Assert(t, err == nil)

// Should be success
r, err = http.Get(location.String())
tests.Assert(t, err == nil)
tests.Assert(t, r.StatusCode == http.StatusNoContent)
tests.Assert(t, r.ContentLength == 0)

}

func TestHandlerConcurrency(t *testing.T) {

// Setup asynchronous manager
Expand Down Expand Up @@ -250,3 +338,53 @@ func TestHandlerApplication(t *testing.T) {
}

}

func TestApplicationWithRedirectFunc(t *testing.T) {

// Setup asynchronous manager
route := "/x"
manager := NewAsyncHttpManager(route)

// Setup the route
router := mux.NewRouter()
router.HandleFunc(route+"/{id}", manager.HandlerStatus).Methods("GET")
router.HandleFunc("/result", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "HelloWorld")
}).Methods("GET")

router.HandleFunc("/app", func(w http.ResponseWriter, r *http.Request) {
manager.AsyncHttpRedirectFunc(w, r, func() (string, error) {
time.Sleep(100 * time.Millisecond)
return "/result", nil
})
}).Methods("GET")

// Setup the server
ts := httptest.NewServer(router)
defer ts.Close()

// Get /app url
r, err := http.Get(ts.URL + "/app")
tests.Assert(t, r.StatusCode == http.StatusAccepted)
tests.Assert(t, err == nil)
location, err := r.Location()
tests.Assert(t, err == nil)

for {
// Since Get automatically redirects, we will
// just keep asking until we get a body
r, err := http.Get(location.String())
tests.Assert(t, err == nil)
tests.Assert(t, r.StatusCode == http.StatusOK)
if r.ContentLength > 0 {
body, err := ioutil.ReadAll(r.Body)
r.Body.Close()
tests.Assert(t, err == nil)
tests.Assert(t, string(body) == "HelloWorld")
break
}
}

}

0 comments on commit 479c448

Please sign in to comment.