I have an HTTP game server that I am setting up and I have one function that returns a lot of information about the map. The output from the server is about 7800 characters long, but when I get the contents of the URL in the game, the game only gets 1124 characters.
Is there a limit on the length of the response content of an IHttpRequest?
Pertinent code:
FString ANetwork::getContentsOfURL(FString URL, TArray<FString> keys, TArray<FString> values)
{
serverResponse = NULL;
TSharedRef<IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest();
HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
int32 count = keys.Num();
URL += "?auth=" + authenticator;
for (int i = 0; i < count; i++)
{
URL += "&" + keys[i] + "=" + values[i];
}
HttpRequest->SetURL(URL);
HttpRequest->SetVerb(TEXT("GET"));
HttpRequest->OnProcessRequestComplete().BindUObject(this, &ANetwork::OnResponseReceived);
HttpRequest->ProcessRequest();
bool wait = true;
while (wait)
{
FHttpResponsePtr response = HttpRequest->GetResponse();
FHttpResponsePtr httpnull;
if (response != httpnull)
{
if (HttpRequest->GetResponse()->GetContentAsString() != "")
{
return HttpRequest->GetResponse()->GetContentAsString();
}
}
}
return "";
}
On a side note, I'm not sure how to check if an FHttpResponsePtr points to a null object. I thought I had it with that code in the while loop, but it doesn't seem to have made a difference. Once in a while, the code will break because the response is null when I try to access the content as a string.
Anyone know how to properly check if it is null?
Edit:
Per #TheBrain's answer, here is my revised loop:
bool wait = true;
while (wait)
{
if (HttpRequest->GetStatus() != EHttpRequestStatus::Processing)
{
FHttpResponsePtr response = HttpRequest->GetResponse();
if (response.IsValid())
{
return response->GetContentAsString();
}
else
return "INVALID";
}
}
return "";
This causes an infinite loop, however.
I don't think there is such a small limit to the response. It looks more like you are fetching the response before it has actually processed the request. You should try to call GetResponse() only after GetStatus() retuns something other than Processing.
On the nullpointer check: FHttpResponsePtr is nothing other than a TSharedPtr. As with any TSharedPtr you can use IsValid() on the pointer itself. For example, with your code from above:
FHttpResponsePtr response = HttpRequest->GetResponse();
if (response != nullptr) { // wrong, the pointer itself is never null!
if (response.IsValid()) { // correct, check for pointer validity
if (response.Get() != nullptr) { // basically the same, but longer
EDIT:
Sorry for the misunderstanding. You must never block the game loop with a while loop like that. So you have two possibilities:
You do the check from the while loop, but only once during your actor's tick event.
You wait for your callback delegate to fire.
Here is a working code sample using a delegate:
void AYourActor::NetworkTest()
{
TSharedRef<IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest();
HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
HttpRequest->SetURL("http://www.google.com");
HttpRequest->SetVerb(TEXT("GET"));
HttpRequest->OnProcessRequestComplete().BindUObject(this, &AYourActor::OnResponseReceived);
HttpRequest->ProcessRequest();
}
void AYourActor::OnResponseReceived(FHttpRequestPtr request, FHttpResponsePtr response, bool didConnect)
{
UE_LOG(LogExec, Warning, TEXT("Response received %d!"), didConnect);
UE_LOG(LogExec, Warning, TEXT("Response: %s"), *(response->GetContentAsString()));
}
Related
I am newbie in C++ and can't understand some behavior.
Have function below and in this case it works.
bool Network::doRequest(HTTPRequest& request, string path, string content) {
HTTPResponse response;
istream* respStreamPtr;
session->sendRequest(request);
respStreamPtr = &session->receiveResponse(response);
if (response.getStatus() == HTTPResponse::HTTP_UNAUTHORIZED)
{
credentials->authenticate(request, response);
session->sendRequest(request);
respStreamPtr = &session->receiveResponse(response);
}
if (response.get("Content-Encoding") == "deflate") {
Poco::InflatingInputStream inflater(*respStreamPtr);
respStreamPtr = &std::istream(inflater.rdbuf());
Logger::dumpStream(*respStreamPtr);
}
return true;
}
But if move string Logger::dumpStream(*respStreamPtr); out of the if block. Like this:
if (response.get("Content-Encoding") == "deflate") {
Poco::InflatingInputStream inflater(*respStreamPtr);
respStreamPtr = &std::istream(inflater.rdbuf());
}
Logger::dumpStream(*respStreamPtr);
It's stop to work!!!
Condition (response.get("Content-Encoding") == "deflate") always true;
So trouble with visibility stream content in block.
But I can't understand what I do wrong.
Help me please.
P.S. In both case no exception. In second case just no data in file somefile.txt.
In first case file somefile.txt has inflated data from http request.
void Logger::dumpStream(std::istream& inputStream) {
fstream outStream("somefile.txt", ios_base::trunc | ios_base::out | ios_base::binary);
outStream << inputStream.rdbuf();
outStream.close();
}
I'm not familiar with the classes you're using, but it seems very likely that the problem is Poco::InflatingInputStream inflater is going out of scope.
Inside the if statement:
if (response.get("Content-Encoding") == "deflate") {
Poco::InflatingInputStream inflater(*respStreamPtr);
respStreamPtr = &std::istream(inflater.rdbuf());
}
respStreamPtr is being pointed at a stream which uses a buffer from your inflater object. Once the if statement closes, that buffer is no longer valid and therefore you can't use your respStreamPtr outside.
This is not C++/CLI. This is UWP C++/CX
I am trying to send an HttpRequestMessage outside of a managed class in C++. I looked at the UWP samples, but their requests occur inside of a managed class.
All I want to do is send the request, and then have a callback function. I don't need fancy async/await patterns. This is looking to be a lot more difficult than it should be.
EDIT: I have gotten it to work, but the error handling is atrocious. The extra error handling code from the UWP HttpClient example was not compiling.
client = ref new Windows::Web::Http::HttpClient();
client->DefaultRequestHeaders->UserAgent->Append(ref new Windows::Web::Http::Headers::HttpProductInfoHeaderValue("Windows", "10"));
cancellation_token_source cancellationTokenSource = cancellation_token_source();
create_task(client->SendRequestAsync(message)).then([=](Windows::Web::Http::HttpResponseMessage^ response)
{
auto operation = response->Content->ReadAsBufferAsync();
auto task = create_task(operation);
if (task.wait() == task_status::completed)
{
webResponse->statusCode = (int)response->StatusCode;
auto buffer = task.get();
size_t length = buffer->Length;
if (length > 0)
{
Array<byte>^ array = nullptr;
CryptographicBuffer::CopyToByteArray(buffer, &array);
webResponse->contentLength = array->Length;
webResponse->data = (byte*)malloc(webResponse->contentLength);
memcpy(webResponse->data, array->Data, webResponse->contentLength);
delete array;
}
for each(IKeyValuePair<String^, String^>^ pair in response->Headers)
{
std::string key = PlatformStringToString(pair->Key);
std::string value = PlatformStringToString(pair->Value);
if (key == "Content-Type" && false)
{
// Should have this for completeness, but do we really care?
}
else
{
Web::WebHeader *header = new Web::WebHeader(key.c_str(), value.c_str());
webResponse->AddHeader(header);
}
}
if (request->receiveDoneCallback)
request->receiveDoneCallback(webResponse, request->userPtr);
}
else
abort();
delete request;
delete response;
});
I'm having some trouble with the following method and I need some help trying to figure out what I am doing wrong.
I want to return a reference to a Value in a document. I am passing the Document from outside the function so that when I read a json file into it I don't "lose it".
const rapidjson::Value& CTestManager::GetOperations(rapidjson::Document& document)
{
const Value Null(kObjectType);
if (m_Tests.empty())
return Null;
if (m_current > m_Tests.size() - 1)
return Null;
Test& the_test = m_Tests[m_current];
CMyFile fp(the_test.file.c_str()); // non-Windows use "r"
if (!fp.is_open())
return Null;
u32 operations_count = 0;
CFileBuffer json(fp);
FileReadStream is(fp.native_handle(), json, json.size());
if (document.ParseInsitu<kParseCommentsFlag>(json).HasParseError())
{
(...)
}
else
{
if (!document.IsObject())
{
(...)
}
else
{
auto tests = document.FindMember("td_tests");
if (tests != document.MemberEnd())
{
for (SizeType i = 0; i < tests->value.Size(); i++)
{
const Value& test = tests->value[i];
if (test["id"].GetInt() == the_test.id)
{
auto it = test.FindMember("operations");
if (it != test.MemberEnd())
{
//return it->value; is this legitimate?
return test["operations"];
}
return Null;
}
}
}
}
}
return Null;
}
Which I am calling like this:
Document document;
auto operations = TestManager().GetOperations(document);
When I inspect the value of test["operations"] inside the function I can see everything I would expect (debug code removed from the abode code).
When I inspect the returned value outside the function I can see that it's an array (which I expect). the member count int the array is correct as well, but when print it out, I only see garbage instead.
When I "print" the Value to a string inside the methods, I get what I expect (i.e. a well formated json), but when I do it outside all keys show up as "IIIIIIII" and values that aren't strings show up correctly.
rapidjson::StringBuffer strbuf2;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer2(strbuf2);
ops->Accept(writer2);
As this didn't work I decided to change the method to receive a Value as a parameter and do a deep copy into it like this
u32 CTestManager::GetOperationsEx(rapidjson::Document& document, rapidjson::Value& operations)
{
(...)
if (document.ParseInsitu<kParseCommentsFlag>(json).HasParseError())
{
(...)
}
else
{
if (!document.IsObject())
{
(...)
}
else
{
auto tests = document.FindMember("tests");
if (tests != document.MemberEnd())
{
for (SizeType i = 0; i < tests->value.Size(); i++)
{
const Value& test = tests->value[i];
if (test["id"].GetInt() == the_test.id)
{
const Value& opv = test["operations"];
Document::AllocatorType& allocator = document.GetAllocator();
operations.CopyFrom(opv, allocator); //would Swap work?
return operations.Size();
}
}
}
}
}
return 0;
}
Which I'm calling like this:
Document document;
Value operations(kObjectType);
u32 count = TestManager().GetOperationsEx(document, operations);
But... I get same thing!!!!
I know that it's going to be something silly but I can't put my hands on it!
Any ideas?
The problem in this case lies with the use of ParseInSitu. When any of the GetOperations exist the CFileBuffer loses scope and is cleaned up. Because the json is being parsed in-situ when the buffer to the file goes, so goes the data.
In the following code a Packet (Object) should be returned wen its timeServing is 0.
Else, the timeServing should be reduced by 1. The problem is that in this case nothing should be returned. Can I somehow implement this, or is this impossible ?
public Packet Serve()
{
if(timeServing == 0)
{
Packet p = queue.dequeue();
timeServing = distrServ.getSample();
}
else
{
--timeServing;
}
return p;
}
You could return null depending on the language your programming in this might have a different name (e.g. nil)
I have some lua script that have some long running task like getting a web page so I make it yield then the C code handle get page job async, so the thread free to do other job and after a specify time it check back to see is the get page job finished , if so then resume the script. the problem is the thread can't resume the job after async wait.
here is my code I riped it from a class so a little messy sorry
////script:
function Loginmegaupload_com(hp, user, pass, cookie)
setURL(hp, "http://megaupload.com/?c=login")
importPost(hp, "login=1&redir=1")
addPost(hp, "username", user)
addPost(hp, "password", pass)
GetPage()
if isHeaderContain(hp, "user=") ~= nil then
SetFileLink(cookie, GetAllCookie(hp))
return 1
else
return 0
end
end
////c code
int FileSharingService::GetPage(lua_State *ls)
{
return lua_yield(ls, 0);
}
void FileSharingService::AsyncWait(Http_RequestEx *Http, lua_State *LS, boost::asio::deadline_timer* Timer)
{
if( (Http->status_code == Http_RequestEx::ERROR) || (Http->status_code == Http_RequestEx::FISNISHED))
{
if(Http->status_code == Http_RequestEx::FISNISHED)
{
int result = lua_resume(LS, 0); // here I got result == 2 mean error ?
if(result == 0)//lua script exit normal, resume success
{
delete Http;
delete Timer;
}
}
else
return;
}
else
{
Timer->expires_from_now(boost::posix_time::milliseconds(200));
Timer->async_wait(boost::bind(&FileSharingService::AsyncWait, this, Http, LS, Timer));
}
}
bool FileSharingService::Login(string URL, string User, string Pass, string &Cookie)
{
Http_RequestEx *http = new Http_RequestEx;
http->url = URL;
LuaWarper* Lua = Lua_map[boost::this_thread::get_id()]; //one main luaState per ioservice thread
lua_State *thread = lua_newthread(Lua->GetState());
boost::asio::deadline_timer *timer = new boost::asio::deadline_timer(*HClient.ioservice);
string functioname = "Login" + GetServicename(URL);
if( Lua->isFunctionAvaliable(functioname.c_str()) == false )
{
throw(FileSharingService::SERVICE_NOT_AVALIABLE);
}
else
{
lua_getglobal(thread, functioname.c_str());
lua_pushlightuserdata(thread, http);
lua_pushstring(thread, User.c_str());
lua_pushstring(thread, Pass.c_str());
lua_pushlightuserdata(thread, &Cookie);
int result = lua_resume(thread, 4);
if(result == LUA_YIELD)
{
HClient.Do(*http, false);
AsyncWait(http, thread, timer);
}
else if(result == 0)
{
//fisnished at first call
}
else
{
//yield error, will handle late
}
}
}
Sorry never mind this question, lua_resume return 2 mean error but script work just fine, asio get page work fine too, and I tracked down the line that respond for fail of lua_resume :
httpinfo.header.append(buffer, (HeaderEndIndex-buffer+2) );
if I comment that line lua_resume work as expected it return 0 mean script exit, this line don't do any thing that can affect the lua thread state it just a string assign, I checked there no overflow. so weird.