Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

oauth2client/appengine.py returns "InvalidResponseError: header values must be str, got 'unicode'" with webapp2/python27/wsgi

Beforehand, my problem is similar to question Pyramid on App Engine gets "InvalidResponseError: header values must be str, got 'unicode', and several google-api-python-client bugs, but none helped in my case. Also, I had no answer on issue #254 (which itself looks similar to #111, so I'm trying here.

On a local GAE, the simple example below (a simplified & python27-ified version of this sample) returns InvalidResponseError: header values must be str, got 'unicode', though my code is not doing any unicode header setting. More precisely, I'm expecting result Hello, and instead I have:

Internal Server Error
    The server has either erred or is incapable of performing the requested operation.
    Traceback (most recent call last):
      File "/home/ronj/.gae/lib/webapp2-2.5.2/webapp2.py", line 1546, in __call__
        return response(environ, start_response)
      File "/home/ronj/.gae/lib/webob_0_9/webob/__init__.py", line 2000, in __call__
        start_response(self.status, self.headerlist)
      File "/home/ronj/.gae/google/appengine/runtime/wsgi.py", line 156, in _StartResponse
        (_GetTypeName(value), value, name))
    InvalidResponseError: header values must be str, got 'unicode' (u'https://accounts.google.com/o/oauth2/auth?state=http%3A%2F%2Flocalhost%3A8080%2F&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2callback&response_type=code&client_id=xxxxxxxxxxxx.apps.googleusercontent.com&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube&access_type=offline') for 'Location'

Any idea? I am using GAE 1.7.5 on Python 2.7.3 on Ubuntu 12.10 x64.

EDIT: Jonas provided an answer in issue #254: "it should be relatively easy to add some str() into the methods on OAuth2WebServerFlow that generates URLs. Wrap with str() before return on line 830 in oauth2client/client.py".
→ That looks great, but how am I supposed to implement that? I agree that I can modify the file on my local machine where I installed GAE, but once deployed it will be Google's GAE that will be used, right? How can I override it? (and sorry for the newbie question)

Thanks for your help!


app.yaml:

application: yourapp
version: 1
runtime: python27
api_version: 1
threadsafe: true

handlers:

- url: /
  script: yourapp.main

libraries:
- name: webapp2
  version: latest

yourapp.py:

import webapp2, os, httplib2
from apiclient.discovery import build
from oauth2client.appengine import oauth2decorator_from_clientsecrets
from google.appengine.api import memcache

CLIENT_SECRETS = os.path.join(os.path.dirname(__file__), 'client_secrets.json')
MISSING_CLIENT_SECRETS_MESSAGE = "Warning: Please configure OAuth 2.0"
YOUTUBE_READ_WRITE_SCOPE = "https://www.googleapis.com/auth/youtube"
YOUTUBE_API_SERVICE_NAME = "youtube"
YOUTUBE_API_VERSION = "v3"

http = httplib2.Http(memcache)
youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, http=http)
decorator = oauth2decorator_from_clientsecrets(
    CLIENT_SECRETS,
    scope=YOUTUBE_READ_WRITE_SCOPE,
    message=MISSING_CLIENT_SECRETS_MESSAGE)


class MainPage(webapp2.RequestHandler):

  @decorator.oauth_required
  def get(self):
    self.response.headers['Content-Type'] = 'text/plain'
    self.response.write('Hello')

main = webapp2.WSGIApplication([('/', MainPage)], debug=True)
like image 708
Ronan Jouchet Avatar asked Mar 27 '13 20:03

Ronan Jouchet


2 Answers

I ran into this same issue even though I was already using the latest version of the apiclient.

The answer which worked for me to resolve this issue was as posted in the issue tracker here http://code.google.com/p/google-api-python-client/issues/detail?id=254

NOT WORKING

flow = client.flow_from_clientsecrets(CLIENT_SECRETS,scope=scopes)
callback = self.request.relative_url('/oauth2callback')
auth_url = flow.step1_get_authorize_url(callback)
return self.redirect(auth_url)

WORKING

flow = client.flow_from_clientsecrets(CLIENT_SECRETS,scope=scopes)
callback = self.request.relative_url('/oauth2callback')
auth_url = str(flow.step1_get_authorize_url(callback))
return self.redirect(auth_url)

Note the str() wrapping the flow.step1_get_authorize_url call

like image 189
John Avatar answered Oct 01 '22 20:10

John


Yet another answer... adding a str() fixes the problem, but not the root cause. I spent hours trying to figure out why one particular redirect raised this error while others didn't, before noticing that the URL forthe bad redirect was missing the initial "/".

I have no idea why this is the case - possibly an incomplete path is processed differently from a complete one. But if you hit this error try changing:

self.redirect('home.view')

to:

self.redirect('/home.view')
like image 31
FoxyLad Avatar answered Oct 01 '22 20:10

FoxyLad