How to speed up slow GetResponse and GetRequest? - c#

I have a web site in ASP.NET C# that is running pretty slow in one spot. When I used a profiler to examine it the two spots of slowness are GetResponse and GetRequest. I am connecting to a 3rd party system to get availability on a calendar so I'm communicating with them through a POST. We have a counterpart that is in ASP classic and it seems faster but I have no easy way to profile it.
I am sending an XML string byte-encoded that's around 440 bytes, depending on a few things and the return is under 2k. I'm including most of the relevant routines here. I tried setting the default Proxy to GetEmptyWebProxy in case that would help as I read it could.
It is taking 2 to 5 seconds to process the request/response. The pressure is on because the boss is counting hippopotamuses and I can't get real stats on the Classic version so it's all perception at this point.
My question is this - Am I going about as fast as I can and it's the nature of the beast or is there something I can do to speed up this communications? Any help greatly appreciated.
Here's some code:
protected void UpdateInventory(DateTime _SelectedDate)
{
// build request object
Envelope _Request = new Envelope();
_Request.Body = new Body();
_Request.Body.QueryEvents = new QueryEvents();
_Request.Header = new Header();
//setup search criteria for API request
_Request.Body.QueryEvents.EventRangeBeginDate = _SelectedDate.ToString(_DTFormat);
_Request.Body.QueryEvents.EventRangeEndDate = _SelectedDate.AddDays(1).ToString(_DTFormat);
_Request.Body.QueryEvents.EventTypeID = _EventTypeId;
_Request.Header.SourceID = "BackOffice";
_Request.Header.MessageID = 0;
_Request.Header.MessageType = "QueryEvents";
_Request.Header.TimeStamp = LocaleHelper.LocalNow.ToString(_DTFormat);
_Request.Header.EchoData = "BO Calendar";
// send API request
HttpStatusCode _Status = GetGatewayInventory(_Request);
}
protected HttpStatusCode GetGatewayInventory(Envelope _InvRequest)
{
// set up return value
HttpStatusCode _RetVal = HttpStatusCode.Unused;
// initialize global inventory object
_Inventory = new Envelope();
// serialize request object into XML
XmlSerializer _Serializer = new XmlSerializer(_InvRequest.GetType());
MemoryStream _Stream = new MemoryStream();
_Serializer.Serialize(_Stream, _InvRequest);
XmlDocument _Doc = new XmlDocument();
_Stream.Position = 0;
_Doc.Load(_Stream);
// remove unneeded info.
XmlNode _Node = _Doc.SelectSingleNode("/Envelope");
XmlElement _ENode = (XmlElement)_Node;
_ENode.RemoveAttribute("xmlns:xsi");
_ENode.RemoveAttribute("xmlns:xsd");
// clean up
string _XmlStr = _Doc.InnerXml.Replace("\"1.0\"", "'1.0'");
byte[] _Bytes = System.Text.Encoding.ASCII.GetBytes(_XmlStr);
// send string to gateway
// set web request
string _GWHost = _GatewayHostLive;
HttpWebRequest _req = (HttpWebRequest)WebRequest.Create(_GWHost);
IWebProxy myProxy = GlobalProxySelection.GetEmptyWebProxy();
GlobalProxySelection.Select = myProxy;
_req.Proxy = myProxy;
_req.Method = "POST";
_req.ContentLength = _Bytes.Length;
_req.ContentType = "text/xml; encoding='utf-8'";
Stream _RequestStream = _req.GetRequestStream();
_RequestStream.Write(_Bytes, 0, _Bytes.Length);
_RequestStream.Close();
using (HttpWebResponse _Resp = (HttpWebResponse)_req.GetResponse())
{
_RetVal = _Resp.StatusCode;
if (_Resp.StatusCode == HttpStatusCode.OK)
{
Stream _respStream = _Resp.GetResponseStream();
XmlTextReader _xmlreader = new XmlTextReader(_respStream);
XmlDocument _RespXml = new XmlDocument();
_RespXml.Load(_xmlreader);
_xmlreader.Close();
// deserialize back into object
StringReader _sr = new StringReader(_RespXml.InnerXml);
XmlSerializer _XmlSr = new XmlSerializer(_Inventory.GetType());
XmlReader _Inreader = new XmlTextReader(_sr);
_Inventory = (Envelope)_XmlSr.Deserialize(_Inreader);
StripExtrasIfOnlyShowFirstAvailable(_Inventory, CountTotalTickets());
grd_EventTimes.DataSource = _Inventory.Body.Events.Event;
grd_EventTimes.DataBind();
grd_EventTimes.Visible = true;
if (_Inventory.Body.Events.Event.Count > 0)
lbl_GatewayId.Text = "GN: " + _Inventory.Body.Events.Event[0].EventName + " ID:" + _EventTypeId ;
}
_Resp.Close();
}
// exit and return value
return _RetVal;
}

