/// <summary> /// Prepares the <see cref="RequestEnvelope" /> to be sent with <see cref="_httpClient" />. /// </summary> /// <param name="requestEnvelope">The <see cref="RequestEnvelope" /> that will be send.</param> /// <returns><see cref="StreamContent" /> to be sent with <see cref="_httpClient" />.</returns> private ByteArrayContent PrepareRequestEnvelope(RequestEnvelope requestEnvelope) { var messageBytes = requestEnvelope.ToByteArray(); // TODO: Compression? return(new ByteArrayContent(messageBytes)); }
private async Task <ByteString> PerformRemoteProcedureCallAsync(RequestEnvelope requestEnvelope) { try { using (var requestData = new ByteArrayContent(requestEnvelope.ToByteArray())) { Logger.Debug($"Sending RPC Request: '{string.Join(", ", requestEnvelope.Requests.Select(x => x.RequestType))}'"); Logger.Debug($"=> Platform Request: '{string.Join(", ", requestEnvelope.PlatformRequests.Select(x => x.Type))}'"); using (var response = await _session.HttpClient.PostAsync(_requestUrl ?? Constants.ApiUrl, requestData)) { if (!response.IsSuccessStatusCode) { Logger.Debug(await response.Content.ReadAsStringAsync()); throw new Exception("Received a non-success HTTP status code from the RPC server, see the console for the response."); } var responseBytes = response.Content.ReadAsByteArrayAsync().Result; var responseEnvelope = ResponseEnvelope.Parser.ParseFrom(responseBytes); switch (responseEnvelope.StatusCode) { // Valid response. case ResponseEnvelope.Types.StatusCode.Ok: // Success!? break; // Valid response and new rpc url. case ResponseEnvelope.Types.StatusCode.OkRpcUrlInResponse: if (Regex.IsMatch(responseEnvelope.ApiUrl, "pgorelease\\.nianticlabs\\.com\\/plfe\\/\\d+")) { _requestUrl = $"https://{responseEnvelope.ApiUrl}/rpc"; } else { throw new Exception($"Received an incorrect API url: '{responseEnvelope.ApiUrl}', status code was: '{responseEnvelope.StatusCode}'."); } break; // A new rpc endpoint is available. case ResponseEnvelope.Types.StatusCode.Redirect: if (Regex.IsMatch(responseEnvelope.ApiUrl, "pgorelease\\.nianticlabs\\.com\\/plfe\\/\\d+")) { _requestUrl = $"https://{responseEnvelope.ApiUrl}/rpc"; return(await PerformRemoteProcedureCallAsync(requestEnvelope)); } throw new Exception($"Received an incorrect API url: '{responseEnvelope.ApiUrl}', status code was: '{responseEnvelope.StatusCode}'."); // The login token is invalid. // TODO: Make cleaner to reduce duplicate code with the GetRequestEnvelopeAsync method. case ResponseEnvelope.Types.StatusCode.InvalidAuthToken: Logger.Debug("Received StatusCode 102, reauthenticating."); _session.AccessToken.Expire(); await _session.Reauthenticate(); // Apply new token. requestEnvelope.AuthInfo = new RequestEnvelope.Types.AuthInfo { Provider = _session.AccessToken.ProviderID, Token = new RequestEnvelope.Types.AuthInfo.Types.JWT { Contents = _session.AccessToken.Token, Unknown2 = 59 } }; // Re-sign envelope. var signature = requestEnvelope.PlatformRequests.FirstOrDefault(x => x.Type == PlatformRequestType.SendEncryptedSignature); if (signature != null) { requestEnvelope.PlatformRequests.Remove(signature); } requestEnvelope.PlatformRequests.Insert(0, await _rpcEncryption.GenerateSignatureAsync(requestEnvelope)); // Re-send envelope. return(await PerformRemoteProcedureCallAsync(requestEnvelope)); default: Logger.Info($"Unknown status code: {responseEnvelope.StatusCode}"); break; } LastRpcRequest = DateTime.UtcNow; if (requestEnvelope.Requests[0].RequestType == RequestType.GetMapObjects) { LastRpcMapObjectsRequest = LastRpcRequest; LastGeoCoordinateMapObjectsRequest = _session.Player.Coordinate; } if (responseEnvelope.AuthTicket != null) { _session.AccessToken.AuthTicket = responseEnvelope.AuthTicket; Logger.Debug("Received a new AuthTicket from Pokemon!"); } var mapPlatform = responseEnvelope.PlatformReturns.FirstOrDefault(x => x.Type == PlatformRequestType.UnknownPtr8); if (mapPlatform != null) { var unknownPtr8Response = UnknownPtr8Response.Parser.ParseFrom(mapPlatform.Response); _mapKey = unknownPtr8Response.Message; } return(HandleResponseEnvelope(requestEnvelope, responseEnvelope)); } } } catch (Exception e) { Logger.Error($"SendRemoteProcedureCall exception: {e}"); return(null); } }
/// <summary> /// Tries to send the <see cref="RequestEnvelope"/> message to the network. /// Returns an <typeparamref name="TResponseType"/> when completed. /// </summary> /// <param name="envelope">Envolope to send.</param> /// <returns>An awaitable future result.</returns> public IFuture <IEnumerable <TResponseType> > SendRequestAsFutures <TResponseType, TFutureType>(RequestEnvelope envelope, TFutureType responseMessageFuture, string url) where TResponseType : class, IResponseMessage, IMessage <TResponseType>, IMessage, new() where TFutureType : IFuture <IEnumerable <TResponseType> >, IAsyncCallBackTarget { //TODO: Add URL/URI //TODO: Verify header stuff IRestRequest request = new RestRequest(url).AddParameter("application/x-www-form-urlencoded", envelope.ToByteArray(), ParameterType.RequestBody); request.Method = Method.POST; var requestFuture = new RestSharpAsyncRequestFuturesDeserializationDecorator <TFutureType, TResponseType>(responseMessageFuture); //To send protobuf requests httpClient.ExecuteAsync(request, (res, hand) => { requestFuture.OnResponse(res); }); //we have to provide the future as the callback return(requestFuture); }
/// <summary> /// Tries to send the <see cref="RequestEnvelope"/> message to the network. /// Returns an <see cref="IFuture{ResponseEnvelope}"/> when completed. /// </summary> /// <param name="envelope">Envolope to send.</param> /// <returns>An awaitable future result.</returns> public IFuture <ResponseEnvelope> SendRequestAsResponseFuture <TFutureType>(RequestEnvelope envelope, TFutureType responseEnvelopeFuture, string url) where TFutureType : IFuture <ResponseEnvelope>, IAsyncCallBackTarget { //TODO: Add URL/URI //TODO: Verify header stuff IRestRequest request = new RestRequest(url).AddParameter("application/x-www-form-urlencoded", envelope.ToByteArray(), ParameterType.RequestBody); request.Method = Method.POST; var requestFuture = new RestSharpAsyncRequestFutureDeserializationDecorator <TFutureType>(responseEnvelopeFuture); httpClient.ExecuteAsync(request, (res, hand) => { requestFuture.OnResponse(res); }); return(requestFuture); }
private async Task <ByteString> PerformRemoteProcedureCallAsync(RequestEnvelope requestEnvelope) { if (requestEnvelope == null) { return(null); } try { switch (_session.State) { case SessionState.Stopped: _session.Logger.Error("We tried to send a request while the session was stopped."); return(null); case SessionState.Banned: _session.Logger.Error("We tried to send a request while the session was temporal banned."); return(null); case SessionState.Paused: break; } using (var requestData = new ByteArrayContent(requestEnvelope.ToByteArray())) { if (requestData == null) { await Task.Delay(10000); //wait 10 secs on grave bug throw new SessionStateException($"RequestData is null"); } using (var response = await _session.HttpClient.PostAsync(_requestUrl ?? Constants.ApiUrl, requestData)) { if (response == null) { await Task.Delay(10000); //wait 10 secs on grave bug throw new SessionStateException($"RequestData response is null"); } _session.Logger.Debug("Sending RPC Request: '" + string.Join(", ", requestEnvelope.Requests.Select(x => x.RequestType)) + "'"); _session.Logger.Debug("=> Platform Request: '" + string.Join(", ", requestEnvelope.PlatformRequests.Select(x => x.Type)) + "'"); if (!response.IsSuccessStatusCode) { _session.Logger.Warn(await response.Content.ReadAsStringAsync()); await Task.Delay(10000); //wait 10 secs on grave bug throw new Exception("Received a non-success HTTP status code from the RPC server, see the console for the response."); } var responseBytes = await response.Content.ReadAsByteArrayAsync(); var responseEnvelope = ResponseEnvelope.Parser.ParseFrom(responseBytes); switch (responseEnvelope.StatusCode) { // Valid response. case ResponseEnvelope.Types.StatusCode.Ok: // Success!? break; // Valid response and new rpc url. case ResponseEnvelope.Types.StatusCode.OkRpcUrlInResponse: if (Regex.IsMatch(responseEnvelope.ApiUrl, "pgorelease\\.nianticlabs\\.com\\/plfe\\/\\d+")) { _requestUrl = $"https://{responseEnvelope.ApiUrl}/rpc"; } else { await Task.Delay(10000); //wait 10 secs on grave bug throw new Exception($"Received an incorrect API url: '{responseEnvelope.ApiUrl}', status code was: '{responseEnvelope.StatusCode}'."); } break; // A new rpc endpoint is available. case ResponseEnvelope.Types.StatusCode.Redirect: if (Regex.IsMatch(responseEnvelope.ApiUrl, "pgorelease\\.nianticlabs\\.com\\/plfe\\/\\d+")) { _requestUrl = $"https://{responseEnvelope.ApiUrl}/rpc"; return(await PerformRemoteProcedureCallAsync(requestEnvelope)); } await Task.Delay(10000); //wait 10 secs on grave bug throw new Exception($"Received an incorrect API url: '{responseEnvelope.ApiUrl}', status code was: '{responseEnvelope.StatusCode}'."); // The login token is invalid. // TODO: Make cleaner to reduce duplicate code with the GetRequestEnvelopeAsync method. case ResponseEnvelope.Types.StatusCode.InvalidAuthToken: _session.Logger.Debug("Received StatusCode 102, reauthenticating."); await _session.GetValidAccessToken(true); // Apply new token. requestEnvelope.AuthInfo = new RequestEnvelope.Types.AuthInfo { Provider = _session.AccessToken.ProviderID, Token = new RequestEnvelope.Types.AuthInfo.Types.JWT { Contents = _session.AccessToken.Token, Unknown2 = 59 } }; // Clear all PlatformRequests. requestEnvelope.PlatformRequests.Clear(); // Re-send envelope. ByteString resend = null; resend = await PerformRemoteProcedureCallAsync(requestEnvelope); if (resend == null) { throw new SessionStateException("INVALID AUTH TOKEN"); } else { return(resend); } throw new SessionStateException("INVALID AUTH TOKEN"); case ResponseEnvelope.Types.StatusCode.BadRequest: await Task.Delay(10000); //wait 10 secs on grave bug throw new APIBadRequestException("BAD REQUEST"); case ResponseEnvelope.Types.StatusCode.SessionInvalidated: await Task.Delay(10000); //wait 10 secs on grave bug throw new SessionInvalidatedException("SESSION INVALIDATED"); case ResponseEnvelope.Types.StatusCode.Unknown: await Task.Delay(10000); //wait 10 secs on grave bug throw new SessionUnknowException("UNKNOWN"); case ResponseEnvelope.Types.StatusCode.InvalidPlatformRequest: await Task.Delay(10000); //wait 10 secs on grave bug throw new InvalidPlatformException("INVALID PLATFORM REQUEST"); case ResponseEnvelope.Types.StatusCode.InvalidRequest: await Task.Delay(10000); //wait 10 secs on grave bug throw new InvalidPlatformException("INVALID REQUEST"); default: await Task.Delay(10000); //wait 10 secs on grave bug throw new Exception($"Unknown status code: {responseEnvelope.StatusCode}"); } LastRpcRequest = DateTime.UtcNow; if (requestEnvelope.Requests[0].RequestType == RequestType.CreateOrUpdatePlayer) { LastRpcMapObjectsRequest = LastRpcRequest; LastGeoCoordinateMapObjectsRequest = _session.Player.Coordinate; } if (responseEnvelope.AuthTicket != null) { _session.AccessToken.AuthTicket = responseEnvelope.AuthTicket; _session.Logger.Debug("Received a new AuthTicket from Wizards!"); } return(HandleResponseEnvelope(requestEnvelope, responseEnvelope)); } } } catch (SessionInvalidatedException ex) { throw ex; } catch (InvalidPlatformException ex) { throw ex; } catch (APIBadRequestException ex) { throw new APIBadRequestException(ex.Message); } catch (Exception ex) { throw new SessionStateException($"Your account may be temporary banned! please try from the official client." + ex); } }
/// <summary> /// Prepares the <see cref="RequestEnvelope" /> to be sent with <see cref="_httpClient" />. /// </summary> /// <param name="requestEnvelope">The <see cref="RequestEnvelope" /> that will be send.</param> /// <returns><see cref="StreamContent" /> to be sent with <see cref="_httpClient" />.</returns> private ByteArrayContent PrepareRequestEnvelope(RequestEnvelope requestEnvelope) { var messageBytes = requestEnvelope.ToByteArray(); // TODO: Compression? return new ByteArrayContent(messageBytes); }