/// <summary> /// helper method to make a get request to basecamp. checks cache first if you've already received that response and checks with basecamp if you /// need to update your cache. /// </summary> /// <param name="url">the api method endpoint being called</param> /// <exception cref="Exceptions.UnauthorizedException">Will be thrown if you cannot refresh the basecamp token when it has expired</exception> /// <exception cref="ArgumentException">URLs must end in .json</exception> /// <exception cref="Exceptions.RateLimitExceeded">Thrown when you exceed the ratelimit - will contain information on when you can retry</exception> private dynamic _getJSONFromURL(string url) { // ensure url ends with .json or .json?xxx if (!url.ToLower().EndsWith(".json") && !(url.Contains("?") && url.ToLower().Substring(0, url.IndexOf("?")).EndsWith(".json"))) { throw new ArgumentException("Invalid URL. URLs must end in .json", url); } string unique_id_to_hash = (_accessToken.access_token + url.ToLower()); var cacheKey = unique_id_to_hash.CalculateMD5(); try { //if in cache, check with server and if not modified then return original results string cached_results = (string)_cache.Get(cacheKey); if (cached_results != null) { string if_none_match = (string)_cache.Get(cacheKey + "etag"); string if_modified_since = (string)_cache.Get(cacheKey + "lastModified"); System.Net.HttpWebRequest wr = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(url); wr.Method = "HEAD"; wr.Headers.Add(System.Net.HttpRequestHeader.Authorization, string.Format("Bearer {0}", _accessToken.access_token)); wr.UserAgent = _appNameAndContact; if (!string.IsNullOrWhiteSpace(if_modified_since)) { wr.IfModifiedSince = DateTime.Parse(if_modified_since); } if (!string.IsNullOrWhiteSpace(if_none_match)) { wr.Headers["If-None-Match"] = if_none_match; } var resp = (System.Net.HttpWebResponse)wr.BetterGetResponse();//use extension to properly handle 304 if (resp.StatusCode == System.Net.HttpStatusCode.NotModified) { return(Json.Decode(cached_results)); } } } catch { //if cache check fails just make the real request to basecamp } try { System.Net.HttpWebRequest wr = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(url); wr.Method = "GET"; wr.Headers.Add(System.Net.HttpRequestHeader.Authorization, string.Format("Bearer {0}", _accessToken.access_token)); wr.UserAgent = _appNameAndContact; var resp = (System.Net.HttpWebResponse)wr.BetterGetResponse(); if (resp.StatusCode == System.Net.HttpStatusCode.OK) { using (var sw = new System.IO.StreamReader(resp.GetResponseStream())) { var strResp = sw.ReadToEnd(); var json_results = Json.Decode(strResp); var resp_etag = resp.Headers["ETag"] != null ? resp.Headers["ETag"] : null; var resp_last_modified = resp.Headers["Last-Modified"] != null ? resp.Headers["Last-Modified"] : null; if (resp_etag != null || resp_last_modified != null) { //cache it if (!string.IsNullOrWhiteSpace(resp_etag)) { _cache.Set(cacheKey + "etag", resp_etag); } if (!string.IsNullOrWhiteSpace(resp_last_modified)) { _cache.Set(cacheKey + "lastModified", resp_last_modified); } if (!string.IsNullOrWhiteSpace(strResp)) { _cache.Set(cacheKey, strResp); } } return(json_results); } } else if (resp.StatusCode == (System.Net.HttpStatusCode) 429)//too many requests { throw new Exceptions.RateLimitExceededException(int.Parse(resp.Headers["Retry-After"])); } else if (resp.StatusCode == System.Net.HttpStatusCode.Unauthorized) { if (resp.Headers[System.Net.HttpResponseHeader.WwwAuthenticate] != null) { string www_auth = resp.Headers[System.Net.HttpResponseHeader.WwwAuthenticate]; int error_start = www_auth.LastIndexOf("error=\"token_expired\""); if (error_start > -1) { //need to refresh token throw new Exceptions.TokenExpired(); } } //throw an unauthorized exception if you get here throw new Exceptions.UnauthorizedException(); } else { throw new Exceptions.GeneralAPIException("Try again later. Status code returned was " + (int)resp.StatusCode, (int)resp.StatusCode); } } catch (Exceptions.BaseException) { throw; } catch { return(null); } }