goauth2 and urlfetch: invalid memory address or nil pointer dereference

216 views
Skip to first unread message

Rich Churcher

unread,
Feb 9, 2013, 12:33:40 AM2/9/13
to google-ap...@googlegroups.com
With no answers on SO to the following: http://stackoverflow.com/questions/14729004/runtime-error-when-attempting-to-read-http-response-body-having-used-urlfetch , I'm echoing the question here as I'm still a bit stumped. Coming to the conclusion that a urlfetch transport just can't be used with goauth2, but maybe you guys can point out any flaws in that logic.

Briefly:

    package app

    import (
    "fmt"
    "net/http"
    "appengine"
    "appengine/urlfetch"
    )

    func init () {
    http.HandleFunc("/", home)
    }

    func home(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    config := &oauth.Config{
    ClientId:     "<redacted>",
    ClientSecret: "<redacted>",
    Scope:        "email",
    AuthURL:      "https://www.facebook.com/dialog/oauth",
    RedirectURL:  "http://example.com/",
    }

    code := r.FormValue("code")
    if code == "" {
    http.Redirect(w, r, config.AuthCodeURL("foo"), http.StatusFound)
    }
    t := &oauth.Transport{Config: config, Transport: &urlfetch.Transport{Context: c}}
    tok, _ := t.Exchange(code)
    graphResponse, _ := t.Client().Get("https://graph.facebook.com/me")

    fmt.Fprintf(w, "<pre>%s<br />%s</pre>", tok, graphResponse)
    }

Errors all nil, but ignored for this example. This runs fine, and produces a valid token and what sure looks like an *http.Response. In fact, I know I'm getting one back thanks to reflect.TypeOf(graphResponse). However, when I try to access the Body as I would normally:

    defer graphResponse.Body.Close()

