WebExceptionStatus status;
WebConnectionGroup group;
bool busy;
- ArrayList queue;
WaitOrTimerCallback initConn;
- internal ManualResetEvent dataAvailable;
bool keepAlive;
bool aborted;
byte [] buffer;
- internal static AsyncCallback readDoneDelegate = new AsyncCallback (ReadDone);
+ static AsyncCallback readDoneDelegate = new AsyncCallback (ReadDone);
EventHandler abortHandler;
ReadState readState;
internal WebConnectionData Data;
bool chunkedRead;
ChunkStream chunkStream;
AutoResetEvent waitForContinue;
+ AutoResetEvent goAhead;
bool waitingForContinue;
+ int queued;
public WebConnection (WebConnectionGroup group, ServicePoint sPoint)
{
this.group = group;
this.sPoint = sPoint;
- queue = new ArrayList (1);
- dataAvailable = new ManualResetEvent (true);
buffer = new byte [4096];
readState = ReadState.None;
Data = new WebConnectionData ();
initConn = new WaitOrTimerCallback (InitConnection);
abortHandler = new EventHandler (Abort);
+ goAhead = new AutoResetEvent (true);
}
public void Connect ()
if(hostEntry == null) {
status = sPoint.UsesProxy ? WebExceptionStatus.ProxyNameResolutionFailure :
WebExceptionStatus.NameResolutionFailure;
+ socket.Close();
+ socket = null;
} else {
foreach(IPAddress address in hostEntry.AddressList) {
socket = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
break;
} catch (SocketException) {
socket.Close();
+ socket = null;
status = WebExceptionStatus.ConnectFailure;
}
}
{
status = st;
Close ();
+ lock (this) {
+ busy = false;
+ if (st == WebExceptionStatus.RequestCanceled)
+ Data.Init ();
+
+ status = st;
+ }
+
if (e == null) { // At least we now where it comes from
try {
throw new Exception ();
if (Data != null && Data.request != null)
Data.request.SetResponseError (st, e);
+
+ goAhead.Set ();
}
internal bool WaitForContinue (byte [] headers, int offset, int size)
WebConnection cnc = (WebConnection) result.AsyncState;
WebConnectionData data = cnc.Data;
NetworkStream ns = cnc.nstream;
- if (ns == null)
+ if (ns == null) {
+ cnc.busy = false;
+ cnc.goAhead.Set ();
return;
+ }
int nread = -1;
- cnc.dataAvailable.Reset ();
try {
nread = ns.EndRead (result);
} catch (Exception e) {
cnc.status = WebExceptionStatus.ReceiveFailure;
cnc.HandleError (cnc.status, e);
- cnc.dataAvailable.Set ();
return;
}
if (nread == 0) {
- Console.WriteLine ("nread == 0: may be the connection was closed?");
- data.request.SetResponseData (data);
- cnc.Close ();
- cnc.dataAvailable.Set ();
+ cnc.status = WebExceptionStatus.ReceiveFailure;
+ cnc.HandleError (cnc.status, null);
return;
}
if (nread < 0) {
cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, null);
- cnc.dataAvailable.Set ();
return;
}
if (pos == -1 || exc != null) {
cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, exc);
- cnc.dataAvailable.Set ();
return;
}
}
if (cnc.readState != ReadState.Content) {
cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, null);
- cnc.dataAvailable.Set ();
return;
}
cnc.chunkStream.Write (cnc.buffer, pos, nread);
}
- cnc.prevStream = stream;
+ int more = Interlocked.Decrement (ref cnc.queued);
+
+ if (more > 0)
+ stream.ReadAll ();
+
data.stream = stream;
- data.request.SetResponseData (data);
stream.CheckComplete ();
+ data.request.SetResponseData (data);
+ lock (cnc) {
+ cnc.prevStream = stream;
+ }
}
static void InitRead (object state)
ns.BeginRead (cnc.buffer, 0, cnc.buffer.Length, readDoneDelegate, cnc);
} catch (Exception e) {
cnc.HandleError (WebExceptionStatus.ReceiveFailure, e);
- cnc.dataAvailable.Set ();
}
}
void InitConnection (object state, bool notUsed)
{
HttpWebRequest request = (HttpWebRequest) state;
- if (aborted) {
- status = WebExceptionStatus.RequestCanceled;
- request.SetWriteStreamError (status);
+
+ // Just in case 2 requests are released
+ bool relaunch = false;
+ lock (this) {
+ relaunch = busy;
+ busy = true;
+ }
+
+ if (relaunch) {
+ SendRequest (request);
+ return;
+ }
+ //
+
+ if (status == WebExceptionStatus.RequestCanceled) {
+ busy = false;
+ Data.Init ();
+ goAhead.Set ();
+ aborted = false;
return;
}
+ keepAlive = request.KeepAlive;
+ Data.Init ();
+ Data.request = request;
+
Connect ();
if (status != WebExceptionStatus.Success) {
+ busy = false;
request.SetWriteStreamError (status);
Close ();
+ goAhead.Set ();
return;
}
if (!CreateStream (request)) {
+ busy = false;
request.SetWriteStreamError (status);
Close ();
+ goAhead.Set ();
return;
}
InitRead (this);
}
- void BeginRequest (HttpWebRequest request)
- {
- lock (this) {
- keepAlive = request.KeepAlive;
- Data.Init ();
- Data.request = request;
- }
-
- ThreadPool.RegisterWaitForSingleObject (dataAvailable, initConn, request, -1, true);
- }
-
internal EventHandler SendRequest (HttpWebRequest request)
{
- Monitor.Enter (this);
+ lock (this) {
+ Interlocked.Increment (ref queued);
+ if (prevStream != null && socket != null && socket.Connected) {
+ prevStream.ReadAll ();
+ prevStream = null;
+ }
- if (prevStream != null && socket != null && socket.Connected) {
- prevStream.ReadAll ();
- prevStream = null;
- }
-
- if (!busy) {
- busy = true;
- Monitor.Exit (this);
- BeginRequest (request);
- } else {
- queue.Add (request);
- Monitor.Exit (this);
+ ThreadPool.RegisterWaitForSingleObject (goAhead, initConn, request, -1, true);
}
return abortHandler;
internal void NextRead ()
{
- Monitor.Enter (this);
- string header = (sPoint.UsesProxy) ? "Proxy-Connection" : "Connection";
- string cncHeader = (Data.Headers != null) ? Data.Headers [header] : null;
- bool keepAlive = this.keepAlive;
- if (cncHeader != null) {
- cncHeader = cncHeader.ToLower ();
- keepAlive = (keepAlive && cncHeader.IndexOf ("keep-alive") != -1);
- }
-
- if ((socket != null && !socket.Connected) ||
- (!keepAlive || (cncHeader != null && cncHeader.IndexOf ("close") != -1))) {
- Close ();
- }
+ lock (this) {
+ busy = false;
+ string header = (sPoint.UsesProxy) ? "Proxy-Connection" : "Connection";
+ string cncHeader = (Data.Headers != null) ? Data.Headers [header] : null;
+ bool keepAlive = this.keepAlive;
+ if (cncHeader != null) {
+ cncHeader = cncHeader.ToLower ();
+ keepAlive = (keepAlive && cncHeader.IndexOf ("keep-alive") != -1);
+ }
- busy = false;
- dataAvailable.Set ();
+ if ((socket != null && !socket.Connected) ||
+ (!keepAlive || (cncHeader != null && cncHeader.IndexOf ("close") != -1))) {
+ Close ();
+ }
- if (queue.Count > 0) {
- HttpWebRequest request = (HttpWebRequest) queue [0];
- queue.RemoveAt (0);
- Monitor.Exit (this);
- SendRequest (request);
- } else {
- Monitor.Exit (this);
+ goAhead.Set ();
}
}
HandleError (WebExceptionStatus.RequestCanceled, null);
}
+ internal bool Busy {
+ get { lock (this) return busy; }
+ }
+
~WebConnection ()
{
Close ();
// (C) 2003 Ximian, Inc (http://www.ximian.com)
//
+using System;
using System.Collections;
+using System.Configuration;
+using System.Net.Configuration;
using System.Net.Sockets;
namespace System.Net
ServicePoint sPoint;
string name;
ArrayList connections;
+ static ConnectionManagementData manager;
+ const string configKey = "system.net/connectionManagement";
+ int maxConnections;
+ Random rnd;
+
+ static WebConnectionGroup ()
+ {
+ manager = (ConnectionManagementData) ConfigurationSettings.GetConfig (configKey);
+ }
public WebConnectionGroup (ServicePoint sPoint, string name)
{
this.sPoint = sPoint;
this.name = name;
connections = new ArrayList (1);
+ maxConnections = (int) manager.GetMaxConnections (sPoint.Address.Host);
}
public WebConnection GetConnection (string name)
connections.RemoveAt ((int) removed [i]);
}
- //TODO: Should use the limits in the config file.
- if (connections.Count == 0) {
- cnc = new WebConnection (this, sPoint);
- connections.Add (new WeakReference (cnc));
- } else {
- cncRef = (WeakReference) connections [connections.Count - 1];
- cnc = cncRef.Target as WebConnection;
- }
+ cnc = CreateOrReuseConnection ();
}
return cnc;
}
+ WebConnection CreateOrReuseConnection ()
+ {
+ // lock is up there.
+ WebConnection cnc;
+ WeakReference cncRef;
+
+ int count = connections.Count;
+ if (maxConnections > count) {
+ cnc = new WebConnection (this, sPoint);
+ connections.Add (new WeakReference (cnc));
+ return cnc;
+ }
+
+ if (rnd == null)
+ rnd = new Random ();
+
+ foreach (WeakReference wr in connections) {
+ cnc = wr.Target as WebConnection;
+ if (cnc.Busy)
+ continue;
+
+ return cnc;
+ }
+
+ int idx = (count > 1) ? rnd.Next (0, count - 1) : 0;
+ cncRef = (WeakReference) connections [idx];
+ cnc = cncRef.Target as WebConnection;
+ if (cnc == null) {
+ cnc = new WebConnection (this, sPoint);
+ connections.RemoveAt (idx);
+ connections.Add (new WeakReference (cnc));
+ }
+ return cnc;
+ }
+
public string Name {
get { return name; }
}