/// <summary> /// Sends to the HTTP response asynchronously, then closes the connection if required. /// </summary> /// <param name="persist">Sets or overrides exiting Connection header unless Unspecified, /// in which case observes existing header or default behavior for version</param> public void Send(ConnectionPersistence persist) { // By default, don't do anything with the connection after sending EventHandler <SocketAsyncEventArgs> finished = (sender, comp) => { }; if (persist != ConnectionPersistence.UNSPECIFIED) { // There's some value for "persist", use it headers["Connection"] = Utility.PERSISTENCE[(int)persist]; if (ConnectionPersistence.CLOSE == persist) { // Close the connection after send is complete finished = (sender, comp) => { socket.Close(); }; } } else if (headers.ContainsKey("Connection")) { // Figure out what to do with the connection String conn = headers["Connection"]; if (conn.Equals( Utility.PERSISTENCE[(int)ConnectionPersistence.KEEP_ALIVE], StringComparison.OrdinalIgnoreCase)) { persist = ConnectionPersistence.KEEP_ALIVE; } else if ( conn.Equals(Utility.PERSISTENCE[(int)ConnectionPersistence.CLOSE], StringComparison.OrdinalIgnoreCase)) { finished = (sender, comp) => { socket.Close(); }; } else if (this.version != HttpVersion.ONE_POINT_ONE) { // Default to close for pre-1.1 finished = (sender, comp) => { socket.Close(); }; } } // If that falls through, there's no Connection header else if (this.version != HttpVersion.ONE_POINT_ONE) { // Default to close for pre-1.1 finished = (sender, comp) => { socket.Close(); }; } byte[] resp = buildResponse(); SocketAsyncEventArgs args = new SocketAsyncEventArgs(); args.SetBuffer(resp, 0, resp.Length); args.Completed += finished; socket.SendBufferSize = resp.Length; if (!socket.SendAsync(args)) { finished(null, null); } }
/// <summary> /// Allows creating an empty HttpRequest object. /// Caller must then use Continue to populate the request data. /// </summary> public HttpRequest() { method = HttpMethod.INVALID_METHOD; path = null; querystring = null; fragment = null; version = HttpVersion.INVALID_VERSION; urlparams = null; contentlength = -1L; persist = ConnectionPersistence.UNSPECIFIED; headers = null; multipartboundry = null; body = null; bodytext = null; bodyparts = null; current = 0; currentLine = 0; bodyIndex = -1; }
private String parseRequest(String request, bool resume) { String[] lines = request.Split(new String[] { "\r\n", "\n" }, StringSplitOptions.None); #region FIRSTLINE if (!resume || 0 == currentLine) { parseFirstLine(lines[0]); } // if (!resume || 0 == currentLine) #endregion // firstline // Parse the headers if (lines.Length > 2) { headers = new Dictionary <String, String>(lines.Length - 2); // Max header count } else { headers = new Dictionary <string, string>(); } if (!resume) { contentlength = -1; } for (currentLine = (resume) ? currentLine : 1; currentLine < lines.Length; currentLine++) { #region ENDREQUEST if (String.IsNullOrEmpty(lines[currentLine])) { // Two carriage returns in a row signals the end of the headers int endIndex = request.IndexOf("\r\n\r\n") + 4; if (3 == endIndex) { endIndex = request.IndexOf("\n\n") + 2; } if (contentlength > 0) { // There is some body to this request int bodyLen = Math.Min((int)contentlength, (request.Length - endIndex)); body = Encoding.UTF8.GetBytes(request.Substring(endIndex, bodyLen)); bodytext = request.Substring(endIndex); // Check whether we're done... if (bodyLen < contentlength) { // We need more body } else { #region MULTIPART // End of the headers. If multipart, parse the parts if (multipartboundry != null) { int firstpart = bodytext.IndexOf("--" + multipartboundry); // Find the end of the line firstpart = bodytext.IndexOf('\n', firstpart) + 1; if (-1 == firstpart) { throw new ProtocolViolationException("Incomplete multipart boundary line: " + lines[currentLine + 1]); } String line = bodytext.Substring(firstpart, (bodytext.IndexOf('\n', firstpart) - firstpart)).Trim(); while (!String.IsNullOrWhiteSpace(line)) { int partIndex = line.IndexOf(':'); String partHeaderName, partHeaderValue; if (partIndex > 0) { partHeaderName = line.Substring(0, partIndex).Trim(); partHeaderValue = line.Substring(partIndex + 1).Trim(); } else { partHeaderName = line; partHeaderValue = null; } } } #endregion // Note that we're done with this request, return any remnant current = -1; currentLine = -1; request = request.Substring(endIndex + (int)contentlength); } } else { // There is no body to this request; we're done current = -1; currentLine = -1; request = request.Substring(endIndex); } // Either way, we're done parsing return(request); } #endregion #region HEADERS // OK, this should be a header int valueIndex = lines[currentLine].IndexOf(':'); String headerName, headerValue; if (valueIndex > 0) { headerName = lines[currentLine].Substring(0, valueIndex).Trim(); headerValue = lines[currentLine].Substring(valueIndex + 1).Trim(); } else { headerName = lines[currentLine]; headerValue = null; } if (headerName.Equals("Content-Length", StringComparison.OrdinalIgnoreCase)) { if (!long.TryParse(headerValue, out contentlength) && (currentLine + 1) < lines.Length) { // This was supposed to be a complete line. It is broken throw new ProtocolViolationException( "Invalid value for HTTP header (" + lines[currentLine] + ")"); } // Move on to the next header continue; } else if (headerName.Equals("Content-Type", StringComparison.OrdinalIgnoreCase)) { // For now, just look for multipart if (headerValue.StartsWith("multipart")) { int offset = headerValue.IndexOf("boundary="); if (offset > 0) { multipartboundry = headerValue.Substring(offset + 9); } else { // There is supposed to be a boundary definition here... throw new ProtocolViolationException( "Invalid value for HTTP header (" + lines[currentLine] + ")"); } } } else if (headerName.Equals("Connection", StringComparison.OrdinalIgnoreCase)) { for (int i = 0; i < Utility.PERSISTENCE.Length; i++) { if (headerValue.Equals(Utility.PERSISTENCE[i], StringComparison.OrdinalIgnoreCase)) { persist = (ConnectionPersistence)i; break; } } } // May be some other header that we don't yet recognize headers[headerName] = headerValue; #endregion } // If we get here, we didn't find the end of the request... return(request); }