You can try to disable the Nagle algorithm via ServicePointManager.UseNagleAlgorithm = false, see MSDN.
The (bad) effects of Nagle can add at least half a second of extra delay to your POST request. It mixes badly with small POST requests since those do two writes to the underlying TCP stream before waiting for (reading) the response. See details in the Nagle Wikipedia link.

Related

Call All Operations In ASMX Programmatically

Assume there exists a generic string collection that contains a variety of web service URLs:
List<string> webServiceCollection = new List<string>();
webServiceCollection.Add("http://<site>/_vti_bin/Lists.asmx");
webServiceCollection.Add("http://<site>/_vti_bin/Sites.asmx");
There a variety of methods, with a variety of input and return types in each web service. Is it possible to read the all the available operations and attempt the operation with default values? For this purpose, I am trying to write a security tool that will help test whether the web service allows the user to interact with them under a variety of accounts as an academic exercise.
You can do something like this:
PSEUDO CODE
System.Net.WebClient client = new System.Net.WebClient();
string strUrl = #"http://localhost:xxxxx/Service.asmx?wsdl";
System.IO.Stream stream = client.OpenRead(strUrl);
string serviceName = "Service";
// Get a WSDL file describing a service.
ServiceDescription description = ServiceDescription.Read(stream);
ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
importer.ProtocolName = "Soap12"; // Use SOAP 1.2.
importer.AddServiceDescription(description, null, null);
// Generate a proxy client.
importer.Style = ServiceDescriptionImportStyle.Client;
// Generate properties to represent primitive values.
importer.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateProperties;
// Initialize a Code-DOM tree into which we will import the service.
CodeNamespace nmspace = new CodeNamespace();
CodeCompileUnit unit1 = new CodeCompileUnit();
unit1.Namespaces.Add(nmspace);
// Import the service into the Code-DOM tree. This creates proxy code
// that uses the service.
ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit1);
if (warning == 0)
{
// Generate and print the proxy code in C#.
CodeDomProvider provider1 = CodeDomProvider.CreateProvider("CSharp");
// Compile the assembly with the appropriate references
string[] assemblyReferences = new string[2] { "System.Web.Services.dll", "System.Xml.dll" };
CompilerParameters parms = new CompilerParameters(assemblyReferences);
CompilerResults results = provider1.CompileAssemblyFromDom(parms, unit1);
foreach (CompilerError oops in results.Errors)
{
Console.WriteLine("========Compiler error============");
Console.WriteLine(oops.ErrorText);
}
//Invoke the web service method
foreach (PortType portType in description.PortTypes)
{
foreach (Operation operation in portType.Operations)
{
try
{
object o = results.CompiledAssembly.CreateInstance(serviceName);
Type t = o.GetType();
Console.WriteLine(t.InvokeMember(operation.Name, System.Reflection.BindingFlags.InvokeMethod, null, o, null));
}catch(Exception ex){
Console.WriteLine(ex.Message);
}
}
}
}
else
{
// Print an error message.
Console.WriteLine("Warning: " + warning);
}
this will be your best attempt to try and achieve your goal:
string completeUrl ="http://localhost/testwebservice";
// Create a request for the URL.
WebRequest request = WebRequest.Create(completeUrl);
// If required by the server, set the credentials.
request.Credentials = CredentialCache.DefaultCredentials;
// If you have a proxy configured.
WebProxy proxyObject = new WebProxy("http://proxy.com/", true);
request.Proxy = proxyObject;
//Get the response.
using(HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
// Get the stream containing content returned by the server.
using(Stream dataStream = response.GetResponseStream())
{
// Open the stream using a StreamReader for easy access.
using(StreamReader reader = new StreamReader(dataStream))
{
// Read the content.
string responseFromServer = reader.ReadToEnd();
}
}
}
and in order to add your try you should do:
List<string> webServiceCollection = new List<string>();
webServiceCollection.Add("http://<site>/_vti_bin/Lists.asmx");
webServiceCollection.Add("http://<site>/_vti_bin/Sites.asmx");
foreach(var reqeust in webServiceCollection)
{
string completeUrl = $"{request}";;
// Create a request for the URL.
WebRequest request = WebRequest.Create(completeUrl);
// If required by the server, set the credentials.
request.Credentials = CredentialCache.DefaultCredentials;
// If you have a proxy configured.
WebProxy proxyObject = new WebProxy("http://proxy.com/", true);
request.Proxy = proxyObject;
//Get the response.
using(HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
// Get the stream containing content returned by the server.
using(Stream dataStream = response.GetResponseStream())
{
// Open the stream using a StreamReader for easy access.
using(StreamReader reader = new StreamReader(dataStream))
{
// Read the content.
string responseFromServer = reader.ReadToEnd();
}
}
}
}

