private void SaveDataFrame(Http2Stream stream, DataFrame dataFrame) { string originalPath = stream.Headers.GetValue(CommonHeaders.Path.ToLower()); //If user sets the empty file in get command we return notFound webpage string fileName = string.IsNullOrEmpty(Path.GetFileName(originalPath)) ? Index : Path.GetFileName(originalPath); string path = Path.Combine(AssemblyPath, fileName); try { _fileHelper.SaveToFile(dataFrame.Data.Array, dataFrame.Data.Offset, dataFrame.Data.Count, path, stream.ReceivedDataAmount != 0); } catch (IOException) { Http2Logger.LogError("File is still downloading. Repeat request later"); stream.Close(ResetStatusCode.InternalError); return; } stream.ReceivedDataAmount += dataFrame.Data.Count; if (dataFrame.IsEndStream) { if (stream.HalfClosedRemote) { //send terminator stream.WriteDataFrame(new ArraySegment <byte>(new byte[0]), true); Http2Logger.LogConsole("Terminator was sent"); } _fileHelper.RemoveStream(path); Http2Logger.LogConsole("Bytes received: " + stream.ReceivedDataAmount); } }
public async void StartConnection() { Console.WriteLine("Start connection called"); if (_useHttp20 && !_sessionAdapter.IsDisposed && !_isDisposed) { Dictionary <string, string> initialRequest = null; if (!_isSecure) { initialRequest = new Dictionary <string, string> { { CommonHeaders.Path, _path }, }; } await _sessionAdapter.StartSessionAsync(initialRequest); //GC.Collect(); } if (!_sessionAdapter.IsDisposed) { return; } Http2Logger.LogError("Connection was aborted by the remote side. Check your session header."); Dispose(true); }
private void RequestSentHandler(object sender, RequestSentEventArgs args) { var stream = args.Stream; var method = stream.Headers.GetValue(":method"); if (method == "put" || method == "post") { var localPath = stream.Headers.GetValue(":localPath".ToLower()); byte[] binary = null; bool gotException = false; try { binary = _fileHelper.GetFile(localPath); } catch (FileNotFoundException) { gotException = true; Http2Logger.LogError("Specified file not found: " + localPath); } if (!gotException) { SendDataTo(args.Stream, binary); } } }
public async void StartConnection() { if (_useHttp20 && !_socket.IsClosed && !_isDisposed) { await _clientSession.Start(); } else if (_socket.IsClosed || _isDisposed) { Http2Logger.LogError("Connection was aborted by the remote side. Check your session header."); Dispose(true); } }
private void SaveDataFrame(Http2Stream stream, DataFrame dataFrame) { lock (_writeLock) { string originalPath = stream.Headers.GetValue(":path".ToLower()); //If user sets the empty file in get command we return notFound webpage string fileName = string.IsNullOrEmpty(Path.GetFileName(originalPath)) ? NotFound : Path.GetFileName(originalPath); string path = Path.Combine(AssemblyPath, fileName); try { _fileHelper.SaveToFile(dataFrame.Data.Array, dataFrame.Data.Offset, dataFrame.Data.Count, path, stream.ReceivedDataAmount != 0); } catch (IOException) { Http2Logger.LogError("File is still downloading. Repeat request later"); stream.WriteDataFrame(new byte[0], true); stream.Dispose(); } stream.ReceivedDataAmount += dataFrame.FrameLength; if (dataFrame.IsEndStream) { if (!stream.EndStreamSent) { //send terminator stream.WriteDataFrame(new byte[0], true); Http2Logger.LogConsole("Terminator was sent"); } _fileHelper.RemoveStream(path); Http2Logger.LogConsole("Bytes received " + stream.ReceivedDataAmount); #if DEBUG const string wayToServerRoot1 = @"..\..\..\..\..\Drop\Root"; const string wayToServerRoot2 = @".\Root"; var areFilesEqual = _fileHelper.CompareFiles(path, wayToServerRoot1 + originalPath) || _fileHelper.CompareFiles(path, wayToServerRoot2 + originalPath); if (!areFilesEqual) { Console.ForegroundColor = ConsoleColor.Red; Http2Logger.LogError("Files are NOT EQUAL!"); } else { Console.ForegroundColor = ConsoleColor.Green; Http2Logger.LogConsole("Files are EQUAL!"); } Console.ForegroundColor = ConsoleColor.Gray; #endif } } }
public void ReadHeadersAndInspectHandshake() { try { _response = Read11Headers(); _wasResponseReceived = true; } catch (Exception ex) { Http2Logger.LogError(ex.Message); throw; } }
public HttpSocketServer(Func <IDictionary <string, object>, Task> next, IDictionary <string, object> properties) { _next = next; var addresses = (IList <IDictionary <string, object> >)properties[OwinConstants.CommonKeys.Addresses]; var address = addresses.First(); _port = Int32.Parse(address.Get <string>("port")); _scheme = address.Get <string>("scheme"); _useHandshake = (bool)properties["use-handshake"]; _usePriorities = (bool)properties["use-priorities"]; _useFlowControl = (bool)properties["use-flowControl"]; int securePort; try { securePort = int.Parse(ConfigurationManager.AppSettings["securePort"]); } catch (Exception) { Http2Logger.LogError("Incorrect port in the config file!" + ConfigurationManager.AppSettings["securePort"]); return; } if (_port == securePort && _scheme == Uri.UriSchemeHttp || _port != securePort && _scheme == Uri.UriSchemeHttps) { Http2Logger.LogError("Invalid scheme or port! Use https for secure port."); return; } var extensions = new[] { ExtensionType.Renegotiation, ExtensionType.ALPN }; // protocols should be in order of their priority _options = _port == securePort ? new SecurityOptions(SecureProtocol.Tls1, extensions, new[] { Protocols.Http2, Protocols.Http1 }, ConnectionEnd.Server) : new SecurityOptions(SecureProtocol.None, extensions, new[] { Protocols.Http2, Protocols.Http1 }, ConnectionEnd.Server); _options.VerificationType = CredentialVerification.None; _options.Certificate = Certificate.CreateFromCerFile(AssemblyName + CertificateFilename); _options.Flags = SecurityFlags.Default; _options.AllowedAlgorithms = SslAlgorithms.RSA_AES_256_SHA | SslAlgorithms.NULL_COMPRESSION; _server = new SecureTcpListener(_port, _options); ThreadPool.SetMaxThreads(30, 10); Listen(); }
public static void Http11SendResponse(SecureSocket socket) { string[] headers = GetHttp11Headers(socket); string filename = GetFileName(headers); if (headers.Length == 0) { Http2Logger.LogError("Request headers empty!"); } string path = Path.GetFullPath(AssemblyPath + @"\root" + filename); string contentType = ContentTypes.GetTypeFromFileName(filename); if (!File.Exists(path)) { Http2Logger.LogError("File " + filename + " not found"); SendResponse(socket, new byte[0], StatusCode.Code404NotFound, contentType); socket.Close(); return; } try { using (var sr = new StreamReader(path)) { string file = sr.ReadToEnd(); var fileBytes = Encoding.UTF8.GetBytes(file); int sent = SendResponse(socket, fileBytes, StatusCode.Code200Ok, contentType); Http2Logger.LogDebug(string.Format("Sent: {0} bytes", sent)); Http2Logger.LogInfo("File sent: " + filename); socket.Close(); if (OnSocketClosed != null) { OnSocketClosed(null, new SocketCloseEventArgs()); } } } catch (Exception ex) { var msgBytes = Encoding.UTF8.GetBytes(ex.Message); SendResponse(socket, msgBytes, StatusCode.Code500InternalServerError, contentType); Http2Logger.LogError(ex.Message); } }
/// <summary> /// Pumps the outgoing data to write queue /// </summary> /// <returns></returns> private Task PumpOutgoingData() { return(Task.Run(() => { try { _writeQueue.PumpToStream(); } catch (Exception) { Http2Logger.LogError("Sending frame was cancelled because connection was lost"); Dispose(); } })); }
private async void OpenHttp2Session(SecureSocket incomingClient, IDictionary <string, object> handshakeResult) { Http2Logger.LogDebug("Handshake successful"); _session = new Http2Session(incomingClient, ConnectionEnd.Server, _usePriorities, _useFlowControl, handshakeResult); _session.OnFrameReceived += FrameReceivedHandler; try { await _session.Start(); } catch (Exception) { Http2Logger.LogError("Client was disconnected"); } }
private void Listen() { InitializeRootFileList(); Http2Logger.LogInfo("Started on port " + _port); _server.Start(); while (!_disposed) { try { var client = new HttpConnectingClient(_server, _options, _next, _useHandshake, _usePriorities, _useFlowControl, _listOfRootFiles); client.Accept(); } catch (Exception ex) { Http2Logger.LogError("Unhandled exception was caught: " + ex.Message); } } }
private async void OpenHttp2Session(Stream incomingClientStream) { Http2Logger.LogDebug("Handshake successful"); using (var messageHandler = new Http2OwinMessageHandler(incomingClientStream, ConnectionEnd.Server, incomingClientStream is SslStream, _next, _cancelClientHandling.Token)) { try { await messageHandler.StartSessionAsync(); } catch (Exception) { Http2Logger.LogError("Client was disconnected"); } } // GC.Collect(); }
/// <summary> /// Inspect if request if http2 upgrade /// If so starts http2 session via provider. /// Calls next layer if not. /// </summary> /// <returns></returns> public override async Task Invoke(IOwinContext context) { if (IsOpaqueUpgradePossible(context.Request) && IsRequestForHttp2Upgrade(context.Request)) { var upgradeDelegate = context.Environment[CommonOwinKeys.OpaqueUpgrade] as UpgradeDelegate; Debug.Assert(upgradeDelegate != null, "upgradeDelegate is not null"); // save original request parameters; used to complete request after upaque upgrade is done var requestCopy = GetInitialRequestParams(context.Request); upgradeDelegate.Invoke(new Dictionary <string, object>(), async opaque => { //use the same stream which was used during upgrade var opaqueStream = opaque.Environment[CommonOwinKeys.OpaqueStream] as Stream; //TODO Provide cancellation token here // Move to method try { using (var http2MessageHandler = new Http2OwinMessageHandler(opaqueStream, ConnectionEnd.Server, opaqueStream is SslStream, Next.Invoke, CancellationToken.None) ) { await http2MessageHandler.StartSessionAsync(requestCopy); // GC.Collect(); } } catch (Exception ex) { Http2Logger.LogError(ex.Message); } }); // specify Upgrade protocol context.Response.Headers.Add(CommonHeaders.Upgrade, new[] { Protocols.Http2NoTls }); return; } //If we dont have upgrade delegate then pass request to the next layer await Next.Invoke(context); }
/// <summary> /// Pumps the outgoing data to write queue /// </summary> /// <returns></returns> private void PumpOutgoingData() { try { _writeQueue.PumpToStream(_cancelSessionToken); } catch (OperationCanceledException) { Http2Logger.LogError("Handling session was cancelled"); Dispose(); } catch (Exception) { Http2Logger.LogError("Sending frame was cancelled because connection was lost"); Dispose(); } Http2Logger.LogDebug("Write thread finished"); }
public HttpSocketServer(Func <IOwinContext, Task> next, IDictionary <string, object> properties) { _next = next; var addresses = (IList <IDictionary <string, object> >)properties["host.Addresses"]; var address = addresses.First(); _port = Int32.Parse(address.Get <string>("port")); _scheme = address.Get <string>("scheme"); _cancelAccept = new CancellationTokenSource(); _useHandshake = (bool)properties["use-handshake"]; _usePriorities = (bool)properties["use-priorities"]; _useFlowControl = (bool)properties["use-flowControl"]; int securePort; if (!int.TryParse(ConfigurationManager.AppSettings["securePort"], out securePort)) { Http2Logger.LogError("Incorrect port in the config file!"); return; } try { _serverCert = LoadPKCS12Certificate(AssemblyName + CertificateFilename, "p@ssw0rd"); } catch (Exception ex) { Http2Logger.LogInfo("Unable to start server. Check certificate. Exception: " + ex.Message); return; } _isSecure = _port == securePort; _server = new TcpListener(IPAddress.Any, _port); ThreadPool.SetMaxThreads(30, 10); _listenThread = new Thread(Listen); _listenThread.Start(); }
private void Listen() { Http2Logger.LogInfo("Server running at port " + _port); _server.Start(); while (!_disposed) { try { var client = new HttpConnectingClient(_server, _next.Invoke, _serverCert, _isSecure, _useHandshake, _usePriorities, _useFlowControl); client.Accept(_cancelAccept.Token); } catch (Exception ex) { Http2Logger.LogError("Unhandled exception was caught: " + ex.Message); } } Http2Logger.LogDebug("Listen thread was finished"); }
private void HandleRequest(SecureSocket incomingClient, string alpnSelectedProtocol, bool backToHttp11, IDictionary <string, object> handshakeResult) { if (backToHttp11 || alpnSelectedProtocol == Protocols.Http1) { Http2Logger.LogDebug("Sending with http11"); Http11Manager.Http11SendResponse(incomingClient); return; } if (GetSessionHeaderAndVerifyIt(incomingClient)) { OpenHttp2Session(incomingClient, handshakeResult); } else { Http2Logger.LogError("Client has wrong session header. It was disconnected"); incomingClient.Close(); } }
/// <summary> /// Authenticate client. /// </summary> /// <param name="remoteEndpoint">Uri connect.</param> /// <param name="sslStream">Sslstreamn argument.</param> /// <returns>Returned true if success.</returns> private static bool AuthenticateAsClient(Uri remoteEndpoint, SslStream sslStream) { try { X509Certificate certificate = new X509Certificate("certificate.pfx"); sslStream.AuthenticateAsClient(remoteEndpoint.Host, new X509CertificateCollection(new[] { certificate }), SslProtocols.Tls, false); } catch (AuthenticationException e) { Http2Logger.LogError(e.Message); if (e.InnerException != null) { Http2Logger.LogError(string.Format("Inner exception: {0}", e.InnerException.Message)); } Http2Logger.LogError("Authentication failed - closing the connection."); return(false); } return(true); }
private void HandleAcceptedClient(Stream incomingClient) { bool backToHttp11 = false; string selectedProtocol = Protocols.Http1; if (_useHandshake) { try { if (_isSecure) { incomingClient = new SslStream(incomingClient, false); (incomingClient as SslStream).AuthenticateAsServer(_cert); selectedProtocol = (incomingClient as SslStream).AlpnSelectedProtocol; } } catch (OpenSslException) { backToHttp11 = true; } catch (Exception e) { Http2Logger.LogError("Exception occurred. Closing client's socket. " + e.Message); incomingClient.Close(); return; } } try { HandleRequest(incomingClient, selectedProtocol, backToHttp11); } catch (Exception e) { Http2Logger.LogError("Exception occurred. Closing client's socket. " + e.Message); incomingClient.Close(); } }
private static void SaveFile(string directory, string fileName, byte[] fileBytes) { string newfilepath; // create local file path if (!string.IsNullOrEmpty(directory)) { if (directory[0] == '\\') { directory = '.' + directory; } Directory.CreateDirectory(directory); newfilepath = directory + '\\' + fileName; } else { newfilepath = fileName; } if (File.Exists(newfilepath)) { try { File.Delete(newfilepath); } catch (Exception) { Http2Logger.LogError("Cant overwrite file: " + newfilepath); } } using (var fs = new FileStream(newfilepath, FileMode.Create)) { fs.Write(fileBytes, 0, fileBytes.Length); } Http2Logger.LogInfo("File saved: " + fileName); }
private void SaveDataFrame(Http2Stream stream, DataFrame dataFrame) { lock (_writeLock) { string path = stream.Headers.GetValue(":path".ToLower()); try { string pathToSave = AssemblyPath + Root + path; if (!Directory.Exists(Path.GetDirectoryName(pathToSave))) { throw new DirectoryNotFoundException("Access denied"); } _fileHelper.SaveToFile(dataFrame.Data.Array, dataFrame.Data.Offset, dataFrame.Data.Count, pathToSave, stream.ReceivedDataAmount != 0); } catch (Exception ex) { Http2Logger.LogError(ex.Message); stream.WriteDataFrame(new byte[0], true); stream.Dispose(); } stream.ReceivedDataAmount += dataFrame.FrameLength; if (dataFrame.IsEndStream) { if (!stream.EndStreamSent) { //send terminator stream.WriteDataFrame(new byte[0], true); Http2Logger.LogDebug("Terminator was sent"); } _fileHelper.RemoveStream(AssemblyPath + Root + path); } } }
/// <summary> /// Dispatches the incoming frame. /// </summary> /// <param name="frame">The frame.</param> /// <exception cref="System.NotImplementedException"></exception> private void DispatchIncomingFrame(Frame frame) { Http2Stream stream = null; //Spec 03 tells that frame with continues flag MUST be followed by a frame with the same type //and the same stread id. if (_toBeContinuedHeaders != null) { if (_toBeContinuedFrame.FrameType != frame.FrameType || _toBeContinuedFrame.StreamId != frame.StreamId) { //If not, we must close the session. Dispose(); return; } } try { switch (frame.FrameType) { case FrameType.Headers: Http2Logger.LogDebug("New headers with id = " + frame.StreamId); var headersFrame = (HeadersFrame)frame; var serializedHeaders = new byte[headersFrame.CompressedHeaders.Count]; Buffer.BlockCopy(headersFrame.CompressedHeaders.Array, headersFrame.CompressedHeaders.Offset, serializedHeaders, 0, serializedHeaders.Length); var decompressedHeaders = _comprProc.Decompress(serializedHeaders); var headers = new HeadersList(decompressedHeaders); if (!headersFrame.IsEndHeaders) { _toBeContinuedHeaders = decompressedHeaders; _toBeContinuedFrame = headersFrame; break; } if (_toBeContinuedHeaders != null) { headers.AddRange(_toBeContinuedHeaders); } headersFrame.Headers.AddRange(headers); foreach (var header in headers) { Http2Logger.LogDebug("Stream {0} header: {1}={2}", frame.StreamId, header.Key, header.Value); } stream = GetStream(headersFrame.StreamId); if (stream == null) { // new stream if (ActiveStreams.GetOpenedStreamsBy(_remoteEnd) + 1 > OurMaxConcurrentStreams) { //Remote side tries to open more streams than allowed Dispose(); return; } string path = headers.GetValue(":path"); if (path == null) { path = _handshakeHeaders.ContainsKey(":path") ? _handshakeHeaders[":path"] : @"\index.html"; headers.Add(new KeyValuePair <string, string>(":path", path)); } stream = new Http2Stream(headers, headersFrame.StreamId, _writeQueue, _flowControlManager, _comprProc); ActiveStreams[stream.Id] = stream; stream.OnClose += (o, args) => { if (!ActiveStreams.Remove(ActiveStreams[args.Id])) { throw new ArgumentException("Cant remove stream from ActiveStreams"); } }; _toBeContinuedFrame = null; _toBeContinuedHeaders = null; } break; case FrameType.Priority: var priorityFrame = (PriorityFrame)frame; Http2Logger.LogDebug("Priority frame. StreamId: {0} Priority: {1}", priorityFrame.StreamId, priorityFrame.Priority); stream = GetStream(priorityFrame.StreamId); if (_usePriorities) { stream.Priority = priorityFrame.Priority; } break; case FrameType.RstStream: var resetFrame = (RstStreamFrame)frame; stream = GetStream(resetFrame.StreamId); if (stream != null) { Http2Logger.LogDebug("RST frame with code " + resetFrame.StatusCode); stream.Dispose(); } break; case FrameType.Data: var dataFrame = (DataFrame)frame; Http2Logger.LogDebug("Data frame. StreamId:{0} Length:{1}", dataFrame.StreamId, dataFrame.FrameLength); stream = GetStream(dataFrame.StreamId); //Aggressive window update if (stream.IsFlowControlEnabled) { stream.WriteWindowUpdate(2000000); } break; case FrameType.Ping: var pingFrame = (PingFrame)frame; Http2Logger.LogDebug("Ping frame with StreamId:{0} Payload:{1}", pingFrame.StreamId, pingFrame.Payload.Count); if (pingFrame.FrameLength != PingFrame.PayloadLength) { throw new ProtocolError(ResetStatusCode.ProtocolError, "Ping payload size is not equal to 8"); } if (pingFrame.IsPong) { _wasPingReceived = true; _pingReceived.Set(); } else { var pongFrame = new PingFrame(true, pingFrame.Payload.ToArray()); _writeQueue.WriteFrame(pongFrame); } break; case FrameType.Settings: //Not first frame in the session. //Client initiates connection and sends settings before request. //It means that if server sent settings before it will not be a first frame, //because client initiates connection. if (_ourEnd == ConnectionEnd.Server && !_wasSettingsReceived && ActiveStreams.Count != 0) { Dispose(); return; } var settingFrame = (SettingsFrame)frame; Http2Logger.LogDebug("Settings frame. Entry count: {0} StreamId: {1}", settingFrame.EntryCount, settingFrame.StreamId); _wasSettingsReceived = true; _settingsManager.ProcessSettings(settingFrame, this, _flowControlManager); break; case FrameType.WindowUpdate: if (_useFlowControl) { var windowFrame = (WindowUpdateFrame)frame; Http2Logger.LogDebug("WindowUpdate frame. Delta: {0} StreamId: {1}", windowFrame.Delta, windowFrame.StreamId); stream = GetStream(windowFrame.StreamId); if (stream != null) { stream.UpdateWindowSize(windowFrame.Delta); stream.PumpUnshippedFrames(); } } break; case FrameType.GoAway: _goAwayReceived = true; Http2Logger.LogDebug("GoAway frame received"); Dispose(); break; default: throw new NotImplementedException(frame.FrameType.ToString()); } if (stream != null && frame is IEndStreamFrame && ((IEndStreamFrame)frame).IsEndStream) { //Tell the stream that it was the last frame Http2Logger.LogDebug("Final frame received for stream with id = " + stream.Id); stream.EndStreamReceived = true; } if (stream != null && OnFrameReceived != null) { OnFrameReceived(this, new FrameReceivedEventArgs(stream, frame)); } } //Frame came for already closed stream. Ignore it. //Spec: //An endpoint that sends RST_STREAM MUST ignore //frames that it receives on closed streams if it sends RST_STREAM. // //An endpoint MUST NOT send frames on a closed stream. An endpoint //that receives a frame after receiving a RST_STREAM or a frame //containing a END_STREAM flag on that stream MUST treat that as a //stream error (Section 5.4.2) of type PROTOCOL_ERROR. catch (Http2StreamNotFoundException) { if (stream != null) { stream.WriteRst(ResetStatusCode.ProtocolError); } else { //GoAway? } } catch (CompressionError ex) { //The endpoint is unable to maintain the compression context for the connection. Http2Logger.LogError("Compression error occured: " + ex.Message); Close(ResetStatusCode.CompressionError); } catch (ProtocolError pEx) { Http2Logger.LogError("Protocol error occured: " + pEx.Message); Close(pEx.Code); } }
/// <summary> /// Dispatches the incoming frame. /// </summary> /// <param name="frame">The frame.</param> /// <exception cref="System.NotImplementedException"></exception> private void DispatchIncomingFrame(Frame frame) { Http2Stream stream = null; try { if (frame.PayloadLength > Constants.MaxFramePayloadSize) { throw new ProtocolError(ResetStatusCode.FrameSizeError, String.Format("Frame too large: Type: {0} {1}", frame.FrameType, frame.PayloadLength)); } /* 12 -> 6.5 * A SETTINGS frame MUST be sent by both endpoints at the start of a * connection, and MAY be sent at any other time by either endpoint over * the lifetime of the connection.*/ if (frame.FrameType != FrameType.Settings && !_wasSettingsReceived) { throw new ProtocolError(ResetStatusCode.ProtocolError, "Settings frame was not the first frame in the session"); } Http2Logger.LogDebug("Incoming frame: " + frame.FrameType); switch (frame.FrameType) { case FrameType.Headers: HandleHeaders(frame as HeadersFrame, out stream); break; case FrameType.Continuation: HandleContinuation(frame as ContinuationFrame, out stream); break; case FrameType.Priority: HandlePriority(frame as PriorityFrame, out stream); break; case FrameType.RstStream: HandleRstFrame(frame as RstStreamFrame, out stream); break; case FrameType.Data: HandleDataFrame(frame as DataFrame, out stream); break; case FrameType.Ping: HandlePingFrame(frame as PingFrame); break; case FrameType.Settings: HandleSettingsFrame(frame as SettingsFrame); if (!(frame as SettingsFrame).IsAck) { // sending ACK settings WriteSettings(new SettingsPair[0], true); } break; case FrameType.WindowUpdate: HandleWindowUpdateFrame(frame as WindowUpdateFrame, out stream); break; case FrameType.GoAway: HandleGoAwayFrame(frame as GoAwayFrame); break; case FrameType.PushPromise: HandlePushPromiseFrame(frame as PushPromiseFrame, out stream); if (stream != null) //This means that sequence is complete { _promisedResources.Add(stream.Id, stream.Headers.GetValue(CommonHeaders.Path)); } break; case FrameType.AltSvc: HandleAltSvcFrame(frame); break; case FrameType.Blocked: HandleBlockedFrame(frame); break; default: /* 12 -> 4.1 * Implementations MUST treat the receipt of an unknown frame type * (any frame types not defined in this document) as a connection * error of type PROTOCOL_ERROR. */ throw new ProtocolError(ResetStatusCode.ProtocolError, "Unknown frame type detected"); } _lastFrame = frame; if (frame is IEndStreamFrame && ((IEndStreamFrame)frame).IsEndStream) { //Tell the stream that it was the last frame Http2Logger.LogDebug("Final frame received for stream id=" + stream.Id); stream.HalfClosedRemote = true; //Promised resource has been pushed if (_promisedResources.ContainsKey(stream.Id)) { _promisedResources.Remove(stream.Id); } } if (stream == null || OnFrameReceived == null) { return; } OnFrameReceived(this, new FrameReceivedEventArgs(stream, frame)); stream.FramesReceived++; } //09 //5.1. Stream States //An endpoint MUST NOT send frames on a closed stream. An endpoint //that receives a frame after receiving a RST_STREAM [RST_STREAM] or //a frame containing a END_STREAM flag on that stream MUST treat //that as a stream error (Section 5.4.2) of type STREAM_CLOSED //[STREAM_CLOSED]. catch (Http2StreamNotFoundException ex) { Http2Logger.LogDebug("Frame for already Closed stream with stream id={0}", ex.Id); var rstFrame = new RstStreamFrame(ex.Id, ResetStatusCode.StreamClosed); Http2Logger.LogDebug("Sending RST_STREAM frame: stream id={0}, status code={1}", rstFrame.StreamId, rstFrame.StatusCode); _writeQueue.WriteFrame(rstFrame); stream.WasRstSent = true; } catch (CompressionError ex) { //The endpoint is unable to maintain the compression context for the connection. Http2Logger.LogError("Compression error occurred: " + ex.Message); Close(ResetStatusCode.CompressionError); } catch (ProtocolError pEx) { Http2Logger.LogError("Protocol error occurred: " + pEx.Message); Close(pEx.Code); } catch (MaxConcurrentStreamsLimitException) { //Remote side tries to open more streams than allowed Dispose(); } catch (Exception ex) { Http2Logger.LogError("Unknown error occurred: " + ex.Message); Close(ResetStatusCode.InternalError); } }
/// <summary> /// Processes incoming request. /// </summary> public async void ProcessRequest() { try { // invalid connection, skip if (!_client.CanRead) { return; } var rawHeaders = Http11Helper.ReadHeaders(_client); Http2Logger.LogDebug("Http1.1 Protocol Handler. Process request " + string.Join(" ", rawHeaders)); // invalid connection, skip if (rawHeaders == null || rawHeaders.Length == 0) { return; } // headers[0] contains METHOD, URI and VERSION like "GET /api/values/1 HTTP/1.1" var headers = Http11Helper.ParseHeaders(rawHeaders.Skip(1).ToArray()); // client MUST include Host header due to HTTP/1.1 spec if (!headers.ContainsKey("Host")) { throw new ApplicationException("Host header is missing"); } // parse request parameters: method, path, host, etc var splittedRequestString = rawHeaders[0].Split(' '); var method = splittedRequestString[0]; if (!IsMethodSupported(method)) { throw new NotSupportedException(method + " method is not currently supported via HTTP/1.1"); } var scheme = _protocol == SslProtocols.None ? Uri.UriSchemeHttp : Uri.UriSchemeHttps; var path = splittedRequestString[1]; // main OWIN environment components // OWIN request and response below shares the same environment dictionary instance _environment = CreateOwinEnvironment(method, scheme, "", path, headers); _request = new OwinRequest(_environment); _response = new OwinResponse(_environment); // we may need to populate additional fields if request supports UPGRADE AddOpaqueUpgradeIfNeeded(); await _next.Invoke(new OwinContext(_environment)); if (_opaqueCallback == null) { EndResponse(); } else { // do not close connection EndResponse(false); var opaqueEnvironment = CreateOpaqueEnvironment(); await _opaqueCallback(new OwinContext(opaqueEnvironment)); } } catch (Exception ex) { Http2Logger.LogError(ex.Message); EndResponse(ex); Http2Logger.LogDebug("Closing connection"); _client.Close(); } }
public static void Main(string[] args) { Console.SetWindowSize(125, 29); Http2Logger.WriteToFile = false; _sessions = new Dictionary <string, Http2SessionHandler>(); _environment = ArgsHelper.GetEnvironment(args); var isTestsEnabled = ConfigurationManager.AppSettings["testModeEnabled"] == "true"; var waitForTestsFinish = new ManualResetEvent(!isTestsEnabled); Console.WriteLine(); Console.WriteLine(); Http2Logger.LogDebug("Tests enabled: " + isTestsEnabled); ThreadPool.SetMaxThreads(10, 10); var uri = ArgsHelper.TryGetUri(args); if (!isTestsEnabled) { HelpDisplayer.ShowMainMenuHelp(); Console.WriteLine("Enter command"); } do { try { Command cmd; string command; if (uri != null) { command = Verbs.Get + " " + uri; //TODO set up method correctly } else { Console.Write(">"); command = Console.ReadLine(); } try { cmd = CommandParser.CommandParser.Parse(command); } catch (Exception ex) { Http2Logger.LogError(ex.Message); continue; } //Scheme and port were checked during parsing get cmd. switch (cmd.GetCmdType()) { case CommandType.Put: case CommandType.Post: case CommandType.Get: case CommandType.Delete: case CommandType.Dir: Http2Logger.LogConsole("Uri command detected"); var uriCmd = (IUriCommand)cmd; string method = uriCmd.Method; //Only unique sessions can be opened if (_sessions.ContainsKey(uriCmd.Uri.Authority)) { Http2Logger.LogConsole("Session already exists"); _sessions[uriCmd.Uri.Authority].SendRequestAsync(uriCmd.Uri, method); break; } Http2Logger.LogConsole("Creating new session"); var sessionHandler = new Http2SessionHandler(_environment); _sessions.Add(uriCmd.Uri.Authority, sessionHandler); sessionHandler.OnClosed += (sender, eventArgs) => { _sessions.Remove(sessionHandler.ServerUri); Http2Logger.LogDebug("Session deleted from collection: " + sessionHandler.ServerUri); waitForTestsFinish.Set(); }; //Get cmd is equivalent for connect -> get. This means, that each get request //will open new session. Console.WriteLine(uriCmd.Uri.ToString()); bool success = sessionHandler.Connect(uriCmd.Uri); if (!success) { Http2Logger.LogError("Connection failed"); break; } if (!sessionHandler.WasHttp1Used) { sessionHandler.StartConnection(); if (sessionHandler.Protocol != SslProtocols.None) { sessionHandler.SendRequestAsync(uriCmd.Uri, method); } } break; case CommandType.Help: ((HelpCommand)cmd).ShowHelp.Invoke(); break; case CommandType.Ping: string url = ((PingCommand)cmd).Uri.Authority; if (_sessions.ContainsKey(url)) { _sessions[url].Ping(); } else { Http2Logger.LogError("Can't ping until session is opened."); } break; case CommandType.Exit: var sessionsDictCopy = new Dictionary <string, Http2SessionHandler>(_sessions); foreach (var sessionUri in sessionsDictCopy.Keys) { sessionsDictCopy[sessionUri].Dispose(false); } sessionsDictCopy.Clear(); return; } } catch (Exception e) { Http2Logger.LogError("Problems occurred - please restart client. Error: " + e.Message); } } while (!isTestsEnabled); waitForTestsFinish.WaitOne(5000); Http2Logger.LogDebug("Exiting"); Console.WriteLine(); Console.WriteLine(); }
public static void Main(string[] args) { Console.SetWindowSize(125, 29); Http2Logger.WriteToFile = false; _sessions = new Dictionary <string, Http2SessionHandler>(); var argsList = new List <string>(args); _environment = new Dictionary <string, object> { { "useHandshake", !argsList.Contains("-no-handshake") }, { "usePriorities", !argsList.Contains("-no-priorities") }, { "useFlowControl", !argsList.Contains("-no-flowcontrol") }, }; HelpDisplayer.ShowMainMenuHelp(); ThreadPool.SetMaxThreads(10, 10); Console.WriteLine("Enter command"); while (true) { try { Console.Write(">"); string command = Console.ReadLine(); Command cmd; try { cmd = CommandParser.Parse(command); } catch (Exception ex) { Http2Logger.LogError(ex.Message); continue; } //Scheme and port were checked during parsing get cmd. switch (cmd.GetCmdType()) { case CommandType.Put: case CommandType.Post: case CommandType.Get: case CommandType.Delete: case CommandType.Dir: var uriCmd = (IUriCommand)cmd; string method = uriCmd.Method; string localPath = null; string serverPostAct = null; if (cmd is PostCommand) { localPath = (cmd as PostCommand).LocalPath; serverPostAct = (cmd as PostCommand).ServerPostAct; } else if (cmd is PutCommand) { localPath = (cmd as PutCommand).LocalPath; } //Only unique sessions can be opened if (_sessions.ContainsKey(uriCmd.Uri.Authority)) { _sessions[uriCmd.Uri.Authority].SendRequestAsync(uriCmd.Uri, method, localPath, serverPostAct); break; } var sessionHandler = new Http2SessionHandler(_environment); _sessions.Add(uriCmd.Uri.Authority, sessionHandler); sessionHandler.OnClosed += (sender, eventArgs) => _sessions.Remove(sessionHandler.ServerUri); //Get cmd is equivalent for connect -> get. This means, that each get request //will open new session. bool success = sessionHandler.Connect(uriCmd.Uri); if (!success) { Http2Logger.LogError("Connection failed"); break; } Task.Run(() => sessionHandler.StartConnection()); using (var waitForConnectionStart = new ManualResetEvent(false)) { waitForConnectionStart.WaitOne(200); } sessionHandler.SendRequestAsync(uriCmd.Uri, method, localPath, serverPostAct); break; case CommandType.Help: ((HelpCommand)cmd).ShowHelp.Invoke(); break; case CommandType.Ping: string url = ((PingCommand)cmd).Uri.Authority; if (_sessions.ContainsKey(url)) { _sessions[url].Ping(); } else { Http2Logger.LogError("Can't ping until session is opened."); } break; case CommandType.Exit: foreach (var sessionUri in _sessions.Keys) { _sessions[sessionUri].Dispose(false); } _sessions.Clear(); return; } } catch (Exception e) { Http2Logger.LogError("Problems occured - please restart client. Error: " + e.Message); } } }
public bool Connect(Uri connectUri) { _path = connectUri.PathAndQuery; _version = Protocols.Http2; _scheme = connectUri.Scheme; _host = connectUri.Host; _port = connectUri.Port; ServerUri = connectUri.Authority; if (_clientSession != null) { return(false); } try { int port = connectUri.Port; int securePort; if (!int.TryParse(ConfigurationManager.AppSettings["securePort"], out securePort)) { Http2Logger.LogError("Incorrect port in the config file!"); return(false); } //Connect alpn extension, set known protocols var extensions = new[] { ExtensionType.Renegotiation, ExtensionType.ALPN }; _options = port == securePort ? new SecurityOptions(SecureProtocol.Tls1, extensions, new[] { Protocols.Http1, Protocols.Http2 }, ConnectionEnd.Client) : new SecurityOptions(SecureProtocol.None, extensions, new[] { Protocols.Http1, Protocols.Http2 }, ConnectionEnd.Client); _options.VerificationType = CredentialVerification.None; _options.Certificate = Org.Mentalis.Security.Certificates.Certificate.CreateFromCerFile(CertificatePath); _options.Flags = SecurityFlags.Default; _options.AllowedAlgorithms = SslAlgorithms.RSA_AES_256_SHA | SslAlgorithms.NULL_COMPRESSION; _socket = new SecureSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp, _options); IDictionary <string, object> handshakeResult = null; using (var monitor = new ALPNExtensionMonitor()) { monitor.OnProtocolSelected += (o, args) => { _selectedProtocol = args.SelectedProtocol; }; _socket.Connect(new DnsEndPoint(connectUri.Host, connectUri.Port), monitor); if (_useHandshake) { var handshakeEnvironment = MakeHandshakeEnvironment(_socket); //Handshake manager determines what handshake must be used: upgrade or secure handshakeResult = HandshakeManager.GetHandshakeAction(handshakeEnvironment).Invoke(); Http2Logger.LogDebug("Handshake finished"); if (_selectedProtocol == Protocols.Http1) { _useHttp20 = false; return(true); } } } SendSessionHeader(); _useHttp20 = true; _clientSession = new Http2Session(_socket, ConnectionEnd.Client, _usePriorities, _useFlowControl, handshakeResult); //For saving incoming data _clientSession.OnFrameReceived += FrameReceivedHandler; _clientSession.OnRequestSent += RequestSentHandler; _clientSession.OnSessionDisposed += (sender, args) => Dispose(false); } catch (Http2HandshakeFailed ex) { if (ex.Reason == HandshakeFailureReason.InternalError) { _useHttp20 = false; } else { Http2Logger.LogError("Specified server did not respond"); Dispose(true); return(false); } } catch (SocketException) { Http2Logger.LogError("Check if any server listens port " + connectUri.Port); Dispose(true); return(false); } catch (Exception ex) { Http2Logger.LogError("Unknown connection exception was caught: " + ex.Message); Dispose(true); return(false); } return(true); }
public IDictionary <string, object> Handshake() { var response = new HandshakeResponse(); _readThread = new Thread((object state) => { var handle = state as EventWaitHandle; try { response = Read11Headers(); _wasResponseReceived = true; if (handle != null) { handle.Set(); } } catch (Exception ex) { Http2Logger.LogError(ex.Message); // singal that there is an error if (handle != null) { handle.Set(); } _error = ex; throw; } }); _readThread.IsBackground = true; _readThread.Start(_responseReceivedRaised); if (_end == ConnectionEnd.Client) { // Build the request var builder = new StringBuilder(); builder.AppendFormat("{0} {1} {2}\r\n", "GET", _headers[":path"], "HTTP/1.1"); //TODO pass here requested filename builder.AppendFormat("Host: {0}\r\n", _headers[":host"]); builder.Append("Connection: Upgrade, Http2-Settings\r\n"); builder.Append("Upgrade: HTTP-DRAFT-04/2.0\r\n"); builder.Append("Http2-Settings: "); //TODO check out how to send window size and max_conc_streams if (_headers != null) { var http2Settings = new StringBuilder(); foreach (var key in _headers.Keys) { if (!string.Equals(":path", key, StringComparison.OrdinalIgnoreCase)) { http2Settings.AppendFormat("{0}: {1}\r\n", key, _headers[key]); } } byte[] settingsBytes = Encoding.UTF8.GetBytes(http2Settings.ToString()); builder.Append(Convert.ToBase64String(settingsBytes)); } builder.Append("\r\n\r\n"); byte[] requestBytes = Encoding.UTF8.GetBytes(builder.ToString()); InternalSocket.Send(requestBytes, 0, requestBytes.Length, SocketFlags.None); _responseReceivedRaised.WaitOne(Timeout); _responseReceivedRaised.Dispose(); } else { _responseReceivedRaised.WaitOne(Timeout); _responseReceivedRaised.Dispose(); if (response.Result == HandshakeResult.Upgrade) { const string status = "101"; const string protocol = "HTTP/1.1"; const string postfix = "Switching Protocols"; var builder = new StringBuilder(); builder.AppendFormat("{0} {1} {2}\r\n", protocol, status, postfix); builder.Append("Connection: Upgrade\r\n"); builder.Append("Upgrade: HTTP-draft-04/2.0\r\n"); builder.Append("\r\n"); byte[] requestBytes = Encoding.ASCII.GetBytes(builder.ToString()); InternalSocket.Send(requestBytes, 0, requestBytes.Length, SocketFlags.None); } } if (!_wasResponseReceived) { if (_readThread.IsAlive) { _readThread.Abort(); _readThread.Join(); } throw new Http2HandshakeFailed(HandshakeFailureReason.Timeout); } if (_error != null) { throw _error; } if (response.Result != HandshakeResult.Upgrade) { throw new Http2HandshakeFailed(HandshakeFailureReason.InternalError); } if (_readThread.IsAlive) { _readThread.Abort(); } _readThread.Join(); return(_handshakeResult); }
/// <summary> /// Get files via http request. /// </summary> /// <param name="content">Downloaded file.</param> /// <param name="type">Type of file.</param> /// <param name="requestUri">Full name of file.</param> /// <param name="uri">The address site.</param> private void PrivateGetFile(byte[] content, string type, Uri requestUri, string uri) { byte[] headers = this.GetHeaders(content); if (headers == null) { Http2Logger.LogError("HTTP response: Invalid"); return; } this.httpMonitor.LogResponse(headers, content.Length); int status = this.GetStatus(headers); if (useHttp2Handshake) { Http2Logger.LogDebug(Encoding.UTF8.GetString(headers)); } Http2Logger.LogInfo(string.Format("HTTP response: {0}, length: {1} name:{2}", status, content.LongLength, uri)); if (status == 200 || status == 101) { string url = requestUri.Scheme + "://" + requestUri.Authority; string directory = string.Empty; string localDir = string.Empty; string file = requestUri.LocalPath; string localFile = Path.GetFileName(uri); for (int i = 0; i < requestUri.Segments.Length - 1; i++) { directory += requestUri.Segments[i]; localDir += requestUri.Segments[i].Replace('/', '\\'); } if (!string.IsNullOrEmpty(localDir)) { if (localDir[0] == '\\') { localDir = '.' + localDir; } Directory.CreateDirectory(localDir); localFile = localDir + '\\' + localFile; } int contentOffset = headers.Length; using (var fs = new FileStream(localFile, FileMode.Create)) { fs.Write(content, contentOffset, content.Length - contentOffset); } if (type == ContentTypes.TextHtml) { string strContent = Encoding.UTF8.GetString(content, contentOffset, content.Length - contentOffset) .Replace("http2frame_start\r\n", "") .Replace("http2frame_end", ""); XHtmlDocument document = XHtmlDocument.Parse(strContent); string path = url + directory; foreach (var image in document.Images) { this.AddNameToDownloadList(string.Format("{0}/{1}", path.ToLower(), image.ToLower())); } foreach (var link in document.Links) { this.AddNameToDownloadList(string.Format("{0}/{1}", path.ToLower(), link.ToLower())); } foreach (var script in document.Scripts) { this.AddNameToDownloadList(string.Format("{0}/{1}", path.ToLower(), script.ToLower())); } } } // if status 200 }
private void HandleAcceptedClient(SecureSocket incomingClient) { bool backToHttp11 = false; string alpnSelectedProtocol = Protocols.Http2; var handshakeEnvironment = MakeHandshakeEnvironment(incomingClient); IDictionary <string, object> handshakeResult = null; //Think out smarter way to get handshake result. //DO NOT change Middleware function. If you will do so, server will not even launch. (It's owin's problem) Func <Task> handshakeAction = () => { var handshakeTask = new Task(() => { handshakeResult = HandshakeManager.GetHandshakeAction(handshakeEnvironment).Invoke(); }); return(handshakeTask); }; if (_useHandshake) { var environment = new Dictionary <string, object> { //Sets the handshake action depends on port. { "HandshakeAction", handshakeAction }, }; try { var handshakeTask = _next(environment); handshakeTask.Start(); if (!handshakeTask.Wait(6000)) { incomingClient.Close(); Http2Logger.LogError("Handshake timeout. Connection dropped."); return; } alpnSelectedProtocol = incomingClient.SelectedProtocol; } catch (Http2HandshakeFailed ex) { if (ex.Reason == HandshakeFailureReason.InternalError) { backToHttp11 = true; } else { incomingClient.Close(); Http2Logger.LogError("Handshake timeout. Client was disconnected."); return; } } catch (Exception e) { Http2Logger.LogError("Exception occured. Closing client's socket. " + e.Message); incomingClient.Close(); return; } } try { HandleRequest(incomingClient, alpnSelectedProtocol, backToHttp11, handshakeResult); } catch (Exception e) { Http2Logger.LogError("Exception occured. Closing client's socket. " + e.Message); incomingClient.Close(); } }