I sell Android apps in Google Play and all orders go through Google Wallet as usual. Google Wallet doesn't allow grouping and filtering the order list, so I wanted to create a small utility (C#, WinForms) to show that order list in a more convenient way. I managed to find the following URL allows downloading a CSV file with the order information and this file fully meets my requirements "https://wallet.google.com/merchant/pages/u/0/bcid-XXX/oid-YYY/cid-ZZZ/purchaseorderdownload?startTime=...&endTime=...". However, I'm unable to authorize to Google and I cannot use this URL without being redirected to the login page. Since I'm going to create a WinForms tool, I cannot use this login page. So, the question is, how to authorize to Google in a sort of automatic mode, so that I can use this URL and download CSV files for further processing in my tool?
Ted, here is my code:
private const string LoginUrl = "https://accounts.google.com/ServiceLoginAuth";
private const string WalletUrl = "https://wallet.google.com/merchant/pages";
private readonly DateTime _unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private const string Username = "YourUserName#gmail.com";
private const string Password = "YourPassword";
private readonly DateTime _startDate = new DateTime(2014, 9, 1);
private readonly DateTime _endDate = new DateTime(2014, 9, 30);
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
string orders = GetLoginHtml();
}
private string GetLoginHtml()
{
var request = (HttpWebRequest)WebRequest.Create(LoginUrl);
var cookieJar = new CookieContainer();
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.CookieContainer = cookieJar;
using (var requestStream = request.GetRequestStream())
{
string content = "Email=" + Username + "&Passwd=" + Password;
requestStream.Write(Encoding.UTF8.GetBytes(content), 0, Encoding.UTF8.GetBytes(content).Length);
using (var sr = new StreamReader(request.GetResponse().GetResponseStream()))
{
string html = sr.ReadToEnd();
string galxValue = ParseOutValue(html, "GALX");
return GetLoginHtml2(galxValue, cookieJar);
}
}
}
private string GetLoginHtml2(string galx, CookieContainer cookieJar)
{
var request = (HttpWebRequest)WebRequest.Create(LoginUrl);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.CookieContainer = cookieJar;
using (var requestStream = request.GetRequestStream())
{
string content = "Email=" + Username + "&Passwd=" + Password + "&GALX=" + galx;
requestStream.Write(Encoding.UTF8.GetBytes(content), 0, Encoding.UTF8.GetBytes(content).Length);
using (var sr = new StreamReader(request.GetResponse().GetResponseStream()))
{
string html = sr.ReadToEnd();
return GetLoginHtml3(galx, cookieJar);
}
}
}
private string GetLoginHtml3(string galx, CookieContainer cookieJar)
{
var request = (HttpWebRequest)WebRequest.Create(WalletUrl);
request.Method = "GET";
request.ContentType = "text/xml";
request.CookieContainer = cookieJar;
using (var sr = new StreamReader(request.GetResponse().GetResponseStream()))
{
string html = sr.ReadToEnd();
string bcid = ParseOutBcid(html);
string oid = ParseOutOid(html);
string cid = ParseOutCid(html);
string orders = GetOrders(cookieJar, bcid, oid, cid, _startDate, _endDate);
return orders;
}
}
private string GetOrders(CookieContainer cookieJar, string bcid, string oid, string cid, DateTime startDate, DateTime endDate)
{
var st = (long)(startDate.ToUniversalTime() - _unixEpoch).TotalMilliseconds;
var et = (long)(endDate.ToUniversalTime() - _unixEpoch).TotalMilliseconds;
var request = (HttpWebRequest)WebRequest.Create(WalletUrl + "/u/0/bcid-" + bcid + "/oid-" + oid + "/cid-" + cid + "/purchaseorderdownload?startTime=" + st + "&endTime=" + et);
request.Method = "GET";
request.ContentType = "text/xml";
request.CookieContainer = cookieJar;
using (var sr = new StreamReader(request.GetResponse().GetResponseStream()))
{
string html = sr.ReadToEnd();
return html;
}
}
private string ParseOutValue(string html, string value)
{
int ndx1 = html.IndexOf("<input name=\"" + value + "\"", StringComparison.Ordinal);
int ndx2 = html.IndexOf("value=", ndx1, StringComparison.Ordinal);
return html.Substring(ndx2 + 7, html.IndexOf("\"", ndx2 + 7, StringComparison.Ordinal) - ndx2 - 7);
}
private string ParseOutBcid(string html)
{
int ndx1 = html.IndexOf("bcid-", StringComparison.Ordinal);
int ndx2 = html.IndexOf("/oid", ndx1, StringComparison.Ordinal);
return html.Substring(ndx1 + 5, ndx2 - ndx1 - 5);
}
private string ParseOutOid(string html)
{
int ndx1 = html.IndexOf("/oid-", StringComparison.Ordinal);
int ndx2 = html.IndexOf("/cid", ndx1, StringComparison.Ordinal);
return html.Substring(ndx1 + 5, ndx2 - ndx1 - 5);
}
private string ParseOutCid(string html)
{
int ndx1 = html.IndexOf("/cid-", StringComparison.Ordinal);
string retval = "";
ndx1 = ndx1 + 5;
while (char.IsNumber(html[ndx1]))
{
retval = retval + html[ndx1];
ndx1++;
}
return retval;
}
A couple of things. First, you need to change the Username to whatever your gmail username is and change the Password to whatever your password is. Also, set the _startDate and _endDate to whatever time frame you are trying to pull. You shouldn't have to change anything else. Just call the "GetLoginHtml()" function and it will return your Wallet orders!
Keep in mind, I haven't had a chance to optimize the code yet. Its very raw and there might still be bugs. Also, one other thing, if you get an indexing error while running it, its because you have to open Internet Explorer and log into your Google Account for the first time. Once you log in, you can close Internet Explorer and the program should work after that. I need to figure out a way around that, which I didn't yet.
Let me know what you think!
Related
I am at a total loss here. Im currently having success doing a GET request against a web service that implements DIGEST MD5-sess authentication. This works fine. I get the expected result so I figure my 'BUILD DIGEST AUTH HEADER' method works as intended.
I then get the requirement to also support POST, PUT and PATCH but now I run into a whole heap of problems. First request obviously returns a 401 and I read the info in the WWW-Authenticate header and make a MD5-sess auth header taking into account that this is now a POST, PUT or PATCH request.
var methodString = urlResolverStrategy.ResolverDataModel.HttpMethod.ToString();
var ha2 = CalculateMd5Hash(string.Format("{0}:{1}", methodString, dir));
I keep everything the same but do also obviously add the content to the request on POST, PUT and PATCH. But for some reason I cannot figure out Im locked out of the service. Second request return another 401 even.
Is there something special that needs to be done for DIGEST Auth when method is POST, PUT and PATCH that Im missing?
I have all my requests setup in Postman as well put Postman does not have the same problem. Second call in Postman gets the expected results. A 200 and the expected data on POST, PUT and PATCH.
Im using a slightly modified version DigestAuthFixer.cs that's also available on some other posts here in stackoverflow.
Code below is currently hardcoded to use the MD5-sess method.
public class DigestAuthFixer
{
private static readonly Random random = new Random(DateTime.Now.Millisecond);
private readonly AuthData authData;
readonly UrlResolverStrategyBase urlResolverStrategy;
public HttpStatusCode StatusCode { get; private set; }
public DigestAuthFixer(BasicAuth basicAuth, UrlResolverStrategyBase urlResolverStrategy)
{
// TODO: Complete member initialization
authData = new AuthData
{
Host = urlResolverStrategy.GetHostName(),
User = basicAuth.GetUsername(),
Password = basicAuth.GetPassword(),
};
this.urlResolverStrategy = urlResolverStrategy;
}
private string CalculateMd5Hash(string input)
{
var inputBytes = Encoding.ASCII.GetBytes(input);
var hash = MD5.Create().ComputeHash(inputBytes);
var sb = new StringBuilder();
foreach (var b in hash)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString();
}
private string GrabHeaderVar(string varName, string header)
{
var regHeader = new Regex(string.Format(#"{0}=""([^""]*)""", varName));
var matchHeader = regHeader.Match(header);
if (matchHeader.Success)
{
return matchHeader.Groups[1].Value;
}
throw new ApplicationException(string.Format("Header {0} not found", varName));
}
private string GetDigestHeader(string dir)
{
authData.NC++;
string ha1;
if (authData.Algorithm == "MD5-sess")
{
var ha0 = CalculateMd5Hash(string.Format("{0}:{1}:{2}", authData.User, authData.Realm, authData.Password));
ha1 = CalculateMd5Hash(string.Format("{0}:{1}:{2}", ha0, authData.Nonce, authData.Cnonce));
}
else
{
ha1 = CalculateMd5Hash(string.Format("{0}:{1}:{2}", authData.User, authData.Realm, authData.Password));
}
var methodString = urlResolverStrategy.ResolverDataModel.HttpMethod.ToString();
var ha2 = CalculateMd5Hash(string.Format("{0}:{1}", methodString, dir));
var digestResponse = CalculateMd5Hash(string.Format("{0}:{1}:{2:00000000}:{3}:{4}:{5}", ha1, authData.Nonce, authData.NC, authData.Cnonce, authData.Qop, ha2));
var authString = string.Format("Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", algorithm=\"{4}\", qop={5}, nc={6:00000000}, cnonce=\"{7}\", response=\"{8}\"", authData.User, authData.Realm, authData.Nonce, dir, authData.Algorithm, authData.Qop, authData.NC, authData.Cnonce, digestResponse);
return authString;
}
public string GrabResponse(string nUrl, string content)
{
var uri = new Uri(authData.Host + nUrl);
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = urlResolverStrategy.ResolverDataModel.HttpMethod.ToString();
// If we've got a recent Auth header, re-use it!
if (!string.IsNullOrEmpty(authData.Cnonce) && DateTime.Now.Subtract(authData.CnonceDate).TotalHours < 1.0)
{
request.Headers.Add("Authorization", GetDigestHeader(nUrl));
}
if (!string.IsNullOrEmpty(urlResolverStrategy.ResolverDataModel.IfMatchHeaderValue))
{
request.Headers.Add(HttpHelper.IfMatchHeaderName, urlResolverStrategy.ResolverDataModel.IfMatchHeaderValue);
}
AddContentToBody(request, content);
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
StatusCode = response.StatusCode;
}
catch (WebException ex)
{
// Try to fix a 401 exception by adding a Authorization header
if (ex.Response == null || ((HttpWebResponse)ex.Response).StatusCode != HttpStatusCode.Unauthorized)
throw;
var wwwAuthenticateHeader = ex.Response.Headers["WWW-Authenticate"];
authData.Realm = GrabHeaderVar("realm", wwwAuthenticateHeader);
authData.Nonce = GrabHeaderVar("nonce", wwwAuthenticateHeader);
authData.Qop = GrabHeaderVar("qop", wwwAuthenticateHeader);
authData.Algorithm = "MD5-sess"; // GrabHeaderVar("algorithm", wwwAuthenticateHeader);
authData.NC = 0;
authData.Cnonce = RandomString(8);
authData.CnonceDate = DateTime.Now;
string debug = wwwAuthenticateHeader + Environment.NewLine + nUrl + Environment.NewLine + uri.ToString() + Environment.NewLine + authData.ToString();
var digestRequest = (HttpWebRequest)WebRequest.Create(uri);
digestRequest.Method = urlResolverStrategy.ResolverDataModel.HttpMethod.ToString();
AddContentToBody(digestRequest, content);
var authHeader = GetDigestHeader(nUrl);
debug += uri.ToString() + Environment.NewLine;
debug += nUrl + Environment.NewLine;
debug += authHeader + Environment.NewLine;
digestRequest.Headers.Add("Authorization", authHeader);
if (!string.IsNullOrEmpty(urlResolverStrategy.ResolverDataModel.IfMatchHeaderValue))
{
request.Headers.Add(HttpHelper.IfMatchHeaderName, urlResolverStrategy.ResolverDataModel.IfMatchHeaderValue);
}
HttpWebResponse digestResponse = null;
try
{
//return authHeader;
digestResponse = (HttpWebResponse)digestRequest.GetResponse();
StatusCode = digestResponse.StatusCode;
response = digestResponse;
}
catch (Exception digestRequestEx)
{
if (digestResponse != null)
{
StatusCode = response.StatusCode;
}
else
{
StatusCode = HttpStatusCode.InternalServerError;
}
//return "It broke" + Environment.NewLine + debug;
return "There was a problem with url, username or password (" + digestRequestEx.Message + ")";
}
}
var reader = new StreamReader(response.GetResponseStream());
return reader.ReadToEnd();
}
public string RandomString(int length)
{
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[length];
for (int i = 0; i < stringChars.Length; i++)
{
stringChars[i] = chars[random.Next(chars.Length)];
}
return new string(stringChars);
}
private void AddContentToBody(HttpWebRequest request, string content)
{
if (string.IsNullOrEmpty(content))
return;
var data = Encoding.Default.GetBytes(content); // note: choose appropriate encoding
request.ContentLength = data.Length;
request.ContentType = HttpHelper.MediaTypes.Json;
request.Accept = "*/*";
request.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
//request.Headers.Add("Accept-Encoding", "gzip, deflate, br");
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write(content);
}
}
}
internal class AuthData
{
public string Host;
public string User;
public string Password;
public string Realm;
public string Nonce;
public string Qop;
public string Cnonce;
public DateTime CnonceDate;
public int NC;
public string Algorithm;
public override string ToString()
{
string newLine = Environment.NewLine;
string result = Host + newLine;
result += User + newLine;
result += Realm + newLine;
result += Nonce + newLine;
result += Qop + newLine;
result += Cnonce + newLine;
result += CnonceDate + newLine;
result += NC + newLine;
result += Algorithm + newLine;
return result;
}
}
So apparently it matters in which order you add all the headers to the request. With hookbin I was able to detect that even though I have put an Authorization header on my digestRequest object it did not follow through to hookbin.
My placing the Authorization header addition to just below the setting the method line it all works...
I had no idea that that could pose a problem. The reason my GET method work is because is because 'content' is empty so no headers are added.
var digestRequest = (HttpWebRequest)WebRequest.Create(uri);
digestRequest.Method = urlResolverStrategy.ResolverDataModel.HttpMethod.ToString();
digestRequest.Headers.Add("Authorization", GetDigestHeader(nUrl));
[HTTPWEBREQUEST 1]https://1drv.ms/u/s!AtL5uCkGy1ERgbEWHlNApMtROuP_0Q
[HTTPWEBREQUEST 2]https://1drv.ms/u/s!AtL5uCkGy1ERgbEXlHjqHdho3lUjfw
When I am trying to call withing main for second time a HttpWebRequest1 and the nested HttpWebRequest2, it runs fine. But on the second run of the nested HttpWebRequest2 I get an exception on THIS line(System.IO.StreamReader sr2 = new System.IO.StreamReader(s2)) on its second run.
Exception:> "Message = "Stream was not readable."
try
{
HttpWebRequest WebRequestObjectCards = (HttpWebRequest)HttpWebRequest.Create("https://api.ucy.ac.cy/api/v1/cards?status=Valid&");
HttpWebRequest WebRequestObjectUsers = (HttpWebRequest)HttpWebRequest.Create("https://api.ucy.ac.cy/api/v1/users/" + ucy_id);
if (WebRequestObjectCards != null && WebRequestObjectUsers != null)
{
WebRequestObjectCards.Method = "GET";
WebRequestObjectCards.Timeout = 12000;
WebRequestObjectCards.ContentType = "application/json";
WebRequestObjectCards.Headers.Add("Authorization", "Bearer XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
WebRequestObjectCards.KeepAlive = true;
WebRequestObjectUsers.Method = "GET";
WebRequestObjectUsers.Timeout = 12000;
WebRequestObjectUsers.ContentType = "application/json";
WebRequestObjectUsers.Headers.Add("Authorization", "Bearer XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
WebRequestObjectUsers.KeepAlive = true;
using (System.IO.Stream s = WebRequestObjectCards.GetResponse().GetResponseStream())
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(s))
{
var jsonResponse = sr.ReadToEnd();
var serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = Int32.MaxValue; // The value of this constant is 2,147,483,647
Students UCYstudents = serializer.Deserialize<Students>(jsonResponse);
//String to be added in csv
var csv = new StringBuilder();
//prepare CSV Header
newLine = string.Format("***StartOfFile***");
csv.AppendLine(newLine);
newLine = string.Format("ID; FirstName; LastName; RFIDUID; PrintedCardNumber; ValidUntil; Enabled; email; group ");
csv.AppendLine(newLine);
//deserialize JSON to CSV
foreach (var item in UCYstudents.data)
{
if (item.ucy_id != null)
{
ucy_id = item.ucy_id;// used as parameter for WebRequestObjectUsers
ID = item.ucy_id.ToString().TrimStart('0');
RFIDUID = item.card_number.ToString().TrimStart('0');
PrintedCardNumber = item.card_number.ToString().TrimStart('0');
if (item.expiration_date != null)
{
ValidUntil = item.expiration_date.ToString().Replace("-30","-01");
dt = Convert.ToDateTime(ValidUntil);
ValidUntil = ("" + dt.Day + "." + dt.Month + "." + dt.Year);
}
else
{
ValidUntil = "";
}
Enabled = "TRUE";
//Getting response from WebRequestObjectUsers
using (System.IO.Stream s2 = WebRequestObjectUsers.GetResponse().GetResponseStream())
{
using (System.IO.StreamReader sr2 = new System.IO.StreamReader(s2))
{
serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = Int32.MaxValue; // The value of this constant is 2,147,483,647
jsonResponse = sr2.ReadToEnd();
Users UCYUser = serializer.Deserialize<Users>(jsonResponse);
FirstName = UCYUser.data.name_en.ToString();
LastName = UCYUser.data.surname_en.ToString();
email = UCYUser.data.mail.ToString();
group = "1";
//Write Fields to CSV File
newLine = string.Format("{0};{1};{2};{3};{4};{5};{6};{7};{8}", ID, FirstName, LastName, RFIDUID, PrintedCardNumber, ValidUntil, Enabled, email, group);
csv.AppendLine(newLine);
ID = "";
FirstName = "";
LastName = "";
RFIDUID = "";
PrintedCardNumber = "";
ValidUntil = "";
email = "";
group = "";
sr2.Close();
}
}
}
}
File.WriteAllText(#".\export.csv", csv.ToString(), Encoding.UTF8);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
The two HttpWebRequest objects you create are different instances of the class; you don't need to close or reset one while using the other.
If the API is following conventions, then the 401 error means exactly what the message says: the server doesn't think the second request is authorized to access that endpoint. Maybe the server has different permissions required to get the list of users. The server identifies the request and permissions through the Authorization: Bearer token that you are sending. Look at other ways to test that token and request data from that endpoint.
I have my two functions, the access attempt and the HMAC signing. It runs and returns an error(401) unauthorized, however in addition I feel my code is longer than it needs to be or redundant somehow, pointing that out would be very helpful to me, thanks in advance!
void AccessAttempt(){
var message = Epoch.ToString () + "GET" + "/v2/payment-methods";
const string WEBSERVICE_URL = "https://api.coinbase.com/v2/payment-methods";
try
{
var webRequest = System.Net.WebRequest.Create(WEBSERVICE_URL);
if (webRequest != null)
{
webRequest.Method = "POST";
webRequest.ContentType = "application/json";
webRequest.Headers.Add("CB-ACCESS-SIGN", genHMAC(message));
webRequest.Headers.Add("CB-ACCESS-TIMESTAMP", Epoch.ToString());
webRequest.Headers.Add("CB-ACCESS-KEY", _apiKey);
webRequest.Headers.Add("CB-VERSION",_apiVersion);
using (System.IO.Stream s = webRequest.GetResponse().GetResponseStream())
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(s))
{
var jsonResponse = sr.ReadToEnd();
OutputText.text = jsonResponse.ToString();
}
}
}
}
catch (Exception ex)
{
OutputText.text = ex.ToString();
}
}
Below is the HMAC signing function called within main function above:
private string genHMAC(string message)
{
byte [] APISecret_Bytes = System.Text.Encoding.UTF8.GetBytes(_apiSecret);
HMACSHA256 hmac = new HMACSHA256(APISecret_Bytes);
hmac.Initialize ();
byte [] MESSAGE_Bytes = System.Text.Encoding.UTF8.GetBytes(message);
var rawHmac = hmac.ComputeHash(MESSAGE_Bytes);
string rawHmacString = string.Empty;
for (int i=0; i<rawHmac.Length; i++)
{
rawHmacString += rawHmac[i];
}
string hexString = string.Empty;
for (int i=0; i<rawHmac.Length; i++)
{
hexString += rawHmac[i].ToString("X2");
}
return hexString;
}
This is a pretty old question, but in case you don't have an answer yet, it looks like there are a few things wrong with your request - here is some code that works for me
public class CoinbaseV2
{
private string APIKey;
private string Secret;
private const string URL_BASE = "https://api.coinbase.com";
private const string URL_BASE_VERSION = URL_BASE + "/v2/";
private const String GET = "GET";
private const String POST = "POST";
private const String PUT = "PUT";
private const String DELETE = "DELETE";
public CoinbaseV2(string inAPIKey, string inSecret)
{
APIKey = inAPIKey;
Secret = inSecret;
}
public string GetUser()
{
return JsonRequest(URL_BASE_VERSION + "user", GET);
}
public string GetUserAccounts()
{
return JsonRequest(URL_BASE_VERSION + "accounts", GET);
}
private string JsonRequest(string url, string method)
{
// take care of any spaces in params
url = Uri.EscapeUriString(url);
string returnData = String.Empty;
var webRequest = HttpWebRequest.Create(url) as HttpWebRequest;
if (webRequest != null)
{
webRequest.Method = method;
webRequest.ContentType = "application/json";
string timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(CultureInfo.CurrentCulture);
string body = "";
string sigurl = url.Replace(URL_BASE,"");
string signature = GenerateSignature(timestamp,method,sigurl,body,Secret);
var whc = new WebHeaderCollection();
whc.Add("CB-ACCESS-SIGN", signature);
whc.Add("CB-ACCESS-TIMESTAMP", timestamp);
whc.Add("CB-ACCESS-KEY", APIKey);
whc.Add("CB-VERSION", "2017-08-07");
webRequest.Headers = whc;
using (WebResponse response = webRequest.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(stream);
returnData = reader.ReadToEnd();
}
}
}
return returnData;
}
//https://github.com/bchavez/Coinbase
public static string GenerateSignature(string timestamp, string method, string url, string body, string appSecret)
{
return GetHMACInHex(appSecret, timestamp + method + url + body).ToLower();
}
internal static string GetHMACInHex(string key, string data)
{
var hmacKey = Encoding.UTF8.GetBytes(key);
var dataBytes = Encoding.UTF8.GetBytes(data);
using (var hmac = new HMACSHA256(hmacKey))
{
var sig = hmac.ComputeHash(dataBytes);
return ByteToHexString(sig);
}
}
//https://stackoverflow.com/questions/311165/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-and-vice-versa/14333437#14333437
static string ByteToHexString(byte[] bytes)
{
char[] c = new char[bytes.Length * 2];
int b;
for (int i = 0; i < bytes.Length; i++)
{
b = bytes[i] >> 4;
c[i * 2] = (char)(87 + b + (((b - 10) >> 31) & -39));
b = bytes[i] & 0xF;
c[i * 2 + 1] = (char)(87 + b + (((b - 10) >> 31) & -39));
}
return new string(c);
}
}
I am trying to access Adobe's Datawarehouse reporting using their API. The way it works is by first forming the JSON using the metrics available and the dimensions you want it to be brojen down by. That all works fine. However where I do have the issue comes down to this. Whenever I request more than 3 to 4 dimensions, the report takes longer to generate. I would need the HttpWebResponse to wait until it has been completed before moving to the next line. Below is my sample code that I have built based on an example from github.
class Program
{
protected static string JSONFolder = System.Configuration.ConfigurationManager.AppSettings["JSONFolder"];
protected static string USERNAME = System.Configuration.ConfigurationManager.AppSettings["Username"];
protected static string SECRET = System.Configuration.ConfigurationManager.AppSettings["Secret"];
protected static string ReportSuiteID = System.Configuration.ConfigurationManager.AppSettings["ReportSuiteID"];
private static string ENDPOINT = "https://api5.omniture.com/admin/1.4/rest/";
protected static string environment = "dev";
static void Main(string[] args)
{
DateTime previousDay = DateTime.Today.AddDays(-1);
string json = RequestJsonBuilder(previousDay, previousDay, ReportSuiteID);
string response = callMethod("Report.Queue", json, 15);
var jss = new JavaScriptSerializer();
var requestDetails = jss.Deserialize<Dictionary<string, string>>(response);
}
/*Build the json for the methods Report.GetStatus and Report.Get*/
public static string RequestJsonBuilderStatus(string id)
{
ReportID json = new ReportID() { reportID = id };
var serializer = new JavaScriptSerializer();
var serializedResult = serializer.Serialize(json);
return serializedResult;
}
/*Build the json for the method Report.Queue*/
static string RequestJsonBuilder(DateTime StartDate, DateTime EndDate, string ReportSuiteID)
{
//Build the list of metrics to send with the request
var listMetrics = new List<Metrics>();
listMetrics.Add(new Metrics() { id = "visits" });
//Build the list of elements to send with the request
var listElements = new List<Elements>();
listElements.Add(new Elements() { id = "page" , top = "25"});
var serializer2 = new JavaScriptSerializer();
Dictionary<string, RankedRequest> dic = new Dictionary<string, RankedRequest>();
dic.Add("reportDescription", new RankedRequest()
{
reportSuiteID = ReportSuiteID,
dateFrom = StartDate.ToString("yyyy-MM-dd"),
dateTo = EndDate.ToString("yyyy-MM-dd"),
metrics = listMetrics,
elements = listElements,
source = "warehouse"
});
var serializedResult2 = serializer2.Serialize(dic);
return serializedResult2;
}
/*Build the rest call to the Adobe Analytics REST APII 1.4*/
public static String callMethod(String method, String data, int secs)
{
Program prog = new Program();
HttpWebResponse statusResponse = null;
string responseXml = "";
StringBuilder sbUrl = new StringBuilder(ENDPOINT + "?method=" + method);
HttpWebRequest omniRequest = (HttpWebRequest)WebRequest.Create(sbUrl.ToString());
string timecreated = generateTimestamp();
string nonce = generateNonce();
string digest = getBase64Digest(nonce + timecreated + SECRET);
nonce = base64Encode(nonce);
omniRequest.Headers.Add("X-WSSE: UsernameToken Username=\"" + USERNAME + "\", PasswordDigest=\"" + digest + "\", Nonce=\"" + nonce + "\", Created=\"" + timecreated + "\"");
omniRequest.Method = "POST";
omniRequest.ContentType = "text/json";
//Write the json details to the request
using (var streamWriter = new StreamWriter(omniRequest.GetRequestStream()))
{
string json = data;
Console.WriteLine("\n 2.0 ############## Json request : \n\n " + json + "\n\n");
streamWriter.Write(json);
}
//Get the response of the request
try
{
Console.WriteLine("\n 2.0 ############## Sleeping thread for " + secs + "\n");
using (HttpWebResponse statusResponse = (HttpWebResponse) omniRequest.GetResponse())
{
using (Stream receiveStream = statusResponse.GetResponseStream())
{
using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8))
{
responseXml = readStream.ReadToEnd();
return responseXml;
}
}
}
}
catch (Exception ex) { throw ex; }
}
// other methods defined below
// private static string getBase64Digest(string input)
// private static string generateNonce()
// public static string base64Encode(string data)
// etc.
}
How do I make HttpWebResponse wait until a HTTP 200 response is actually received. This might take minutes to sometimes an hour depending on the number of metrics and dimensions I add.
Things I have tried also include changing line 88 to something like this:
How to process WebResponse when .NET throws WebException ((400) Bad Request)?
how to wait until a web request with HttpWebRequest is finished?
Sincerely appreciate all help here.
Thanks
Did you try setting the timeout? I was looking recently for the similar question and found that one:
HttpWebRequest.GetResponse() keeps getting timed out
I am trying to log in to a remote website via a login form so I can access a sub page via iFrame so I have to pass the validation to set a cookie or a session so I can get to the sub page with the iFrame Maybe there is a better way to do this I am not 100% sure if anyone has any suggestions please let me know. I am getting access denied error:
<script type="text/javascript">
function submitEstimoteLogin() {
setTimeout(function () {
var ifr = document.getElementById("iFrameLogin");
var ifrDoc = ifr.contentDocument || ifr.contentWindow.document;
var theForm = ifrDoc.forms[0];
var username = theform.getElementById("username");
username.value = "email";
var password = theform.getElementById("password");
password.value = "password";
var sbutton = thefrom.getElementById("");
}, 5000);
}
</script>
<iframe id="iFrameLogin" src="http://cloud.estimote.com" onload="submitEstimoteLogin();" ></iframe>
I have also tried it via code behind I am getting a 404 error:
public String GetWebContent(String username, String password)
{
String urlSignin = "https://cloud.estimote.com/";
String urlLogin = "https://cloud.estimote.com/";
Uri uriSignin = new Uri(urlSignin);
Uri uriLogin = new Uri(urlLogin);
Hashtable formData = new Hashtable();
formData.Add("username", new Hashtable());
formData.Add("password", new Hashtable());
((Hashtable)formData["username"])["value"] = username;
((Hashtable)formData["password"])["value"] = password;
String postData = "";
foreach (string name in formData.Keys)
{
postData += "&" + name + "=" + ((Hashtable)formData[name])["value"];
}
postData = postData.Substring(1);
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] data = encoding.GetBytes(postData);
HttpWebRequest webReq;
HttpWebResponse webResp;
CookieContainer cookies = new CookieContainer();
String responseString = "";
try
{
webReq = (HttpWebRequest)WebRequest.Create(urlSignin);
webResp = (HttpWebResponse)webReq.GetResponse();
for (int i = 0; i < webResp.Headers.Count; i++)
{
string name = webResp.Headers.GetKey(i);
if (name != "Set-Cookie")
continue;
string value = webResp.Headers.Get(i);
foreach (var singleCookie in value.Split(','))
{
Match match = Regex.Match(singleCookie, "(.+?)=(.+?);");
if (match.Captures.Count == 0)
continue;
webResp.Cookies.Add(
new Cookie(
match.Groups[1].ToString(),
match.Groups[2].ToString(),
"/",
webReq.Host.Split(':')[0]));
}
}
cookies.Add(webResp.Cookies);
string sessionCookie = webResp.Headers["Set-Cookie"];
responseString = new StreamReader(webResp.GetResponseStream()).ReadToEnd();
string respCookie = sessionCookie.Substring(0, sessionCookie.IndexOf(';'));
char[] separator = { '=' };
string[] cookieValues = respCookie.Split(separator);
string part1 = HttpUtility.UrlEncode(cookieValues[1]);
cookies.Add(new Cookie(cookieValues[0], part1 , "/", "cloud.estimote.com"));
webReq = (HttpWebRequest)WebRequest.Create(urlLogin);
webReq.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
webReq.Referer = urlSignin;
webReq.KeepAlive = true;
webReq.Method = "POST";
webReq.ContentType = "application/x-www-form-urlencoded";
webReq.ContentLength = data.Length;
webReq.AllowAutoRedirect = false;
webReq.CookieContainer = cookies;
webReq.Timeout = 30000;
webReq.ReadWriteTimeout = 60000;
webReq.GetRequestStream().Write(data, 0, data.Length);
webReq.GetRequestStream().Close();
webResp = (HttpWebResponse)webReq.GetResponse();
responseString = new StreamReader(webResp.GetResponseStream()).ReadToEnd();
webResp.Close();
return responseString;
}
catch (Exception ex)
{
throw ex;
}
}