Webclient 401 Unauthorized Issue on above 5 minutes of processing time - sharepoint

I have HttpHandler (abc.ashx), which I make a call to process (Bulk Upload) an excel file. This processing takes a certain amount of time, which is proportionate to the number of rows in the excel.
On top of this I have an Event Handler in Sharepoint which uses a WebClient to make a post service call to this handler using the code below;
NOTE: I generate an identity token using elevated privilege of Sharepoint, and use that token to impersonate to make the service call. net net I make the post service call using the identity of the System Account.
WindowsIdentity identity = null;
// Get an identity token using delegate activity of Elevated Privelleges of Sharepoint
SPHelper.ElevatedActivity(properties.Web.Site.Url, properties.Web.ServerRelativeUrl, web =>
{
identity = WindowsIdentity.GetCurrent();
});
//Impersonates the identity of System Account user, as received in token in the line above
using (identity.Impersonate())
{
// create a web client object which only increases the timeout of the web client call
var webClient = (new CustomWebClient());
webClient.Credentials = CredentialCache.DefaultNetworkCredentials;
webClient.UseDefaultCredentials = true;
//url of the httphandler as deployed in sharepoint, and the parameters that needs to be passed
string handlerUrl =
string.Format(
properties.Web.Url.Trim('/') + "/_layouts/Handlers/abc.ashx?Table=BulkUpload&Region={0}&UserId={1}&BatchId={2}&FileUrl={3}",
region, userValue.User.LoginName, batchId, fileUrl);
}
// execute the web service call using the webclient object
webClient.DownloadString(handlerUrl));
Essentially this code is written in the Event Handler for ItemAdded and ItemUpdated event.
The issue is in the webClient which seems to give an error "system.net.webexception: the remote server returned an error: (401) unauthorized", after precisely 5 mins of processing. Hence if the excel sheet to be processed has rows less than certain number (1700), the processing happens within 5 minutes, and everything runs fine without any error. However if it has more than that, then the processing takes more than 5 minutes, and fails with the error specificed above.
The strange behaviour is it seems like a timeout issue but the error message indicates authorization issue, which does not make sense, as if there was an authorization issue, it shouldnt have worked even when the processing time was less that 5 minutes.
We have tried to find any configuration that times out in 5 minutes, however we could not find any.
Any help or suggestion on this matter is appreciated.
UPDATE: I have now tried to make this work using HttpWebRequest, and have tried a bunch of setting which might cause this timeout/issue. However still getting the same issue, where after 5 minutes of processing I am getting "system.net.webexception: the remote server returned an error: (401) unauthorized". Below is the code I have tried
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(handlerUrl);
httpWebRequest.Credentials = CredentialCache.DefaultCredentials;
httpWebRequest.UseDefaultCredentials = true;
httpWebRequest.Timeout = 600000;
httpWebRequest.ReadWriteTimeout = 600000;
httpWebRequest.ServicePoint.ConnectionLeaseTimeout = 600000;
httpWebRequest.ServicePoint.MaxIdleTime = 600000;
httpWebRequest.ContentType = "application/json; charset=utf-8";
httpWebRequest.ContentLength = 0;
httpWebRequest.Method = WebRequestMethods.Http.Get;
BulkUploadProcessorResponse response = null;
var httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (Stream stream = httpWebResponse.GetResponseStream())
{
using (StreamReader sr = new StreamReader(stream))
{
response =
JsonHelper.JsonDeserialize<BulkUploadProcessorResponse>(sr.ReadToEnd());
}
}

Related

Global variables values Node.js are missing on App Engine

