# Path: google/datalab/utils/_http.py
# class Http(object):
#   """A helper class for making HTTP requests.
#   """
# 
#   # Reuse one Http object across requests to take advantage of Keep-Alive, e.g.
#   # for BigQuery queries that requires at least ~5 sequential http requests.
#   #
#   # TODO(nikhilko):
#   # SSL cert validation seemingly fails, and workarounds are not amenable
#   # to implementing in library code. So configure the Http object to skip
#   # doing so, in the interim.
#   http = httplib2.Http()
#   http.disable_ssl_certificate_validation = True
# 
#   def __init__(self):
#     pass
# 
#   @staticmethod
#   def request(url, args=None, data=None, headers=None, method=None,
#               credentials=None, raw_response=False, stats=None):
#     """Issues HTTP requests.
# 
#     Args:
#       url: the URL to request.
#       args: optional query string arguments.
#       data: optional data to be sent within the request.
#       headers: optional headers to include in the request.
#       method: optional HTTP method to use. If unspecified this is inferred
#           (GET or POST) based on the existence of request data.
#       credentials: optional set of credentials to authorize the request.
#       raw_response: whether the raw response content should be returned as-is.
#       stats: an optional dictionary that, if provided, will be populated with some
#           useful info about the request, like 'duration' in seconds and 'data_size' in
#           bytes. These may be useful optimizing the access to rate-limited APIs.
#     Returns:
#       The parsed response object.
#     Raises:
#       Exception when the HTTP request fails or the response cannot be processed.
#     """
#     if headers is None:
#       headers = {}
# 
#     headers['user-agent'] = 'GoogleCloudDataLab/1.0'
#     # Add querystring to the URL if there are any arguments.
#     if args is not None:
#       qs = urllib.parse.urlencode(args)
#       url = url + '?' + qs
# 
#     # Setup method to POST if unspecified, and appropriate request headers
#     # if there is data to be sent within the request.
#     if data is not None:
#       if method is None:
#         method = 'POST'
# 
#       if data != '':
#         # If there is a content type specified, use it (and the data) as-is.
#         # Otherwise, assume JSON, and serialize the data object.
#         if 'Content-Type' not in headers:
#           data = json.dumps(data)
#           headers['Content-Type'] = 'application/json'
#       headers['Content-Length'] = str(len(data))
#     else:
#       if method == 'POST':
#         headers['Content-Length'] = '0'
# 
#     # If the method is still unset, i.e. it was unspecified, and there
#     # was no data to be POSTed, then default to GET request.
#     if method is None:
#       method = 'GET'
# 
#     http = Http.http
# 
#     # Authorize with credentials if given
#     if credentials is not None:
#       # Make a copy of the shared http instance before we modify it.
#       http = copy.copy(http)
#       http = google_auth_httplib2.AuthorizedHttp(credentials)
#     if stats is not None:
#       stats['duration'] = datetime.datetime.utcnow()
# 
#     response = None
#     try:
#       log.debug('request: method[%(method)s], url[%(url)s], body[%(data)s]' % locals())
#       response, content = http.request(url,
#                                        method=method,
#                                        body=data,
#                                        headers=headers)
#       if 200 <= response.status < 300:
#         if raw_response:
#           return content
#         if type(content) == str:
#           return json.loads(content)
#         else:
#           return json.loads(str(content, encoding='UTF-8'))
#       else:
#         raise RequestException(response.status, content)
#     except ValueError:
#       raise Exception('Failed to process HTTP response.')
#     except httplib2.HttpLib2Error:
#       raise Exception('Failed to send HTTP request.')
#     finally:
#       if stats is not None:
#         stats['data_size'] = len(data)
#         stats['status'] = response.status
#         stats['duration'] = (datetime.datetime.utcnow() - stats['duration']).total_seconds()


import mock
import unittest
from google.datalab.utils._http import Http
  @mock.patch('google.datalab.utils._http.Http.http.request')
  def test_raises_http_error_json(self, mock_request, mock_response):
    TestCases._setup_mocks(
      mock_request, mock_response,
      b'{"error": {"errors": [{"message": "Not Found"}]}}', 404)
    with self.assertRaises(Exception) as error:
      Http.request('http://www.example.org')

    e = error.exception
    self.assertEqual(e.status, 404)
    self.assertEqual(e.message, 'HTTP request failed: Not Found')

  @mock.patch('httplib2.Response')
  @mock.patch('google.datalab.utils._http.Http.http.request')
  def test_raises_http_error_str(self, mock_request, mock_response):
    TestCases._setup_mocks(mock_request, mock_response, 'Not Found', 404)

    with self.assertRaises(Exception) as error:
      Http.request('http://www.example.org')

    e = error.exception
    self.assertEqual(e.status, 404)
    self.assertEqual(e.content, 'Not Found')

  @mock.patch('httplib2.Response')
  @mock.patch('google.datalab.utils._http.Http.http.request')
  def test_raises_http_error_bytes(self, mock_request, mock_response):
    TestCases._setup_mocks(mock_request, mock_response, b'Not Found', 404)

    with self.assertRaises(Exception) as error:

# Path: google/datalab/utils/_http.py
# class Http(object):
#   """A helper class for making HTTP requests.
#   """
# 
#   # Reuse one Http object across requests to take advantage of Keep-Alive, e.g.
#   # for BigQuery queries that requires at least ~5 sequential http requests.
#   #
#   # TODO(nikhilko):
#   # SSL cert validation seemingly fails, and workarounds are not amenable
#   # to implementing in library code. So configure the Http object to skip
#   # doing so, in the interim.
#   http = httplib2.Http()
#   http.disable_ssl_certificate_validation = True
# 
#   def __init__(self):
#     pass
# 
#   @staticmethod
#   def request(url, args=None, data=None, headers=None, method=None,
#               credentials=None, raw_response=False, stats=None):
#     """Issues HTTP requests.
# 
#     Args:
#       url: the URL to request.
#       args: optional query string arguments.
#       data: optional data to be sent within the request.
#       headers: optional headers to include in the request.
#       method: optional HTTP method to use. If unspecified this is inferred
#           (GET or POST) based on the existence of request data.
#       credentials: optional set of credentials to authorize the request.
#       raw_response: whether the raw response content should be returned as-is.
#       stats: an optional dictionary that, if provided, will be populated with some
#           useful info about the request, like 'duration' in seconds and 'data_size' in
#           bytes. These may be useful optimizing the access to rate-limited APIs.
#     Returns:
#       The parsed response object.
#     Raises:
#       Exception when the HTTP request fails or the response cannot be processed.
#     """
#     if headers is None:
#       headers = {}
# 
#     headers['user-agent'] = 'GoogleCloudDataLab/1.0'
#     # Add querystring to the URL if there are any arguments.
#     if args is not None:
#       qs = urllib.parse.urlencode(args)
#       url = url + '?' + qs
# 
#     # Setup method to POST if unspecified, and appropriate request headers
#     # if there is data to be sent within the request.
#     if data is not None:
#       if method is None:
#         method = 'POST'
# 
#       if data != '':
#         # If there is a content type specified, use it (and the data) as-is.
#         # Otherwise, assume JSON, and serialize the data object.
#         if 'Content-Type' not in headers:
#           data = json.dumps(data)
#           headers['Content-Type'] = 'application/json'
#       headers['Content-Length'] = str(len(data))
#     else:
#       if method == 'POST':
#         headers['Content-Length'] = '0'
# 
#     # If the method is still unset, i.e. it was unspecified, and there
#     # was no data to be POSTed, then default to GET request.
#     if method is None:
#       method = 'GET'
# 
#     http = Http.http
# 
#     # Authorize with credentials if given
#     if credentials is not None:
#       # Make a copy of the shared http instance before we modify it.
#       http = copy.copy(http)
#       http = google_auth_httplib2.AuthorizedHttp(credentials)
#     if stats is not None:
#       stats['duration'] = datetime.datetime.utcnow()
# 
#     response = None
#     try:
#       log.debug('request: method[%(method)s], url[%(url)s], body[%(data)s]' % locals())
#       response, content = http.request(url,
#                                        method=method,
#                                        body=data,
#                                        headers=headers)
#       if 200 <= response.status < 300:
#         if raw_response:
#           return content
#         if type(content) == str:
#           return json.loads(content)
#         else:
#           return json.loads(str(content, encoding='UTF-8'))
#       else:
#         raise RequestException(response.status, content)
#     except ValueError:
#       raise Exception('Failed to process HTTP response.')
#     except httplib2.HttpLib2Error:
#       raise Exception('Failed to send HTTP request.')
#     finally:
#       if stats is not None:
#         stats['data_size'] = len(data)
#         stats['status'] = response.status
#         stats['duration'] = (datetime.datetime.utcnow() - stats['duration']).total_seconds()


# Path: tests/_util/http_tests.py
import mock
import unittest
from google.datalab.utils._http import Http

  @mock.patch('google.datalab.utils._http.Http.http.request')
  def test_raises_http_error_json(self, mock_request, mock_response):
    TestCases._setup_mocks(
      mock_request, mock_response,
      b'{"error": {"errors": [{"message": "Not Found"}]}}', 404)
    with self.assertRaises(Exception) as error:
      Http.request('http://www.example.org')

    e = error.exception
    self.assertEqual(e.status, 404)
    self.assertEqual(e.message, 'HTTP request failed: Not Found')

  @mock.patch('httplib2.Response')
  @mock.patch('google.datalab.utils._http.Http.http.request')
  def test_raises_http_error_str(self, mock_request, mock_response):
    TestCases._setup_mocks(mock_request, mock_response, 'Not Found', 404)

    with self.assertRaises(Exception) as error:
      Http.request('http://www.example.org')

    e = error.exception
    self.assertEqual(e.status, 404)
    self.assertEqual(e.content, 'Not Found')

  @mock.patch('httplib2.Response')
  @mock.patch('google.datalab.utils._http.Http.http.request')
  def test_raises_http_error_bytes(self, mock_request, mock_response):
    TestCases._setup_mocks(mock_request, mock_response, b'Not Found', 404)

    with self.assertRaises(Exception) as error:

      Http.request('http://www.example.org')