Can't create workitem via webrequest in RTC

I'm trying to create a .NET web application integration with RTC, where I would insert new workitems using RTC change management services, as defined in this article (specifically in "Create a Change Request"). I was able to get the URL-to-be-used inside services.xml file (/oslc/contexts/_0_iN4G09EeGGMqpyZT5XdQ/workitems/) and my goal is to insert data using JSON.
My code is basically the following:
CookieContainer cookies = new CookieContainer();
HttpWebRequest documentPost = (HttpWebRequest)WebRequest.Create(rtcServerUrl + "/oslc/contexts/_0_iN4G09EeGGMqpyZT5XdQ/workitems/order");//"Order" is the workitem name
documentPost.Method = "POST";
documentPost.CookieContainer = cookies;
documentPost.Accept = "application/json";
documentPost.ContentType = "application/x-oslc-cm-change-request+json";
documentPost.Timeout = TIMEOUT_SERVICO;
string json = "{ \"dc:title\":\"" + title + "\", \"rtc_cm:filedAgainst\": [ { \"rdf:resource\" : \"" + rtcServerUrl + "/resource/itemOid/com.ibm.team.workitem.Category/" + idCategory + "\"} ] }"; //dc:title and rtc_cm:filedAgainst are the only two mandatory data from the workitem I'm trying to create
using (var writer = new StreamWriter(documentPost.GetRequestStream()))
{
writer.Write(json);
writer.Flush();
writer.Close();
}
Encoding encode = System.Text.Encoding.UTF8;
string retorno = null;
//Login
HttpWebRequest formPost = (HttpWebRequest)WebRequest.Create(rtcServerUrl + "/j_security_check");
formPost.Method = "POST";
formPost.Timeout = TIMEOUT_REQUEST;
formPost.CookieContainer = request.CookieContainer;
formPost.Accept = "text/xml";
formPost.ContentType = "application/x-www-form-urlencoded";
String authString = "j_username=" + userName + "&j_password=" + password; //create authentication string
Byte[] outBuffer = System.Text.Encoding.UTF8.GetBytes(authString); //store in byte buffer
formPost.ContentLength = outBuffer.Length;
System.IO.Stream str = formPost.GetRequestStream();
str.Write(outBuffer, 0, outBuffer.Length); //update form
str.Close();
//FormBasedAuth Step2:submit the login form and get the response from the server
HttpWebResponse formResponse = (HttpWebResponse)formPost.GetResponse();
var rtcAuthHeader = formResponse.Headers["X-com-ibm-team-repository-web- auth-msg"];
//check if authentication has failed
if ((rtcAuthHeader != null) && rtcAuthHeader.Equals("authfailed"))
{
//authentication failed. You can write code to handle the authentication failure.
//if (DEBUG) Console.WriteLine("Authentication Failure");
}
else
{
//login successful
HttpWebResponse responseRetorno = (HttpWebResponse)request.GetResponse();
if (responseRetorno.StatusCode != HttpStatusCode.OK)
retorno = responseRetorno.StatusDescription;
else
{
StreamReader reader = new StreamReader(responseRetorno.GetResponseStream());
retorno = "[Response] " + reader.ReadToEnd();
}
responseRetorno.Close();
formResponse.GetResponseStream().Flush();
formResponse.Close();
}
As I was managed to search for in other forums, this should be enough in order to create the workitem (I have a very similar code working to update workitems using "" URL and PUT method). However, instead of create the workitem in RTC and give me some response with item's identifier, the request's response returns a huge JSON file that ends with "oslc_cm:next":"https:///oslc/contexts/_0_iN4G09EeGGMqpyZT5XdQ/workitems/%7B0%7D?oslc_cm.pageSize=50&_resultToken=_AAY50FEkEee1V4u7RUQSjA&_startIndex=50. It's a JSON representation of the XML I receive when I access /oslc/contexts/_0_iN4G09EeGGMqpyZT5XdQ/workitems/ directly from browser, like I was trying to do a simple query inside the workitem's collection (even though I'm using POST, not GET).
I also tried to use PUT method, but then I receive a 405 status code.
Does anyone have an idea of what am I missing here? My approach is wrong, even though with the same approach I'm able to update existing workitem data in RTC?
Thanks in advance.