I have a Node.js service deployed on App Engine which uses the Dialogflow fulfillment library. The scenario is this: I have an async function which retrieves the credentials using Secret manager and, with that info, calls an API that brings a url instance and a token back. This is a server-to-server authentication (OAuth), so it is the same for all users that access it. I set those values in global variables, like this:
let globalUser = "";
let globalPass = "";
...
async function credentials() {
const credentials = await secretsInstance.getCredentials();
const parsedCredentials = JSON.parse(credentials);
const user = parsedCredentials.user;
const pass = parsedCredentials.pass;
//setting the values to the global variables
globalUser = user;
globalPass = pass;
//call the authentication API - in the callback I set other global variables
await authApiInstance.authenticate(user, pass, callback);
}
After the callback function is called, I set the instance url and token to the global variables.
The token gets expired each 20 minutes, so I need to keep it updated. For that I call a setInterval function in which I call the authApiInstance.authenticate(...)
The problem here is that, when receiving a POST request coming from Dialogflow, I need to call another API that needs that url, which in this stage is empty for the first time, so it throws ECONNREFUSED. Then if I call the server other times, the variable is set.
The logs in GCP are like this:
2020-08-14 23:29:49.078 BRT
"Calling the loadQuestions API
2020-08-14 23:29:49.078 BRT
"The url is: /services/…
2020-08-14 23:29:49.091 BRT
"CATCH: Error: connect ECONNREFUSED 127.0.0.1:80"
2020-08-14 23:29:49.268 BRT
dialogflowGatewayProdxjmztxaet4d8Function execution took 764 ms, finished with status code:
200
2020-08-14 23:29:49.278 BRT
{ message_id: '39045207393', status: 200 }
2020-08-14 23:29:49.289 BRT
"Credentials ok"
2020-08-14 23:29:49.976 BRT
"Url set"
As it can be seen, the credentials and url were set after the API got called, so it didn't have a url to proceed successfully with the call.
I could call the function inside the POST, each time there is a request to guarantee that it will always exist, but the performance would be lost, especially dealing with Chatbots that must be quick.
I also tried the warmup approach, in which theoretically it would be called when deploying and changing the instance (but it could not be called, as by docs):
app.get('/_ah/warmup', (req, res) => {
credentials();
});
How could I approach this? I'm pretty new to Node.js and the server world.
Thanks
credentials(); by itself. no need to do it in express. The issue i would be race condition on the the shared credential.
crude example assuming the event loop has only these script in queue :
let say, you have 2 concurrent users A and B. A request and found the credential expire which in turn request new credential. B request before the credential return from A request, which in turn request another credential. Based on node eventloop, A then get credential_A , B will get credential B. If your third party only allow single credential then A will get an error from api call.
So the approach would be to forward the credential related task to one module, which manages the credential. background task or on request ( get token it expires on request) will face the same race problem. since node doesn't have context of thread, it is simple.
let credential = {}
let isUpdating = false;
const _updateCrediental = (newCrediential){
//map here
}
const _getCredential = async()=> {
try{
if(!updating){
updating = true;
const newCrediential = await apiCall();
updateCrediential(newCrediential);
updating = false;
return credential;
}else{
return false;
}
}catch(err){
throw err;
}
}
export.getCredential = ()=>{
if(credentialIsValid()){
return credential;
}
return __getCredential();
}
/// check the return if it promise type then waaait for it if its false then wait for certain time and check again.
An improvement to this would be using event to instead of using timeout.
I myself would prefer work with database as well as you might want to log credential generation as well. Most database promise certain kind of transaction or locking. (feel safer)

Socket handle leak happening on Azure Web App

I have been stuck in one issue. I am getting error of "Unable to connect to the remote server. An attempt was made to access a socket in a way forbidden by its access permissions".
After searching on web and taking help of azure support, I came to know that if Web App reaches outbound connection limit of azure web app instance, it refuses connections or kill extra connections. Here is the image of open socket handles
My application calls third party WebAPI and wcf service. I have written code to close connections after making call to APIs. but it doesn't work for me. I did following code to call Web API.
var request = (HttpWebRequest)WebRequest.Create("www.xyz.com");
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.KeepAlive = false;
request.Headers.Add("Authorization", "Handshake");
byte[] bodyData;
bodyData = Encoding.UTF8.GetBytes(input_data);
request.ContentLength = bodyData.Length;
request.GetRequestStream().Write(bodyData, 0, bodyData.Length);
request.GetRequestStream().Flush();
request.GetRequestStream().Close();
using (var response = request.GetResponse())
{
using (var reader = new StreamReader(response.GetResponseStream(),
Encoding.UTF8))
{
string output_data = reader.ReadToEnd();
}
response.Close();
}
Could anyone guide me how to get rid on this issue?