(or in fact, referring to any of the response's fields including its Status) I get a panic:

    panic: runtime error: invalid memory address or nil pointer dereference
    runtime.panic go/src/pkg/runtime/proc.c:1442
    runtime.panicstring go/src/pkg/runtime/runtime.c:128
    runtime.sigpanic go/src/pkg/runtime/thread_linux.c:199
    app.home app/app.go:33
    net/http.HandlerFunc.ServeHTTP go/src/pkg/net/http/server.go:704
    net/http.(*ServeMux).ServeHTTP go/src/pkg/net/http/server.go:942
    appengine_internal.executeRequestSafely go/src/pkg/appengine_internal/api_prod.go:240
    appengine_internal.(*server).HandleRequest go/src/pkg/appengine_internal/api_prod.go:190
    reflect.Value.call go/src/pkg/reflect/value.go:526
    reflect.Value.Call go/src/pkg/reflect/value.go:334
    _ _.go:316
    runtime.goexit go/src/pkg/runtime/proc.c:270

Why can I send the entirety of the response to Fprintf and see what's in there, but not access the Body directly?

--
Cheers,
Rich.

Kyle Lemons

unread,
Feb 9, 2013, 1:14:17 PM2/9/13
to Rich Churcher, google-appengine-go
Check them and show us the code anyway.  Since I can't copy and paste your code and try it, it is easiest to help when I can look at the exact code you're using and the exact output from the program in question (preferably the failing case, with the "_ = graphResponse.Body" that triggers the panic included).  Also, use %#v when printing out arbitrary values, not %s.
 
This runs fine, and produces a valid token and what sure looks like an *http.Response. In fact, I know I'm getting one back thanks to reflect.TypeOf(graphResponse). However, when I try to access the Body as I would normally:

    defer graphResponse.Body.Close()

(or in fact, referring to any of the response's fields including its Status) I get a panic:

    panic: runtime error: invalid memory address or nil pointer dereference
    runtime.panic go/src/pkg/runtime/proc.c:1442
    runtime.panicstring go/src/pkg/runtime/runtime.c:128
    runtime.sigpanic go/src/pkg/runtime/thread_linux.c:199
    app.home app/app.go:33
    net/http.HandlerFunc.ServeHTTP go/src/pkg/net/http/server.go:704
    net/http.(*ServeMux).ServeHTTP go/src/pkg/net/http/server.go:942
    appengine_internal.executeRequestSafely go/src/pkg/appengine_internal/api_prod.go:240
    appengine_internal.(*server).HandleRequest go/src/pkg/appengine_internal/api_prod.go:190
    reflect.Value.call go/src/pkg/reflect/value.go:526
    reflect.Value.Call go/src/pkg/reflect/value.go:334
    _ _.go:316
    runtime.goexit go/src/pkg/runtime/proc.c:270

Why can I send the entirety of the response to Fprintf and see what's in there, but not access the Body directly?

--
Cheers,
Rich.

--
You received this message because you are subscribed to the Google Groups "google-appengine-go" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-appengin...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Rich Churcher

unread,
Feb 9, 2013, 6:49:42 PM2/9/13
to google-ap...@googlegroups.com, Rich Churcher


On Sunday, 10 February 2013 07:14:17 UTC+13, Kyle Lemons wrote:

Check them and show us the code anyway.  Since I can't copy and paste your code and try it, it is easiest to help when I can look at the exact code you're using and the exact output from the program in question (preferably the failing case, with the "_ = graphResponse.Body" that triggers the panic included).  Also, use %#v when printing out arbitrary values, not %s.

Hey that's great! Much easier to read.

Well, you've solved the problem by telling me to check the errors explicitly, but you're going to have to explain this to me I think! The errors are definitely testing nil... but not checking them seems to lead to the panic? To be clear, the following example panics:

package app

import (
"fmt"
"net/http"
"appengine"
"appengine/urlfetch"
)

func init () {
http.HandleFunc("/", home)
}

func home(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
config := &oauth.Config{
ClientId:     "<snip>",
ClientSecret: "<snip>",
Scope:        "email",
RedirectURL:  "<snip>",
}

code := r.FormValue("code")
if code == "" {
http.Redirect(w, r, config.AuthCodeURL("foo"), http.StatusFound)
}
t := &oauth.Transport{Config: config, Transport: &urlfetch.Transport{Context: c}}
tok, _ := t.Exchange(code)

fmt.Fprintf(w, "%#v", tok.AccessToken)
}

panic: runtime error: invalid memory address or nil pointer dereference
runtime.panic go/src/pkg/runtime/proc.c:1442
runtime.panicstring go/src/pkg/runtime/runtime.c:128
runtime.sigpanic go/src/pkg/runtime/thread_linux.c:199
app.home app/app.go:33
net/http.HandlerFunc.ServeHTTP go/src/pkg/net/http/server.go:704
net/http.(*ServeMux).ServeHTTP go/src/pkg/net/http/server.go:942
appengine_internal.executeRequestSafely go/src/pkg/appengine_internal/api_prod.go:240
appengine_internal.(*server).HandleRequest go/src/pkg/appengine_internal/api_prod.go:190
reflect.Value.call go/src/pkg/reflect/value.go:526
reflect.Value.Call go/src/pkg/reflect/value.go:334
_ _.go:316
runtime.goexit go/src/pkg/runtime/proc.c:270

If I keep all other details the same, and explicitly check the error:

tok, err := t.Exchange(code)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
        return
}

There's no panic, and the token outputs as you'd expect:

"AAADTWGsQ5NsBADSJe3NC0Dogp57ZBjjAXZCH3mmgUEO01DE1XYTlOwi8QPRMHINOHK7VQNOvEp0WllN7enY1TmU3ZBa7fcu92zP7rWUggZDZD"

I find this very confusing, though I'm relieved to have it working again. Any insight you might have would be welcome (although you could probably convince me that Go is simply punishing me for being sloppy.)

--
Cheers,
Rich.

Andrew Gerrand

unread,
Feb 9, 2013, 7:21:05 PM2/9/13
to Rich Churcher, google-appengine-go

Where are you seeing the panic? If in the console, it could be caused by a second request made by your browser to /favicon.ico

Andrew

--

Rich Churcher

unread,
Feb 9, 2013, 7:26:44 PM2/9/13
to google-ap...@googlegroups.com, Rich Churcher
On Sunday, 10 February 2013 13:21:05 UTC+13, Andrew Gerrand wrote:

Where are you seeing the panic? If in the console, it could be caused by a second request made by your browser to /favicon.ico


Good grief, you're right. But why do I only see this on requests where I don't check the error?

  1. 2013-02-09 15:40:47.969 /favicon.ico 500 367ms 0kb Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17
    219.89.20.120 - - [09/Feb/2013:15:40:47 -0800] "GET /favicon.ico HTTP/1.1" 500 233 - "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17" "sandbox-go.appspot.com" ms=368 cpu_ms=0 cpm_usd=0.000046 instance=00c61b117c89be17bfb76fd5a8c2621759c6e5
  2. C2013-02-09 15:40:47.967

Andrew Gerrand

unread,
Feb 10, 2013, 1:40:40 AM2/10/13
to Rich Churcher, google-appengine-go

Because when you check the error the function returns before trying to use the AccessToken field of the nil tok. That's the cause of the panic.

Always check your errors. :-)