Updating Hbase row with Thrift and Hbase.Thrift

I am trying to update a row in Hbase from a MVC site. I am using .NET 4.5 and the best solution I have found to do this is using the Thrift package and Hbase.Thrift to do this.
I have :
private static Hbase.Client _hbase; //this is actually a class header
var socket = new TSocket("localhost",9090);
var transport = new TBufferedTransport(socket);
var proto = new TBinaryProtocol(transport);
_hbase = new Hbase.Client(proto);
try
{
transport.Open();
_hbase.mutateRows(Encoding.UTF8.GetBytes("Images"), new List<BatchMutation>()
{
new BatchMutation()
{
Row = Guid.NewGuid().ToByteArray(),
Mutations = new List<Mutation> {
new Mutation{Column = Encoding.UTF8.GetBytes("Image"), IsDelete = false, Value = Encoding.UTF8.GetBytes(testImage) }
}
}
});
transport.Close();
}
While this creates a new image in a given column I have not found a way for it to overwrite and existing column. Has anyone successfully pulled this off or is there another client tool I should be using to achieve what I am going for?
I was unable to get this setup to work so I went on to use Hbase Rest. Here is the code for the future lost souls looking for the solution as I was.
System.Diagnostics.Debug.WriteLine("Hbase_update");
//check your port number to make sure you have the right one
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(#"http://localhost:8080/Images/" + key + "?data=");
request.Method = "PUT";
//NOTE:The name of the column I am pushing to is int the Column Family "Image" and column "Binary"
string requestStr = #"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?><CellSet><Row key=""" + Convert.ToBase64String(Encoding.UTF8.GetBytes(key)) + #"""><Cell column=""" + Convert.ToBase64String(Encoding.UTF8.GetBytes("Image:Binary")) + #""">"+ imageBinary + #"</Cell></Row></CellSet>";
var data = Encoding.ASCII.GetBytes(requestStr);
request.Accept = "text/xml";
request.ContentType = "text/xml; encoding='utf-8'";
request.ContentLength = data.Length;
Stream dataStream = request.GetRequestStream();
dataStream.Write(data, 0, data.Length);
dataStream.Close();
try
{
var response = (HttpWebResponse)request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
System.Diagnostics.Debug.WriteLine(responseString);
}
catch (System.Net.WebException e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
}

Can I create dynamic web service client reference without adding its reference to service references of the project?

I need to create dynamic references of multiple web services and sent some value with it.
To be truly dynamic, you have to do three things:
1) Get the service description (wsdl) from the web service
2) Generate the proxy code dynamically from the service description
3) Compile the code and expose it in your application - usually through reflection or some sort of dynamic scripting interface.
The code snippet below is from some experimenting I did a long time ago. It is not production code and won't compile, but should give you a head start if this is the direction you want to go.
It does not include step (3). The code generated can be compiled with classes provided in the System.CodeDom.Compiler namespace.
Uri uri = new Uri(_Url + "?wsdl");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.AllowAutoRedirect = true;
request.PreAuthenticate = false;
if (_User.Length > 0)
{
request.UseDefaultCredentials = false;
request.Credentials = new NetworkCredential(_User, _Password, _Domain);
}
WebResponse response = null;
try
{
response = request.GetResponse();
}
catch (System.Net.WebException wex)
{
response = wex.Response;
}
catch (Exception ex)
{
}
Stream requestStream = response.GetResponseStream();
ServiceDescription sd = ServiceDescription.Read(requestStream);
_ReferenceName = _Namespace + "." + sd.Services[0].Name;
ServiceDescriptionImporter Importer = new ServiceDescriptionImporter();
Importer.AddServiceDescription(sd, string.Empty, string.Empty);
Importer.ProtocolName = "Soap12";
Importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties;
CodeNamespace nameSpace = new CodeNamespace(_Namespace);
CodeCompileUnit ccu = new CodeCompileUnit();
ccu.Namespaces.Add(nameSpace);
ServiceDescriptionImportWarnings warnings = Importer.Import(nameSpace, ccu);
if (warnings == 0)
{
StringWriter sw = new StringWriter(System.Globalization.CultureInfo.CurrentCulture);
Microsoft.CSharp.CSharpCodeProvider prov = new Microsoft.CSharp.CSharpCodeProvider();
CodeGeneratorOptions options = new CodeGeneratorOptions();
options.BlankLinesBetweenMembers = false;
options.BracingStyle = "C";
prov.GenerateCodeFromNamespace(nameSpace, sw, options);
_ProxySource = sw.ToString();
sw.Close();
}
I don't have the code right now but I know this can be done, in fact in a former company I have worked we did have a generic web service client developed in house.
have a lock here:
Generic Web Service Proxy

