/// <summary> /// Completes the reception of bytes from the remote endpoint. /// </summary> /// <param name="ar">The <see cref="IAsyncResult" /> instance returned by the initiating <see cref="BeginReceive" /> call.</param> /// <returns>The number of bytes received or zero if the remote endpoint has gracefully closed the connection (for TCP connections).</returns> /// <exception cref="SocketException">Thrown if the was an network error.</exception> /// <exception cref="SocketClosedException">Thrown if the socket has been closed.</exception> /// <remarks> /// <note> /// For TCP connections, the operation will be considered to have completed when some number of bytes /// between one and the specified <b>count</b> have been received from the remote endpoint /// or the remote endpoint has gracefully closed the connection. For UDP connections, the operation will /// be considered to be complete when the next packet is received, regardless of size. Note that the /// buffer size specified by <b>count</b> must be large enough to hold the next received /// UDP packet. /// </note> /// <note> /// All successful calls to <see cref="BeginReceive" /> must eventually be followed by a call to <see cref="EndReceive" />. /// </note> /// </remarks> public int EndReceive(IAsyncResult ar) { lock (syncLock) { try { return(sock.EndReceive(ar)); } finally { receivePending = false; } } }
private void OnRecvBuf(IAsyncResult ar) { try { syncCompletion = ar.CompletedSynchronously; cbTransfer = sock.EndReceive(ar); } catch (Exception e) { asyncException = e; } finally { asyncEvent.Set(); } }
/// <summary> /// Handles completion of the response reception. /// </summary> /// <param name="ar">The async result.</param> private void OnResponseReceived(IAsyncResult ar) { var httpAR = (HttpAsyncResult)ar.AsyncState; int cbRecv; using (TimedLock.Lock(this)) { try { // Pump the received data into the response parser, // signalling completion when we have the entire message. cbRecv = sock.EndReceive(ar); if (perfBytesRecv != null) { perfBytesRecv.IncrementBy(cbRecv); } if (httpAR.Response.Parse(httpAR.Buffer, cbRecv)) { httpAR.Response.EndParse(); httpAR.Notify(); return; } if (cbRecv == 0) { sock.ShutdownAndClose(); return; } // Continue receiving response data httpAR.Buffer = new byte[HttpStack.BlockSize]; sock.BeginReceive(httpAR.Buffer, 0, HttpStack.BlockSize, SocketFlags.None, onResponseRecv, httpAR); } catch (Exception e) { httpAR.Notify(e); } } }
/// <summary> /// Handles socket receive completions. /// </summary> /// <param name="ar"></param> private void OnReceive(IAsyncResult ar) { HttpAsyncState httpState = (HttpAsyncState)ar.AsyncState; HttpRequest request = httpState.Request; EnhancedSocket sock = httpState.Socket; byte[] recvBuf = httpState.Buffer; int cbRecv; HttpResponse response; bool closeCon; bool close; bool firstRequest; try { cbRecv = sock.EndReceive(ar); } catch { using (TimedLock.Lock(syncLock)) connections.Remove(sock); sock.Close(); return; } if (cbRecv == 0) { using (TimedLock.Lock(syncLock)) connections.Remove(sock); sock.ShutdownAndClose(); return; } if (perfBytesRecv != null) { perfBytesRecv.IncrementBy(cbRecv); } httpState.RecvSize += cbRecv; if (httpState.RecvSize > cbQueryMax) { // The request is too large so respond with a HttpStatus.RequestEntityTooLarge // and close the socket. response = new HttpResponse(HttpStatus.RequestEntityTooLarge); sock.AsyncSendClose(response.Serialize(SendBlockSize)); using (TimedLock.Lock(syncLock)) connections.Remove(sock); return; } if (!request.Parse(recvBuf, cbRecv)) { recvBuf = new byte[RecvBlockSize]; httpState.Buffer = recvBuf; sock.BeginReceive(recvBuf, 0, recvBuf.Length, SocketFlags.None, onRecv, httpState); return; } // We have a complete request so process it. request.EndParse(); firstRequest = httpState.FirstRequest; httpState.FirstRequest = false; try { sock.AppState = this; // Indicate that we're processing a request closeCon = false; for (int i = 0; i < modules.Length; i++) { response = modules[i].OnRequest(this, request, firstRequest, out close); closeCon = closeCon || close; if (response != null) { BlockArray blocks; // Make sure the response version is reasonable if (request.HttpVersion < response.HttpVersion) { response.HttpVersion = request.HttpVersion; } // Truncate any content data for HEAD requests if (request.Method == "HEAD") { response.Content = null; if (response["Content-Length"] != null) { response["Content-Length"] = "0"; } } blocks = response.Serialize(SendBlockSize); if (perfBytesSent != null) { perfBytesSent.IncrementBy(blocks.Size); } if (closeCon) { sock.AsyncSendClose(blocks); using (TimedLock.Lock(syncLock)) connections.Remove(sock); } else { sock.BeginSendAll(blocks, SocketFlags.None, onSend, httpState); } break; } } } finally { sock.AppState = null; // Indicate that we're done processing the request } }
private void OnReceive(IAsyncResult ar) { List <SipMessage> received = null; try { using (TimedLock.Lock(transport)) { int cb; int pos; int cbContents; SipHeader contentLength; byte[] packet; try { if (sock == null) { return; } if (contentBuf == null) { // We're reading a message envelope. // Read packets into headerBuf until we can find the CRLFCRLF // sequence marking the end of the message headers. cb = sock.EndReceive(ar); if (cb == 0) { // The socket has been closed on by the remote element. CloseAndRemove(); return; } cbRecv += cb; tryAgain: // Remove any leading CR or LF characters by shifting the // buffer contents. I know this isn't super efficient but // we'll probably never actually see packets with this // in the wild. for (pos = 0; pos < cbRecv; pos++) { if (headerBuf[pos] != 0x0D && headerBuf[pos] != 0x0A) { break; } } if (pos != 0) { if (pos == cbRecv) { // No data remaining in the buffer cbRecv = 0; sock.BeginReceive(headerBuf, 0, headerBuf.Length, SocketFlags.None, onRecv, null); return; } Array.Copy(headerBuf, pos, headerBuf, 0, headerBuf.Length - pos); cbRecv -= pos; } // Scan the message for the CRLFCRLF sequence terminating the // message envelope. pos = Helper.IndexOf(headerBuf, CRLFCRLF, 0, cbRecv); if (pos != -1) { // We've got the message envelope pos += 4; // Advance past the CRLFCRLF // Parse the message headers and then get the Content-Length header packet = Helper.Extract(headerBuf, 0, pos); try { message = SipMessage.Parse(packet, false); } catch (Exception e) { SipHelper.Trace(string.Format("TCP: UNPARSABLE message received from {0}: [{1}]", remoteEP, e.Message), Helper.FromUTF8(packet)); throw; } contentLength = message[SipHeader.ContentLength]; if (contentLength == null || !int.TryParse(contentLength.Text, out cbContents) || cbContents < 0) { var e = new SipException("Malformed SIP message: Invalid or missing [Content-Length] header from streaming transport."); e.Transport = "TCP"; e.BadPacket = packet; e.SourceEndpoint = remoteEP; throw e; } if (cbContents > MaxContentSize) { var e = new SipException("Invalid SIP message: [Content-Length={0}] exceeds [{1}].", cbContents, MaxContentSize); e.Transport = "TCP"; e.BadPacket = packet; e.SourceEndpoint = remoteEP; throw e; } if (pos + cbContents <= cbRecv) { // We already have the message contents, so extract the contents, // add them to the message, and then queue the message for delivery // once we leave the lock. message.Contents = Helper.Extract(headerBuf, pos, cbContents); if (received == null) { received = new List <SipMessage>(); } received.Add(message); message = null; // Shift any remaining data to the left in headerBuf, // adjust cbRecv, and the loop to look for another // message. pos += cbContents; cb = cbRecv - pos; // Bytes remaining in the buffer if (cb == 0) { // No more data left in the buffer cbRecv = 0; sock.BeginReceive(headerBuf, 0, headerBuf.Length, SocketFlags.None, onRecv, null); return; } Array.Copy(headerBuf, pos, headerBuf, 0, cb); cbRecv = cb; goto tryAgain; } // We don't have all of the message contents, so allocate a buffer for // the contents, copy what we have already into this buffer, and then // initiate a receive operation to read the remaining data. contentBuf = new byte[cbContents]; cbRecv = cbRecv - pos; // Content bytes remaining in the buffer Array.Copy(headerBuf, pos, contentBuf, 0, cbRecv); sock.BeginReceiveAll(contentBuf, cbRecv, cbContents - cbRecv, SocketFlags.None, onRecv, null); return; } // Throw an error if the header buffer is full and we still haven't // found the end of the envelope. if (cbRecv >= headerBuf.Length) { var e = new SipException("Malformed SIP message: Read [{0}] bytes and have not yet encountered end of headers.", headerBuf.Length); e.Transport = "TCP"; e.SourceEndpoint = remoteEP; throw e; } // Continue receiving header data. sock.BeginReceive(headerBuf, cbRecv, headerBuf.Length - cbRecv, SocketFlags.None, onRecv, null); } else { // We're in the process of reading the message contents. // Complete the contents receive operation and queue the // message for delivery after we leave the lock. sock.EndReceiveAll(ar); message.Contents = contentBuf; if (received == null) { received = new List <SipMessage>(); } received.Add(message); // Reset and start reading the next message envelope. message = null; contentBuf = null; cbRecv = 0; sock.BeginReceive(headerBuf, 0, headerBuf.Length, SocketFlags.None, onRecv, null); } } catch (SocketException) { CloseAndRemove(); } catch (Exception e) { SysLog.LogException(e); CloseAndRemove(); } } } finally { // Deliver any queued messages (outside of the lock) if (received != null) { foreach (var message in received) { message.SourceTransport = transport; message.RemoteEndpoint = remoteEP; if ((transport.traceMode & SipTraceMode.Receive) != 0) { SipHelper.Trace(string.Format("TCP: received from {0}", remoteEP), message); } transport.router.Route(transport, message); } } } }
/// <summary> /// Handles receive completions on the socket. /// </summary> /// <param name="ar">The async result.</param> private void OnReceive(IAsyncResult ar) { int cbRecv; try { using (TimedLock.Lock(router.SyncRoot)) { if (sock == null || recvBuf == null) { return; } try { cbRecv = sock.EndReceive(ar); } catch (SocketException e2) { if (e2.SocketErrorCode == SocketError.ConnectionReset) { cbRecv = 0; // Treat connection resets as connection closure } else { throw; } } if (cbRecv == 0) { sock.ShutdownAndClose(); return; } SetLastAccess(); recvPos += cbRecv; if (recvPos < recvBuf.Length) { // Continue the reception sock.BeginReceive(recvBuf, recvPos, recvBuf.Length - recvPos, SocketFlags.None, onReceive, null); return; } if (recvHeader) { // Initiate reception of the frame payload int pos = 0; int cbFrame; byte[] buf; cbFrame = Helper.ReadInt32(recvBuf, ref pos); buf = new byte[MsgRouter.FrameHeaderSize + cbFrame]; Array.Copy(recvBuf, 0, buf, 0, MsgRouter.FrameHeaderSize); recvHeader = false; recvBuf = buf; recvPos = MsgRouter.FrameHeaderSize; cbRecv = cbFrame; sock.BeginReceive(recvBuf, recvPos, cbFrame, SocketFlags.None, onReceive, null); } else { // We've completed the reception of a message Msg msg; byte[] msgBuf; int cbMsg; msgBuf = router.DecryptFrame(recvBuf, recvBuf.Length, out cbMsg); msg = Msg.Load(new EnhancedMemoryStream(msgBuf)); msgBuf = null; // Handle initialization messages locally and queue a // notification for all other messages so that router.OnReceive() // will be called on a worker thread. if (!initProcessed) { TcpInitMsg initMsg; Exception e; initMsg = msg as TcpInitMsg; if (initMsg == null) { e = new MsgException("Invalid TCP channel protocol: TcpInitMsg expected."); SysLog.LogException(e); Helper.Rethrow(e); } initMsg._Trace(router, 2, "Receive", null); // If the sender indicates that it is an uplink then we're going // to use the actual remote port for the remoteEP rather than // ListenPort (which should be 0). The reason for this is that // intervening routers and NATs may translate the port number // reported by the child router. routerEP = initMsg.RouterEP; remoteEP.NetEP.Port = initMsg.IsUplink ? ((IPEndPoint)sock.RemoteEndPoint).Port : initMsg.ListenPort; isDownlink = initMsg.IsUplink; isP2P = initMsg.RouterInfo.IsP2P; initProcessed = true; router.OnTcpInit(this); } else { msg._SetFromChannel(remoteEP); msg._Trace(router, 2, "TCP: Recv", null); msg._Trace(router, 0, "Receive", string.Empty); router.OnReceive(this, msg); } // Initiate reception of the next message BeginReceive(); } } } catch (SocketException e) { // Don't log connection resets because we see these all the // time when a router stops. We're not going to consider // this to be an error. if (e.SocketErrorCode != SocketError.ConnectionReset) { TraceException(e); SysLog.LogException(e); } router.OnTcpClose(this); Close(); } catch (Exception e) { TraceException(e); SysLog.LogException(e); router.OnTcpClose(this); Close(); } }