Andrew

--

Rich Churcher

unread,
Feb 10, 2013, 4:08:50 AM2/10/13
to google-ap...@googlegroups.com, Rich Churcher

On Sunday, 10 February 2013 19:40:40 UTC+13, Andrew Gerrand wrote:

Because when you check the error the function returns before trying to use the AccessToken field of the nil tok. That's the cause of the panic.

Always check your errors. :-)

Oh, I do! But I really wanted to understand why I was seeing this behaviour. I added a section for favicon.ico to app.yaml as described here https://developers.google.com/appengine/kb/general#erroruris so that app.yaml looks like:

application: sandbox-go
version: 0-1
runtime: go
api_version: go1

handlers:
- url: /stylesheets
  static_dir: stylesheets

- url: /javascripts
  static_dir: javascripts

- url: /images
  static_dir: images

- url: /favicon\.ico
  static_files: images/favicon.ico
  upload: images/favicon\.ico

- url: /.*
  script: _go_app

Which stopped the panic showing up under a favicon request, but it was still there under a request for '/'. Then finally I realised: not including favicon, there are always TWO requests to this handler from a cold start! One for '/', and one for '/?code=whatever' after a redirect from the auth provider (Facebook in this case). It was always the first that was panicing. I'd been assuming that http.Redirect() implied that handler execution ended there and then, but of course it didn't. It continued merrily into the panic by trying to conduct a token exchange without a valid code. Adding an else clause was the most obvious remedy:

code := r.FormValue("code")
if code == "" {
http.Redirect(w, r, config.AuthCodeURL("foo"), http.StatusFound)
} else {
t := &oauth.Transport{Config: config, Transport: &urlfetch.Transport{Context: c}}
tok, _ := t.Exchange(code)

fmt.Fprintf(w, "%s", tok.AccessToken)
}

I'm slow, but eventually I do seem to brute-force solutions ;) Thanks for the help.

--
Cheers,
Rich.

Brad Fitzpatrick

unread,
Feb 11, 2013, 11:36:11 AM2/11/13
to Rich Churcher, google-appengine-go
You're also missing a "return" statement after your redirect:

code := r.FormValue("code")
    if code == "" {
    http.Redirect(w, r, config.AuthCodeURL("foo"), http.StatusFound)
    }

On Fri, Feb 8, 2013 at 9:33 PM, Rich Churcher <rich.c...@gmail.com> wrote:

--

Andrew Gerrand

unread,
Mar 11, 2013, 11:19:46 PM3/11/13
to kil...@gmail.com, google-appengine-go, Rich Churcher
Can you provide some more details about the issues you're having?

What do you mean by "cannot get token"?

Explain your problem to me as if I am a small child. :-)

Andrew


On 12 March 2013 10:52, <kil...@gmail.com> wrote:
Hello, i try use all from this topic. 
It's works fine, but only into dev mode (local) 
On kilatib.appspot.com i have problem app cannot get token.
I don't understand why could you help me

--

Andrew Gerrand

unread,
Mar 13, 2013, 5:50:46 PM3/13/13
to kilatiB Shtihno, google-appengine-go
Okay, can you share your code? I'm mystified now.


On 13 March 2013 19:52, kilatiB Shtihno <kil...@gmail.com> wrote:
>>> What returns nil? 
accessToken - nil

>>> Are you checking all your error return values?
yes, check all,- nothing unusual


>>> Are you using the appengine/urlftech package to make HTTP requests?
yes

2013/3/13 Andrew Gerrand <a...@golang.org>



On 12 March 2013 21:01, kilatiB Shtihno <kil...@gmail.com> wrote:
I'm try use goauth2 lib (https://code.google.com/p/goauth2/)
And have challenges on appspot.com

I'm deploy source on github and will speak about this file https://github.com/kilatib/appengine_test/blob/master/app/model/Auth.go
1. App create oauth link ( by the method GetAuthLink(redirectUri string) string)
2. User follows the link.
4. After that him redirect back to our app with two params: 
        `soical`,- social name whose chose user for authorization, 
        `code`, -  for getting token

3. After that calling method `initAccessToken(code string)`
       , -  where `code` - params from get request

My problem when I'm testing this on my local machine ( with the dev_appserver.py) All fine token generated
But on appspot.com token 99.9% cases returned nil. 

What returns nil? Are you checking all your error return values? Are you using the appengine/urlftech package to make HTTP requests? (You can't make normal HTTP requests from App Engine).

Andrew


Reply all
Reply to author
Forward
0 new messages