Bot responses with lag after certain time of non-use

I have a permanent problem which I couldn't solve.
Seems my dll was unloaded from the memory after a while.
For example, when I do not use bot during 30 minutes, and then reuse, it takes some time before responding, like it was re-deployed or dll was no longer in the RAM and it was reloaded before response.
Is there a way to fix the lag when bot is not used for a while?
update :3/1/17
To activate the option ' Always on ', it is necessary to pass on a paying offer.
As I am still there dev, I cannot pass on this offer.
I found a solution to keep my Bot in alive mode by creating a script that will send messages every minute.
My script is in c# and when I execute this, I obtain an error of authorisation.
var authValue = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{MyMicrosoftAppId}:{MyMicrosoftAppPassword}")));
var client = new HttpClient()
{
DefaultRequestHeaders = { Authorization = authValue }
};
var jsonObject = new Rootobject()
{
type = "message",
id = "0a24ca1428074419a1679b37e0e3dd39",
timestamp = DateTime.Now,
serviceUrl = "http://localhost:9000/",
channelId = "emulator",
from = new From()
{
id = "2c1c7fa3",
name = "User1"
},
conversation = new Conversation()
{
isGroup = false,
id = "8a684db8",
name = "Conv1"
},
recipient = new Recipient()
{
id = "56800324",
name = "Bot1"
},
text = "ping",
attachments = new object[0],
entities = new object[0]
};
var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");
var result = client.PostAsync("http://emiko.azurewebsites.net/api/messages", content).Result;
result.ToString();
But I get an authorization error:
StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.StreamContent...
How can I login correctly on the Bot and send a message on its Url
(http://emiko.azurewebsites.net/api/messages)
As mentioned in the official document about Always On:
By default, web apps are unloaded if they are idle for some period of time. This lets the system conserve resources. In Basic or Standard mode, you can enable Always On to keep the app loaded all the time. If your app runs continuous web jobs, you should enable Always On, or the web jobs may not run reliably.
To activate the option ' Always on ', it is necessary to pass on a paying offer. As I am still there dev, I cannot pass on this offer.
Based on your scenario, I assumed that you could periodically send requests to make sure your site is alive, here are some tutorials you could refer to them (tutorial1 and tutorial2).

File uploading from web application to Sharepoint server

This is my first time ever with Sharepoint. Here is the scenario
I have a stand alone web application
I also have a stand alone sharepoint server.
Both are on different servers.
I need to upload a file from web application to sharepoint
I found 2 methods online,
Using the webservice provided by Sharepoint (CopyIntoItems)
Using jQuery library of Sharepoint webservice
After searching the web, I think the jQuery part will not work (you can correct me).
I am looking for a method that takes username/password and uploads a pdf file to Sharepoint server. The following is my C# code that tries to upload but ends up in error
public bool UploadFile(string file, string destination)
{
bool success = false;
CopySoapClient client = new CopySoapClient();
if (client.ClientCredentials != null)
client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
try
{
client.Open();
string filename = Path.GetFileName(file);
string destinationUrl = destination + filename;
string[] destinationUrls = { destinationUrl };
FieldInformation i1 = new FieldInformation { DisplayName = "Title", InternalName = "Title", Type = FieldType.Text, Value = filename };
FieldInformation[] info = { i1 };
CopyResult[] result;
byte[] data = File.ReadAllBytes(file);
//uint ret = client.CopyIntoItems(filename, destinationUrls, info, data, out result);
uint ret = client.CopyIntoItems(file, destinationUrls, info, data, out result);
if (result != null && result.Length > 0 && result[0].ErrorCode == 0)
success = true;
}
finally
{
if (client.State == System.ServiceModel.CommunicationState.Faulted)
client.Abort();
if (client.State != System.ServiceModel.CommunicationState.Closed)
client.Close();
}
return success;
}
I am calling the above function like this
UploadFile(#"C:\temp\uploadFile.txt", "http://spf-03:300/demo/Dokumente").ToString();
Error that i get:
Error Code: Destination Invalid
Error Message: The service method 'Copy' must be called on the same domain that contains the target URL.
There is the 3rd option with SharePoint 2010 and that is to use the Client Side object model. The client side object model a a sub set of the larger Sharepoint API, but it does cover uploading documents. Below is blog post with an example of uploading.
Upload document through client object model
As with most things in SharePoint you will need to authenticate against it the site, so find out if your site collection is forms based or claims based and then you should be able to find sample code for your situation.
Solution to the problem:
The problem was that the "security token webservice" was not working and it was giving some error when we manually ran the webservice.
The server was unable to process the request due to an internal error.
For more information about the error, either turn on
IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute
or from the configuration behavior) on the server in order to send the
exception information back to the client, or turn on tracing as per
the Microsoft .NET Framework 3.0 SDK documentation and inspect the
server trace logs.
The above exception is a generic one. To view the exact exception we enabled remote error viewing from the web.config file of the webservice(link) and saw the exact exception.
We found the solution for the exception and the service started. After that everything was working fine.

SharePoint 2010 Client Object Model - Kerberos/Claims Authentication

I'm trying to read a value from a list in a remote SharePoint site (different SP Web App). The web apps are set up with Claims Auth, and the client web app SP Managed account is configured with an SPN. I believe Kerberos and claims are set up correctly, but I am unable to reach the remote server, and the request causes an exception: "The remote server returned an error: (401) Unauthorized."
The exception occurs in the line ctx.ExecuteQuery(); but it does not catch the exception in the if (scope.HasException) instead, the exception is caught by the calling code (outside of the using{} block).
When I look at the traffic at the remote server using Wireshark, it doesn't look like the request is even getting to the server; it's almost as if the 401 occurs before the Kerberos ticket is exchanged for the claim.
Here's my code:
using (ClientContext ctx = new ClientContext(contextUrl))
{
CredentialCache cc = new CredentialCache();
cc.Add(new Uri(contextUrl), "Kerberos", CredentialCache.DefaultNetworkCredentials);
ctx.Credentials = cc;
ctx.AuthenticationMode = ClientAuthenticationMode.Default;
ExceptionHandlingScope scope = new ExceptionHandlingScope(ctx);
Web ctxWeb = ctx.Web;
List ctxList;
Microsoft.SharePoint.Client.ListItemCollection listItems;
using (scope.StartScope())
{
using (scope.StartTry())
{
ctxList = ctxWeb.Lists.GetByTitle("Reusable Content");
CamlQuery qry = new CamlQuery();
qry.ViewXml = string.Format(ViewQueryByField, "Title", "Text", SharedContentTitle);
listItems = ctxList.GetItems(qry);
ctx.Load(listItems, items => items.Include(
item => item["Title"],
item => item["ReusableHtml"],
item => item["ReusableText"]));
}
using (scope.StartCatch()) { }
using (scope.StartFinally()) { }
}
ctx.ExecuteQuery();
if (scope.HasException)
{
result = string.Format("Error retrieving content<!-- Error Message: {0} | {1} -->", scope.ErrorMessage, contextUrl);
}
if (listItems.Count == 1)
{
Microsoft.SharePoint.Client.ListItem contentItem = listItems[0];
if (SelectedType == SharedContentType.Html)
{
result = contentItem["ReusableHtml"].ToString();
}
else if (SelectedType == SharedContentType.Text)
{
result = contentItem["ReusableText"].ToString();
}
}
}
I realize the part with the CredentialCache shouldn't be necessary in claims, but every single example I can find is either running in a console app, or in a client side application of some kind; this code is running in the codebehind of a regular ASP.NET UserControl.
Edit: I should probably mention, the code above doesn't even work when the remote URL is the root site collection on the same web app as the calling code (which is in a site collection under /sites/)--in other words, even when the hostname is the same as the calling code.
Any suggestions of what to try next are greatly appreciated!
Mike
Is there a reason why you are not using the standard OM?
You already said this is running in a web part, which means it is in the context of application pool account. Unless you elevate permissions by switching users, it won't authenticate correctly. Maybe try that. But I would not use the client OM when you do have access to the API already.

Resources