C# - HttpWebRequest - POST

I am trying to make an Http POST to an Apache web server.
I am finding that setting ContentLength seems to be required for the request to work.
I would rather create an XmlWriter directly from GetRequestStream() and set SendChunked to true, but the request hangs indefinitely when doing so.
Here is how my request is created:
private HttpWebRequest MakeRequest(string url, string method)
{
HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;
request.Method = method;
request.Timeout = Timeout; //Property in my class, assume it's 10000
request.ContentType = "text/xml"; //I am only writing xml with XmlWriter
if (method != WebRequestMethods.Http.Get)
{
request.SendChunked = true;
}
return request;
}
How can I make SendChunked work so I do not have to set ContentLength? I do not see a reason to store the XmlWriter's string somewhere before sending it to the server.
EDIT: Here is my code causing the problem:
using (Stream stream = webRequest.GetRequestStream())
{
using (XmlWriter writer = XmlWriter.Create(stream, XmlTags.Settings))
{
Generator.WriteXml<TRequest>(request, writer);
}
}
Before I did not have a using on the Stream object returned from GetRequestStream(), I assumed XmlWriter closed the stream when disposed, but this is not the case.
One of the answers below, let me to this. I'll mark them as the answer.
As far as HttpWebRequest is concerned, my original code works just fine.
This should work the way you have it written. Can we see the code that actually does the uploading? Are you remembering to close the stream?
Looking at the example at http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.sendchunked.aspx they still set a content length. Really the bottom line is that if you are sending data you need to tell the receiver how much data you will be sending. Why don't you know how much data you are sending before you send the request?
ContentLength:
Property Value
Type: System..::.Int64
The number of bytes of data to send to the Internet resource. The default is -1, which indicates the property has not been set and that there is no request data to send.
Edit for Aaron (I was wrong):
HttpWebRequest httpWebRequest = HttpWebRequest.Create("http://test") as HttpWebRequest;
httpWebRequest.SendChunked = true;
MessageBox.Show("|" + httpWebRequest.TransferEncoding + "|");
From System.Net.HttpWebRequest.SerializeHeaders():
if (this.HttpWriteMode == HttpWriteMode.Chunked)
{
this._HttpRequestHeaders.AddInternal("Transfer-Encoding", "chunked");
}
else if (this.ContentLength >= 0L)
{
this._HttpRequestHeaders.ChangeInternal("Content-Length", this._ContentLength.ToString(NumberFormatInfo.InvariantInfo));
}
I prefer to use a generic method to comply this kind of stuff. Take a look at the XML sender request below. It will serialize your XML and then send it with the appropriate ContentType :
public bool SendXMLRequest<T>(T entity, string url, string method)
{
HttpWebResponse response = null;
bool received = false;
try
{
var request = (HttpWebRequest)WebRequest.Create(url);
var credCache = new CredentialCache();
var netCred = new NetworkCredential(YOUR_LOGIN_HERE, YOUR_PASSWORD_HERE, YOUR_OPTIONAL_DOMAIN_HERE);
credCache.Add(new Uri(url), "Basic", netCred);
request.Credentials = credCache;
request.Method = method;
request.ContentType = "application/xml";
request.SendChunked = "GET" != method.ToUpper();
using (var writer = new StreamWriter(request.GetRequestStream()))
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (StringWriter textWriter = new Utf8StringWriter())
{
serializer.Serialize(textWriter, entity);
var xml = textWriter.ToString();
writer.Write(xml);
}
}
response = (HttpWebResponse)request.GetResponse();
received = response.StatusCode == HttpStatusCode.OK; //YOu can change the status code to check. May be created, etc...
}
catch (Exception ex) { }
finally
{
if(response != null)
response.Close();
}
return received;
}

Categories

Resources