Why is the concurrent dictionary not behaving like a concurrent dictionary?
[Route("aaa")]
[HttpPost]
public Outbound Post(Inbound inbound)
{
Outbound outbound = new Outbound();
TaskState taskState = new TaskState();
taskState.state = 0;
TaskState s;
bool rc = taskDictionary.TryGetValue(inbound.taskId, out s);
if (rc == false)
taskDictionary.GetOrAdd(inbound.taskId, taskState);
else
return null;
if (s != null)
return null;
rc = taskDictionary.TryGetValue(inbound.taskId, out s);
if (rc)
{
string m0 = String.Format("POST dictionary count is " + taskDictionary.Count);
_logger.Log(LogLevel.Error, m0);
}
else
return null;
_logger.Log(LogLevel.Error, "POST method is doing long running work.");
long Billion = 1000000000;
// work quantity 5 means about 26 seconds // 30 billion is 215 seconds
for (long i = 0; i < inbound.WorkQuantity * Billion; i++)
;
outbound.Message = "The server did some work.";
outbound.BigObject = Modify(inbound.BigObject);
_logger.Log(LogLevel.Error, "POST finished long running work and will soon remove from the dictionary.");
rc = taskDictionary.Remove(inbound.taskId, out s);
if (rc == false)
return null;
if (s == null)
return null;
_logger.Log(LogLevel.Error, "POST is returning the object.");
return outbound;
}
[Route("bbb")]
[HttpPost]
public TaskState PostState(TaskId taskId)
{
TaskState s;
if (taskDictionary.TryGetValue(taskId, out s))
{
_logger.Log(LogLevel.Error, "POSTSTATE, state is " + s.AsString());
return s;
}
else
{
_logger.Log(LogLevel.Error, "POSTSTATE, state not found, dictionary count is " + taskDictionary.Count);
return null;
}
}
TaskDictionary taskDictionary = new TaskDictionary();
class TaskDictionary : ConcurrentDictionary<TaskId, TaskState>
{
internal bool IncrementState(TaskId taskId)
{
TaskState s;
if (TryGetValue(taskId, out s))
{
s.Increment();
return true;
}
else
return false;
}
}
The logging output is...
12.207 +00:00 [Error] MyController: POST dictionary count is 1
12.322 +00:00 [Error] MyController: POST method is doing long running work.
14.361 +00:00 [Error] MyController: POSTSTATE, state not found, dictionary count is 0
40.452 +00:00 [Error] MyController: POST finished long running work and will soon remove from the dictionary.
40.569 +00:00 [Error] MyController: POST is returning the object.
Specifically the problem is that request for the state information does not work because the dictionary appears empty.
So, based on the logging output you are doing two post requests to generate the output that you show us.
The way you initialize the Concurrent Dictionary, is the reason why it returns null on the second request. And that has to do with how the app receives those requests. Each request to your app is independent of another request.
Simply put;
A user POST's to you app.
The requests goes through the middleware pipeline, and eventually it will end up at your controller. Now, here's the important part, the controller will be 'constructed' for this particular request. And it will live for the duration of the request. That's because the lifetime of a controller is scoped by default. So the next request, will construct a new controller and that means that the Dictionary is different from the first one.
So to overcome this scoped request problem, you create a service that contains the dictionary, register this as a singleton (meaning it will only be constructed once and then it's shared), and use dependency injection to use it in the controller.
Related
I tried making a HTTP request to my localhost that is running Laravel Api.
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(url + "update"); //request destination
http.addHeader("Content-Type", "application/x-www-form-urlencoded"); //content-type header
String stringData = "payload=" + data;
int httpCode = http.POST(stringData);
String payload = http.getString();
Serial.print(httpCode);
http.end();
}
delay(2000);
}
When I reduce delay value <= 2000, nodeMCU is not performing as expected.
While testing getting 429 error.
Please suggest an alternative that can update every second.
429 Too Many Requests "indicates the user has sent too many requests in a given amount of time". The server could be slow, or rate limited.
The server may send a Retry-After header; if it does, it tells you how long you have to wait before a new request is made.
I suspect you would have to change things on the server side to make it as fast as you want; I doubt the ESP8266 is to blame.
Note that if handling a request takes longer than 1s, you're out of luck anyway.
BTW could you try the code below and see if it works? Just to rule out some other potential problems. It removes the inefficient delay() and only does HTTPClient http; once.
HTTPClient http;
unsigned long int lastPost = 0;
int postInterval = 1000; // Post every second
void setup() {
// Setup stuffs
}
void loop() {
if (WiFi.status() == WL_CONNECTED && (millis() - lastPost) >= postInterval) {
http.begin(url + "update"); //request destination
http.addHeader("Content-Type", "application/x-www-form-urlencoded"); //content-type header
String stringData = "payload=" + data;
int httpCode = http.POST(stringData);
String payload = http.getString();
Serial.print(httpCode);
http.end();
lastPost = millis();
}
}
Untested, just typed it in, but you get the idea.
I have the code to download two files from Server and store It to In local using URLSession (let dataTask = defaultSession.downloadTask(with: url)). Everything Is working fine only the problem is it's downloading first file it's giving me success but the second file is not downloading completely.. So, I hope there is a way to restart download for the second file that gives error ..
I think there is way of doing that and start looking into it and I found this delegate method .. but not much help .. can anyone please help me out how to restart download if it fails .. Do i have to use handleEventsForBackgroundURLSession to clear up previous downloads..?
// bellow download method will triggered when i get filenames I am passing it to this and path is optional here..
func download(path: String?, filenames: [String]) -> Int {
for filename in filenames {
var downloadFrom = "ftp://" + username! + ":"
downloadFrom += password!.addingPercentEncoding(withAllowedCharacters: .urlPasswordAllowed)! + "#" + address!
if let downloadPort = port {
downloadFrom += ":" + String(downloadPort) + "/"
} else {
downloadFrom += "/"
}
if let downloadPath = path {
if !downloadPath.isEmpty {
downloadFrom += downloadPath + "/"
}
}
downloadFrom += filename
if let url = URL(string: downloadFrom) {
let dataTask = defaultSession.downloadTask(with: url)
dataTask.resume()
}
}
return DLResponseCode.success
}
Please find delegate methods bellow ..
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
var responseCode = DLResponseCode.success
// Move the file to a new URL
let fileManager = FileManager.default
let filename = downloadTask.originalRequest?.url?.lastPathComponent
let destUrl = cacheURL.appendingPathComponent(filename!)
do {
let data = try Data(contentsOf: location)
// Delete it if it exists first
if fileManager.fileExists(atPath: destUrl.path) {
do{
try fileManager.removeItem(at: destUrl)
} catch let error {
danLogError("Clearing failed downloadFOTA file failed: \(error)")
responseCode = DLResponseCode.datalogger.failToCreateRequestedProtocolPipe
}
}
try data.write(to: destUrl)
} catch {
danLogError("Issue saving data locally")
responseCode = DLResponseCode.datalogger.noDataConnection
}
// Complete the download message
let message = DLBLEDataloggerChannel.Commands.download(responseCode: responseCode).description
connectionManagerDelegate?.sendMessageToDatalogger(msg: message)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if error == nil {
print("session \(session) download completed")
} else {
print("session \(session) download failed with error \(String(describing: error?.localizedDescription))")
// session.downloadTask(withResumeData: <#T##Data#>)
}
guard error != nil else {
return
}
danLogError("Session \(session) invalid with error \(String(describing: error))\n")
let responseCode = DLResponseCode.datalogger.failToCreateRequestedProtocolPipe
let message = DLBLEDataloggerChannel.Commands.download(responseCode: responseCode).description
connectionManagerDelegate?.sendMessageToDatalogger(msg: message)
}
// When I call didWriteData delegate method it's printing below data seems not dowloaded complete data ..
session <__NSURLSessionLocal: 0x103e37970> download task <__NSCFLocalDownloadTask: 0x108d2ee60>{ taskIdentifier: 2 } { running } wrote an additional 30028 bytes (total 988980 bytes) out of an expected 988980 bytes.
//error that I am getting for second file .. this error is coming some times not always but most of the times..
session <__NSURLSessionLocal: 0x103e37970> download failed with error Optional("cancelled")
Please help me out to figure it out .. If there is any way to handle download again after it fails or why it fails ..
The resume data, if the request is resumable, should be in the NSError object's userInfo dictionary.
Unfortunately, Apple seems to have completely trashed the programming guide for NSURLSession (or at least I can't find it in Google search results), and the replacement content in the reference is missing all of the sections that talk about how to do proper error handling (even the constant that you're looking for is missing), so I'm going to have to describe it all from memory with the help of looking at the headers. Ick.
The key you're looking for is NSURLSessionDownloadTaskResumeData.
If that key is present, its value is a small NSData blob. Store that, then use the Reachability API (with the actual hostname from that request's URL) to decide when to retry the request.
After Reachability tells you that the server is reachable, create a new download task with the resume data and start it.
I want to implement an IQHandler, but I want to make sure that only authenticated users can send IQ Packets to it. I want to make sure that the JID I get from Packet.getFrom() is the authenticated user that sent it.
I need this so that no one can just create an IQ Packet and set the "from" attribute to a user id other than their own. Can someone help me with this?
Try this:
ClientSession session = sessionManager.getSession(sender);
if(session.getStatus() == Session.STATUS_AUTHENTICATED) {
//YOUR STUFF HERE
}
UPDATE:
Looking closer at the source. It appears that the IQRouter already does this for you. If you are not authenticated the server response with an error stating just that.
public void route(IQ packet) {
if (packet == null) {
throw new NullPointerException();
}
JID sender = packet.getFrom();
ClientSession session = sessionManager.getSession(sender);
try {
// Invoke the interceptors before we process the read packet
InterceptorManager.getInstance().invokeInterceptors(packet, session, true, false);
JID to = packet.getTo();
if (session != null && to != null && session.getStatus() == Session.STATUS_CONNECTED &&
!serverName.equals(to.toString())) {
// User is requesting this server to authenticate for another server. Return
// a bad-request error
IQ reply = IQ.createResultIQ(packet);
reply.setChildElement(packet.getChildElement().createCopy());
reply.setError(PacketError.Condition.bad_request);
session.process(reply);
Log.warn("User tried to authenticate with this server using an unknown receipient: " +
packet.toXML());
}
else if (session == null || session.getStatus() == Session.STATUS_AUTHENTICATED || (
isLocalServer(to) && (
"jabber:iq:auth".equals(packet.getChildElement().getNamespaceURI()) ||
"jabber:iq:register"
.equals(packet.getChildElement().getNamespaceURI()) ||
"urn:ietf:params:xml:ns:xmpp-bind"
.equals(packet.getChildElement().getNamespaceURI())))) {
handle(packet);
}
else {
IQ reply = IQ.createResultIQ(packet);
reply.setChildElement(packet.getChildElement().createCopy());
reply.setError(PacketError.Condition.not_authorized);
session.process(reply);
}
// Invoke the interceptors after we have processed the read packet
InterceptorManager.getInstance().invokeInterceptors(packet, session, true, true);
}
I am creating an application that is mentioned to connect to an instance of on owncloud server but i can't find why it doesn't connect to the server .Instead of that the reply i get to the login screen and i get the html code for it
this is the code responsible for the connection
//the network request and reply
QNetworkAccessManager * manager = new QNetworkAccessManager();
QUrl url (url1);
manager->get(QNetworkRequest(url));
connect(manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
SLOT(provideAuthenication(QNetworkReply*,QAuthenticator*)));
connect(manager, SIGNAL(finished(QNetworkReply *)),
this, SLOT(result(QNetworkReply *)));
the reply code
void Login::result(QNetworkReply *reply)
{
reply->deleteLater();
if(reply->error() == QNetworkReply::NoError) {
// Get the http status code
int v = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (v >= 200 && v < 300) // Success
{
qDebug()<<"Here we got the final reply";
QString replyText = reply->readAll();
qDebug()<<replyText;
}
else if (v >= 300 && v < 400) // Redirection
{
qDebug()<<"Get the redirection url";
QUrl newUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
// Because the redirection url can be relative,
// we have to use the previous one to resolve it
newUrl = reply->url().resolved(newUrl);
QNetworkAccessManager *manager = reply->manager();
QNetworkRequest redirection(newUrl);
QNetworkReply *newReply = manager->get(redirection);
QString replyText = newReply->readAll();
qDebug()<<replyText;
return; // to keep the manager for the next request
}
}
else
{
// Error
qDebug()<<reply->errorString();
}
reply->manager()->deleteLater();
}
could you help me figure out why i get the login screen instead of authentication ?
Try to call connect() before calling manager->get() otherwise when the authentication required signal is triggered, there might not be any slots present to call to handle that signal.
Try this instead:
QNetworkAccessManager * manager = new QNetworkAccessManager();
QUrl url (url1);
connect(manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
SLOT(provideAuthenication(QNetworkReply*,QAuthenticator*)));
connect(manager, SIGNAL(finished(QNetworkReply *)),
this, SLOT(result(QNetworkReply *)));
manager->get(QNetworkRequest(url));
I have a good start on a technique similar to this in Express 3
http://notjustburritos.tumblr.com/post/22682186189/socket-io-and-express-3
the idea being to let me grab the session object from within a socket.io connection callback, storing sessions via connect-redis in this case.
So, in app.configure we have
var db = require('connect-redis')(express)
....
app.configure(function(){
....
app.use(express.cookieParser(SITE_SECRET));
app.use(express.session({ store: new db }));
And in the app code there is
var redis_client = require('redis').createClient()
io.set('authorization', function(data, accept) {
if (!data.headers.cookie) {
return accept('Sesssion cookie required.', false)
}
data.cookie = require('cookie').parse(data.headers.cookie);
/* verify the signature of the session cookie. */
//data.cookie = require('cookie').parse(data.cookie, SITE_SECRET);
data.sessionID = data.cookie['connect.sid']
redis_client.get(data.sessionID, function(err, session) {
if (err) {
return accept('Error in session store.', false)
} else if (!session) {
return accept('Session not found.', false)
}
// success! we're authenticated with a known session.
data.session = session
return accept(null, true)
})
})
The sessions are being saved to redis, the keys look like this:
redis 127.0.0.1:6379> KEYS *
1) "sess:lpeNPnHmQ2f442rE87Y6X28C"
2) "sess:qsWvzubzparNHNoPyNN/CdVw"
and the values are unencrypted JSON. So far so good.
The cookie header, however, contains something like
{ 'connect.sid': 's:lpeNPnHmQ2f442rE87Y6X28C.obCv2x2NT05ieqkmzHnE0VZKDNnqGkcxeQAEVoeoeiU' }
So now the SessionStore and the connect.sid don't match, because the signature part (after the .) is stripped from the SessionStore version.
Question is, is is safe to just truncate out the SID part of the cookie (lpeNPnHmQ2f442rE87Y6X28C) and match based on that, or should the signature part be verified? If so, how?
rather than hacking around with private methods and internals of Connect, that were NOT meant to be used this way, this NPM does a good job of wrapping socket.on in a method that pulls in the session, and parses and verifies
https://github.com/functioncallback/session.socket.io
Just use cookie-signature module, as recommended by the comment lines in Connect's utils.js.
var cookie = require('cookie-signature');
//assuming you already put the session id from the client in a var called "sid"
var sid = cookies['connect.sid'];
sid = cookie.unsign(sid.slice(2),yourSecret);
if (sid == "false") {
//cookie validation failure
//uh oh. Handle this error
} else {
sid = "sess:" + sid;
//proceed to retrieve from store
}