/// <summary> /// Initializes a new instance of the <see cref="HttpConnection"/> class. /// </summary> /// <param name="server">The server.</param> /// <param name="id">The identifier.</param> /// <param name="client">The client.</param> public HttpConnection(HttpServer server, Guid id, TcpClient client) { this.client = client; this.address = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString(); this.server = server; this.id = id; this.protocol = HttpProtocol.HTTP; // process Utilities.InvokeAsync(Process); }
public TargetResult() { Url = string.Empty; Html = string.Empty; ResponseHeaders = new NameValueCollection(); Protocol = HttpProtocol.HTTP; Banner = string.Empty; WebServer = string.Empty; Success = false; Hostname = string.Empty; }
/// <summary> /// Basic emitter to send synchronous HTTP requests /// </summary> /// <param name="endpoint">Collector domain</param> /// <param name="protocol">HttpProtocol.HTTP or HttpProtocol.HTTPS</param> /// <param name="port">Port to connect to</param> /// <param name="method">HttpMethod.GET or HttpMethod.POST</param> /// <param name="bufferSize">Maximum number of events queued before the buffer is flushed automatically. /// Defaults to 10 for POST requests and 1 for GET requests.</param> /// <param name="onSuccess">Callback executed when every request in a flush has status code 200. /// Gets passed the number of events flushed.</param> /// <param name="onFailure">Callback executed when not every request in a flush has status code 200. /// Gets passed the number of events sent successfully and a list of unsuccessful events.</param> /// <param name="offlineModeEnabled">Whether to store unsent requests using MSMQ</param> public Emitter(string endpoint, HttpProtocol protocol = HttpProtocol.HTTP, int? port = null, HttpMethod method = HttpMethod.GET, int? bufferSize = null, Action<int> onSuccess = null, Action<int, List<Dictionary<string, string>>> onFailure = null, bool offlineModeEnabled = true) { collectorUri = GetCollectorUri(endpoint, protocol, port, method); this.method = method; this.buffer = new List<Dictionary<string, string>>(); this.bufferSize = Math.Max(1, bufferSize ?? (method == HttpMethod.GET ? 1 : 10)); this.onSuccess = onSuccess; this.onFailure = onFailure; this.OfflineModeEnabled = offlineModeEnabled; Log.Logger.Info(String.Format("{0} initialized with endpoint {1}", this.GetType(), collectorUri)); }
public ProxyHttpProtocol(Control dispatcher, IUrlFilter urlFilter) { ArgumentUtility.CheckNotNull ("dispatcher", dispatcher); ArgumentUtility.CheckNotNull ("urlFilter", urlFilter); _dispatcher = dispatcher; _urlFilter = urlFilter; _dispatcher.Dispatcher.Invoke ( () => { var originalHttpHandler = new HttpProtocol(); _wrapped = (IInternetProtocol) originalHttpHandler; }); }
/// <summary> /// Converts the <see cref="sourceValue" /> parameter to the <see cref="destinationType" /> parameter using <see cref="formatProvider" /// /> and <see cref="ignoreCase" /> /// </summary> /// <param name="sourceValue">the <see cref="System.Object"/> to convert from</param> /// <param name="destinationType">the <see cref="System.Type" /> to convert to</param> /// <param name="formatProvider">not used by this TypeConverter.</param> /// <param name="ignoreCase">when set to <c>true</c>, will ignore the case when converting.</param> /// <returns> /// an instance of <see cref="HttpProtocol" />, or <c>null</c> if there is no suitable conversion. /// </returns> public override object ConvertFrom(object sourceValue, global::System.Type destinationType, global::System.IFormatProvider formatProvider, bool ignoreCase) => HttpProtocol.CreateFrom(sourceValue);
public HttpClient(string hostName, int port = -1, HttpProtocol protocol = HttpProtocol.Auto) { Connection = HttpConnectionStream.ConnectToServer(hostName, port, protocol); RawStream = Connection; }
/// <summary> Construct a parser using the provided HttpProtocol. /// A feedback object printing to System.Console is used. /// </summary> /// <param name="connection">HttpProtocol object. /// </param> /// <throws> ParserException If the creation of the underlying Lexer </throws> /// <summary> cannot be performed. /// </summary> public Parser(HttpProtocol connection) : this(connection, STDOUT) { }
public void GetHttpProtocol_ThrowErrorForUnknownVersion(Version version) { Assert.Throws <ArgumentOutOfRangeException>(() => HttpProtocol.GetHttpProtocol(version)); }
public static string ToSerialString(this HttpProtocol value) => value switch {
/// <summary> /// Inits the Snowplow Tracker; after this point it can be accessed globally. /// </summary> /// <param name="emitterUri">The emitter URI</param> /// <param name="protocol">The protocol to use</param> /// <param name="port">The port the collector is on</param> /// <param name="method">The method to use</param> /// <param name="useClientSession">Whether to enable client session</param> /// <param name="useMobileContext">Whether to enable mobile contexts</param> /// <param name="useGeoLocationContext">Whether to enable geo-location contexts</param> public static void Init( string emitterUri, HttpProtocol protocol = HttpProtocol.HTTP, int?port = null, HttpMethod method = HttpMethod.GET, bool useClientSession = false, bool useMobileContext = false, bool useGeoLocationContext = false) { var logger = new ConsoleLogger(); var dest = new SnowplowHttpCollectorEndpoint(emitterUri, method: method, port: port, protocol: protocol, l: logger); // Note: Maintain reference to Storage as this will need to be disposed of manually _storage = new LiteDBStorage(SnowplowTrackerPlatformExtension.Current.GetLocalFilePath("events.db")); var queue = new PersistentBlockingQueue(_storage, new PayloadToJsonString()); // Note: When using GET requests the sendLimit equals the number of concurrent requests - to many of these will break your application! var sendLimit = method == HttpMethod.GET ? 10 : 100; // Note: To make the tracker more battery friendly and less likely to drain batteries there are two settings to take note of here: // 1. The stopPollIntervalMs: Controls how often we look to the database for more events // 2. The deviceOnlineMethod: Is run before going to the database or attempting to send events, this will prevent any I/O from // occurring unless you have an active network connection var emitter = new AsyncEmitter(dest, queue, sendLimit: sendLimit, stopPollIntervalMs: 1000, sendSuccessMethod: EventSuccessCallback, deviceOnlineMethod: SnowplowTrackerPlatformExtension.Current.IsDeviceOnline, l: logger); var userId = PropertyManager.GetStringValue(KEY_USER_ID, SnowplowCore.Utils.GetGUID()); PropertyManager.SaveKeyValue(KEY_USER_ID, userId); var subject = new Subject() .SetPlatform(Platform.Mob) .SetUserId(userId) .SetLang("en"); if (useClientSession) { _clientSession = new ClientSession(SnowplowTrackerPlatformExtension.Current.GetLocalFilePath("client_session.dict"), l: logger); } // Note: You can either attach contexts to each event individually or for the more common contexts such as Desktop, Mobile and GeoLocation // you can pass a delegate method which will then be called for each event automatically. MobileContextDelegate mobileContextDelegate = null; if (useMobileContext) { mobileContextDelegate = SnowplowTrackerPlatformExtension.Current.GetMobileContext; } GeoLocationContextDelegate geoLocationContextDelegate = null; if (useMobileContext) { geoLocationContextDelegate = SnowplowTrackerPlatformExtension.Current.GetGeoLocationContext; } // Attach the created objects and begin all required background threads! Instance.Start(emitter: emitter, subject: subject, clientSession: _clientSession, trackerNamespace: _trackerNamespace, appId: _appId, encodeBase64: false, synchronous: false, mobileContextDelegate: mobileContextDelegate, geoLocationContextDelegate: geoLocationContextDelegate, l: logger); // Reset session counters SessionMadeCount = 0; SessionSuccessCount = 0; SessionFailureCount = 0; }
public void RequestQueuedStop(HttpProtocol httpProtocol, string httpVersion) { Interlocked.Decrement(ref _httpRequestQueueLength); }
protected MessageBody(HttpProtocol context) { _context = context; }
private TargetResult Connect(HttpProtocol Protocol, string IPAddress, int Port) { TargetResult Result = new TargetResult(); string Address = string.Empty; if (Protocol == HttpProtocol.HTTP) Address = "http://" + IPAddress + ":" + Port.ToString(); else if (Protocol == HttpProtocol.HTTPs) Address = "https://" + IPAddress + ":" + Port.ToString(); CreateWebrequest webRequest = new CreateWebrequest(); Result.Html = webRequest.StringGetWebPage(Address, string.Empty); Result.Url = Address; Result.Protocol = Protocol; if (webRequest.Response != null) { Result.ResponseHeaders = webRequest.Response.Headers; Result.Banner = webRequest.Response.Server; Result.WebServer = GetWebServer(webRequest.Response.Server); IPHostEntry Hostname = Dns.GetHostEntry(IPAddress); Result.Hostname = Hostname.HostName; Result.Success = true; Result.StatusCode = webRequest.Response.StatusCode; } else { Result.Success = false; } return Result; }
public Target() { Address = string.Empty; Port = -1; Protocol = HttpProtocol.HTTP; }
private void _OnLoginRes(Http.HttpRequestHandler.NetworkMsgType type, string message, AbstractHttpProtocol proto) { if (type == Http.HttpRequestHandler.NetworkMsgType.network) { //网络出现问题 _RaiseNetworkErrorEvent(); return; } int errorCode = ErrorCode.SERVICE_ERROR; if (proto != null) { HttpProtocol <User_LoginOrRegister_Request, User_LoginOrRegister_Response> p = proto as HttpProtocol <User_LoginOrRegister_Request, User_LoginOrRegister_Response>; if (p != null) { errorCode = p.error_code; User_LoginOrRegister_Response res = p.GetResMsg(); if (p.error_code == ErrorCode.SUCCESS && res != null && res.uid > 0) { PVPGlobal.isLogined = true; PVPGlobal.userInfo = new User(res); //登陆成功 _StartSocketTcpConnect(res); } else { PVPGlobal.isLogined = false; } } } if (ICM.handlerRegister != null && ICM.handlerRegister.loginOrRegisterEventHandler != null) { ICM.handlerRegister.loginOrRegisterEventHandler(errorCode); } }
/// <summary> /// 用户登陆 /// </summary> /// <param name="loginType">Login type.</param> /// <param name="account">Account.</param> /// <param name="password">Password.</param> /// <param name="facebookAccessToken">Facebook access token.</param> /// <param name="timeout">Timeout.</param> public void Login(LoginType loginType, string account = "", string password = "", string facebookAccessToken = "", float timeout = 5) { this.Close(); string uniqueIdentifier = SystemInfo.deviceUniqueIdentifier; PVPProtobuf_Token.User_LoginOrRegister_Request loginRequest = new PVPProtobuf_Token.User_LoginOrRegister_Request(); loginRequest.login_type = (Int32)loginType; loginRequest.account = account; loginRequest.password = password; if (loginType == LoginType.Guest) { loginRequest.unique_identifier = SystemInfo.deviceUniqueIdentifier; } else { loginRequest.unique_identifier = ""; } loginRequest.facebook_access_token = facebookAccessToken; if (String.IsNullOrEmpty(Config.appKey)) { throw new Exception("You have not init pvp sdk , please call PVPGlobal.Init(string appKey) and set appKey. "); } loginRequest.app_key = Config.appKey; #if UNITY_ANDROID loginRequest.device_type = (int)DeviceType.Android; #elif UNITY_IOS loginRequest.device_type = (int)DeviceType.IOS; #else loginRequest.device_type = (int)DeviceType.OTHER; #endif HttpProtocol <User_LoginOrRegister_Request, User_LoginOrRegister_Response> loginProtocol = new HttpProtocol <User_LoginOrRegister_Request, User_LoginOrRegister_Response> (); loginProtocol.SetReqMsg(loginRequest); Http.HttpRequestHandler h = new Http.HttpRequestHandler(); h.PostRequest(loginProtocol, _OnLoginRes); }
protected StartLine(HttpProtocol protocol) { Protocol = protocol; }
internal void ParseCorrectHttpVersion(string httpVersionText, HttpProtocol expectedProtocol) { var header = new HttpRequestHeader($"GET /myFile {httpVersionText}"); Assert.Equal(header.Protocol, expectedProtocol); }
public static HttpResponse SendRequest(HttpRequest request, string hostName, int port = -1, HttpProtocol protocol = HttpProtocol.Auto) { var client = new HttpClient(hostName, port, protocol); return(client.SendRequest(request)); }
public void RequestUpgradedStop(HttpProtocol httpProtocol) { Interlocked.Decrement(ref _currentUpgradedHttpRequests); }
/// <summary> /// Asynchronous emitter to send non-blocking HTTP requests /// </summary> /// <param name="endpoint">Collector domain</param> /// <param name="protocol">HttpProtocol.HTTP or HttpProtocol.HTTPS</param> /// <param name="port">Port to connect to</param> /// <param name="method">HttpMethod.GET or HttpMethod.POST</param> /// <param name="bufferSize">Maximum number of events queued before the buffer is flushed automatically. /// Defaults to 10 for POST requests and 1 for GET requests.</param> /// <param name="onSuccess">Callback executed when every request in a flush has status code 200. /// Gets passed the number of events flushed.</param> /// <param name="onFailure">Callback executed when not every request in a flush has status code 200. /// Gets passed the number of events sent successfully and a list of unsuccessful events.</param> /// <param name="offlineModeEnabled">Whether to store unsent requests using MSMQ</param> public AsyncEmitter(string endpoint, HttpProtocol protocol = HttpProtocol.HTTP, int?port = null, HttpMethod method = HttpMethod.GET, int?bufferSize = null, Action <int> onSuccess = null, Action <int, List <Dictionary <string, string> > > onFailure = null, bool offlineModeEnabled = true) : base(endpoint, protocol, port, method, bufferSize, onSuccess, onFailure, offlineModeEnabled) { tasks = new List <Task>(); }
public StatusLine(HttpProtocol protocol, HttpStatusCode status) : base(protocol) { StatusCode = status; }
private void HttpInitialize(String url) { HttpProtocol httpProtocol = new HttpProtocol(url); }
/// <summary> /// Start processing the request. /// </summary> /// <returns></returns> internal Task <HttpContext> SendAsync(CancellationToken cancellationToken) { var registration = cancellationToken.Register(ClientInitiatedAbort); // Everything inside this function happens in the SERVER's execution context (unless PreserveExecutionContext is true) async Task RunRequestAsync() { // HTTP/2 specific features must be added after the request has been configured. if (HttpProtocol.IsHttp2(_httpContext.Request.Protocol)) { _httpContext.Features.Set <IHttpResetFeature>(this); } // This will configure IHttpContextAccessor so it needs to happen INSIDE this function, // since we are now inside the Server's execution context. If it happens outside this cont // it will be lost when we abandon the execution context. _testContext = _application.CreateContext(_httpContext.Features); try { await _application.ProcessRequestAsync(_testContext); // Determine whether request body was complete when the delegate exited. // This could throw an error if there was a pending server read. Needs to // happen before completing the response so the response returns the error. var requestBodyInProgress = RequestBodyReadInProgress(); if (requestBodyInProgress) { // If request is still in progress then abort it. CancelRequestBody(); } // Matches Kestrel server: response is completed before request is drained await CompleteResponseAsync(); if (!requestBodyInProgress) { // Writer was already completed in send request callback. await _requestPipe.Reader.CompleteAsync(); // Don't wait for request to drain. It could block indefinitely. In a real server // we would wait for a timeout and then kill the socket. // Potential future improvement: add logging that the request timed out } _application.DisposeContext(_testContext, exception: null); } catch (Exception ex) { Abort(ex); _application.DisposeContext(_testContext, ex); } finally { registration.Dispose(); } } // Async offload, don't let the test code block the caller. if (_preserveExecutionContext) { _ = Task.Factory.StartNew(RunRequestAsync); } else { ThreadPool.UnsafeQueueUserWorkItem(_ => { _ = RunRequestAsync(); }, null); } return(_responseTcs.Task); }
public void RequestStart(HttpProtocol httpProtocol) { if (IsEnabled()) { Core(httpProtocol); }
/// <summary> Constructor for custom HTTP access. /// This would be used to create a parser for a URLConnection that needs /// a special setup or negotiation conditioning beyond what is available /// from the {@link #getConnectionManager ConnectionManager}. /// </summary> /// <param name="connection">A fully conditioned connection. The connect() /// method will be called so it need not be connected yet. /// </param> /// <param name="fb">The object to use for message communication. /// </param> /// <throws> ParserException If the creation of the underlying Lexer </throws> /// <summary> cannot be performed. /// </summary> public Parser(HttpProtocol connection, IParserFeedBack fb) : this(new Lexer(connection), fb) { this.m_obHttpProtocol = connection; }
/// <summary> /// Proxies a normal (i.e. non-upgradable) request to the upstream server, and the response back to our client. /// </summary> /// <remarks> /// Normal proxying comprises the following steps: /// (0) Disable ASP .NET Core limits for streaming requests /// (1) Create outgoing HttpRequestMessage /// (2) Setup copy of request body (background) Downstream --► Proxy --► Upstream /// (3) Copy request headers Downstream --► Proxy --► Upstream /// (4) Send the outgoing request using HttpMessageInvoker Downstream --► Proxy --► Upstream /// (5) Copy response status line Downstream ◄-- Proxy ◄-- Upstream /// (6) Copy response headers Downstream ◄-- Proxy ◄-- Upstream /// (7) Copy response body Downstream ◄-- Proxy ◄-- Upstream /// (8) Copy response trailer headers and finish response Downstream ◄-- Proxy ◄-- Upstream /// (9) Wait for completion of step 2: copying request body Downstream --► Proxy --► Upstream /// /// ASP .NET Core (Kestrel) will finally send response trailers (if any) /// after we complete the steps above and relinquish control. /// </remarks> private async Task NormalProxyAsync( HttpContext context, Uri targetUri, HttpMessageInvoker httpClient, ProxyTelemetryContext proxyTelemetryContext, CancellationToken shortCancellation, CancellationToken longCancellation) { Contracts.CheckValue(context, nameof(context)); Contracts.CheckValue(targetUri, nameof(targetUri)); Contracts.CheckValue(httpClient, nameof(httpClient)); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 0: Disable ASP .NET Core limits for streaming requests var isIncomingHttp2 = HttpProtocol.IsHttp2(context.Request.Protocol); // NOTE: We heuristically assume gRPC-looking requests may require streaming semantics. // See https://github.com/microsoft/reverse-proxy/issues/118 for design discussion. var isStreamingRequest = isIncomingHttp2 && GrpcProtocolHelper.IsGrpcContentType(context.Request.ContentType); if (isStreamingRequest) { DisableMinRequestBodyDataRateAndMaxRequestBodySize(context); } // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 1: Create outgoing HttpRequestMessage var upstreamRequest = new HttpRequestMessage(HttpUtilities.GetHttpMethod(context.Request.Method), targetUri) { // We request HTTP/2, but HttpClient will fallback to HTTP/1.1 if it cannot establish HTTP/2 with the target. // This is done without extra round-trips thanks to ALPN. We can detect a downgrade after calling HttpClient.SendAsync // (see Step 3 below). TBD how this will change when HTTP/3 is supported. Version = Http2Version, }; // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 2: Setup copy of request body (background) Downstream --► Proxy --► Upstream // Note that we must do this before step (3) because step (3) may also add headers to the HttpContent that we set up here. var bodyToUpstreamContent = SetupCopyBodyUpstream(context.Request.Body, upstreamRequest, in proxyTelemetryContext, isStreamingRequest, longCancellation); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 3: Copy request headers Downstream --► Proxy --► Upstream CopyHeadersToUpstream(context, upstreamRequest); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 4: Send the outgoing request using HttpClient ////this.logger.LogInformation($" Starting Proxy --> upstream request"); var upstreamResponse = await httpClient.SendAsync(upstreamRequest, shortCancellation); // Detect connection downgrade, which may be problematic for e.g. gRPC. if (isIncomingHttp2 && upstreamResponse.Version.Major != 2) { // TODO: Do something on connection downgrade... Log.HttpDowngradeDeteced(_logger); } // Assert that, if we are proxying content upstream, it must have started by now // (since HttpClient.SendAsync has already completed asynchronously). // If this check fails, there is a coding defect which would otherwise // cause us to wait forever in step 9, so fail fast here. if (bodyToUpstreamContent != null && !bodyToUpstreamContent.Started) { // TODO: bodyToUpstreamContent is never null. HttpClient might would not need to read the body in some scenarios, such as an early auth failure with Expect: 100-continue. throw new InvalidOperationException("Proxying the downstream request body to the upstream server hasn't started. This is a coding defect."); } // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 5: Copy response status line Downstream ◄-- Proxy ◄-- Upstream ////this.logger.LogInformation($" Setting downstream <-- Proxy status: {(int)upstreamResponse.StatusCode} {upstreamResponse.ReasonPhrase}"); context.Response.StatusCode = (int)upstreamResponse.StatusCode; context.Features.Get <IHttpResponseFeature>().ReasonPhrase = upstreamResponse.ReasonPhrase; // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 6: Copy response headers Downstream ◄-- Proxy ◄-- Upstream CopyHeadersToDownstream(upstreamResponse, context.Response.Headers); // NOTE: it may *seem* wise to call `context.Response.StartAsync()` at this point // since it looks like we are ready to send back response headers // (and this might help reduce extra delays while we wait to receive the body from upstream). // HOWEVER, this would produce the wrong result if it turns out that there is no content // from the upstream -- instead of sending headers and terminating the stream at once, // we would send headers thinking a body may be coming, and there is none. // This is problematic on gRPC connections when the upstream server encounters an error, // in which case it immediately returns the response headers and trailing headers, but no content, // and clients misbehave if the initial headers response does not indicate stream end. // TODO: Some of the tasks in steps (7) - (9) may go unobserved depending on what fails first. Needs more consideration. // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 7: Copy response body Downstream ◄-- Proxy ◄-- Upstream await CopyBodyDownstreamAsync(upstreamResponse.Content, context.Response.Body, proxyTelemetryContext, longCancellation); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 8: Copy response trailer headers and finish response Downstream ◄-- Proxy ◄-- Upstream CopyTrailingHeadersToDownstream(upstreamResponse, context); if (isStreamingRequest) { // NOTE: We must call `CompleteAsync` so that Kestrel will flush all bytes to the client. // In the case where there was no response body, // this is also when headers and trailing headers are sent to the client. // Without this, the client might wait forever waiting for response bytes, // while we might wait forever waiting for request bytes, // leading to a stuck connection and no way to make progress. await context.Response.CompleteAsync(); } // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 9: Wait for completion of step 2: copying request body Downstream --► Proxy --► Upstream if (bodyToUpstreamContent != null) { ////this.logger.LogInformation($" Waiting for downstream --> Proxy --> upstream body proxying to complete"); await bodyToUpstreamContent.ConsumptionTask; } }
public void GetHttpProtocol_CorrectIETFVersion(Version version, string expected) { var actual = HttpProtocol.GetHttpProtocol(version); Assert.Equal(expected, actual); }
internal Request(IServer server, IEndPoint endPoint, IClientConnection client, IClientConnection localClient, HttpProtocol protocol, FlexibleRequestMethod method, RoutingTarget target, IHeaderCollection headers, ICookieCollection?cookies, IForwardingCollection?forwardings, IRequestQuery?query, Stream?content) { Client = client; LocalClient = localClient; Server = server; EndPoint = endPoint; ProtocolType = protocol; Method = method; Target = target; _Cookies = cookies; _Forwardings = forwardings; _Query = query; Headers = headers; Content = content; }
public void IsHttp10_Success(string protocol, bool match) { Assert.Equal(match, HttpProtocol.IsHttp10(protocol)); }
private static string GetCollectorUri(string endpoint, HttpProtocol protocol, int? port, HttpMethod method) { string path; string requestProtocol = (protocol == HttpProtocol.HTTP) ? "http" : "https"; if (method == HttpMethod.GET) { path = "/i"; } else { path = "/com.snowplowanalytics.snowplow/tp2"; } if (port == null) { return String.Format("{0}://{1}{2}", requestProtocol, endpoint, path); } else { return String.Format("{0}://{1}:{2}{3}", requestProtocol, endpoint, port.ToString(), path); } }
/// <summary> /// Asynchronous emitter to send non-blocking HTTP requests /// </summary> /// <param name="endpoint">Collector domain</param> /// <param name="protocol">HttpProtocol.HTTP or HttpProtocol.HTTPS</param> /// <param name="port">Port to connect to</param> /// <param name="method">HttpMethod.GET or HttpMethod.POST</param> /// <param name="bufferSize">Maximum number of events queued before the buffer is flushed automatically. /// Defaults to 10 for POST requests and 1 for GET requests.</param> /// <param name="onSuccess">Callback executed when every request in a flush has status code 200. /// Gets passed the number of events flushed.</param> /// <param name="onFailure">Callback executed when not every request in a flush has status code 200. /// Gets passed the number of events sent successfully and a list of unsuccessful events.</param> /// <param name="offlineModeEnabled">Whether to store unsent requests using MSMQ</param> public AsyncEmitter(string endpoint, HttpProtocol protocol = HttpProtocol.HTTP, int? port = null, HttpMethod method = HttpMethod.GET, int? bufferSize = null, Action<int> onSuccess = null, Action<int, List<Dictionary<string, string>>> onFailure = null, bool offlineModeEnabled = true) : base(endpoint, protocol, port, method, bufferSize, onSuccess, onFailure, offlineModeEnabled) { tasks = new List<Task>(); }
/// <summary> /// Sets the http protocol. /// </summary> /// <param name="httpProtocol">Http protocol.</param> public void SetHttpProtocol(HttpProtocol httpProtocol) { this.httpProtocol = httpProtocol; this.collectorUri = MakeCollectorUri(this.endpoint, this.httpProtocol, this.httpMethod); }
protected HttpMessage(HttpProtocol protocol) { Protocol = protocol; }
/// <summary> /// Proxies a normal (i.e. non-upgradable) request to the upstream server, and the response back to our client. /// </summary> /// <remarks> /// Normal proxying comprises the following steps: /// (1) Create outgoing HttpRequestMessage /// (2) Setup copy of request body (background) Downstream --► Proxy --► Upstream /// (3) Copy request headers Downstream --► Proxy --► Upstream /// (4) Send the outgoing request using HttpMessageInvoker Downstream --► Proxy --► Upstream /// (5) Copy response status line Downstream ◄-- Proxy ◄-- Upstream /// (6) Copy response headers Downstream ◄-- Proxy ◄-- Upstream /// (7) Send response headers Downstream ◄-- Proxy ◄-- Upstream /// (8) Copy response body Downstream ◄-- Proxy ◄-- Upstream /// (9) Wait for completion of step 2: copying request body Downstream --► Proxy --► Upstream /// (10) Copy response trailer headers Downstream ◄-- Proxy ◄-- Upstream /// /// ASP .NET Core (Kestrel) will finally send response trailers (if any) /// after we complete the steps above and relinquish control. /// </remarks> private async Task NormalProxyAsync( HttpContext context, Uri targetUri, HttpMessageInvoker httpClient, ProxyTelemetryContext proxyTelemetryContext, CancellationToken shortCancellation, CancellationToken longCancellation) { Contracts.CheckValue(context, nameof(context)); Contracts.CheckValue(targetUri, nameof(targetUri)); Contracts.CheckValue(httpClient, nameof(httpClient)); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 1: Create outgoing HttpRequestMessage var upstreamRequest = new HttpRequestMessage(HttpUtilities.GetHttpMethod(context.Request.Method), targetUri) { // We request HTTP/2, but HttpClient will fallback to HTTP/1.1 if it cannot establish HTTP/2 with the target. // This is done without extra round-trips thanks to ALPN. We can detect a downgrade after calling HttpClient.SendAsync // (see Step 3 below). TBD how this will change when HTTP/3 is supported. Version = Http2Version, }; // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 2: Setup copy of request body (background) Downstream --► Proxy --► Upstream // Note that we must do this before step (3) because step (3) may also add headers to the HttpContent that we set up here. var bodyToUpstreamContent = SetupCopyBodyUpstream(context.Request.Body, upstreamRequest, in proxyTelemetryContext, longCancellation); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 3: Copy request headers Downstream --► Proxy --► Upstream CopyHeadersToUpstream(context.Request.Headers, upstreamRequest); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 4: Send the outgoing request using HttpClient ////this.logger.LogInformation($" Starting Proxy --> upstream request"); var upstreamResponse = await httpClient.SendAsync(upstreamRequest, shortCancellation); // Detect connection downgrade, which may be problematic for e.g. gRPC. if (upstreamResponse.Version.Major != 2 && HttpProtocol.IsHttp2(context.Request.Protocol)) { // TODO: Do something on connection downgrade... _logger.LogInformation($"HTTP version downgrade detected! This may break gRPC communications."); } // Assert that, if we are proxying content upstream, it must have started by now // (since HttpClient.SendAsync has already completed asynchronously). // If this check fails, there is a coding defect which would otherwise // cause us to wait forever in step 9, so fail fast here. if (bodyToUpstreamContent != null && !bodyToUpstreamContent.Started) { throw new InvalidOperationException("Proxying the downstream request body to the upstream server hasn't started. This is a coding defect."); } // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 5: Copy response status line Downstream ◄-- Proxy ◄-- Upstream ////this.logger.LogInformation($" Setting downstream <-- Proxy status: {(int)upstreamResponse.StatusCode} {upstreamResponse.ReasonPhrase}"); context.Response.StatusCode = (int)upstreamResponse.StatusCode; context.Features.Get <IHttpResponseFeature>().ReasonPhrase = upstreamResponse.ReasonPhrase; // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 6: Copy response headers Downstream ◄-- Proxy ◄-- Upstream CopyHeadersToDownstream(upstreamResponse, context.Response.Headers); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 7: Send response headers Downstream ◄-- Proxy ◄-- Upstream // This is important to avoid any extra delays in sending response headers // e.g. if the upstream server is slow to provide its response body. ////this.logger.LogInformation($" Starting downstream <-- Proxy response"); // TODO: Some of the tasks in steps (7) - (9) may go unobserved depending on what fails first. Needs more consideration. await context.Response.StartAsync(shortCancellation); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 8: Copy response body Downstream ◄-- Proxy ◄-- Upstream await CopyBodyDownstreamAsync(upstreamResponse.Content, context.Response.Body, proxyTelemetryContext, longCancellation); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 9: Wait for completion of step 2: copying request body Downstream --► Proxy --► Upstream if (bodyToUpstreamContent != null) { ////this.logger.LogInformation($" Waiting for downstream --> Proxy --> upstream body proxying to complete"); await bodyToUpstreamContent.ConsumptionTask; } // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 10: Copy response trailer headers Downstream ◄-- Proxy ◄-- Upstream CopyTrailingHeadersToDownstream(upstreamResponse, context); }
public Target() { Address = string.Empty; Port = -1; Protocol = HttpProtocol.HTTP; }
public HttpRequest(HttpProtocol protocol, string method, string path, NameValueCollection headers) : base(protocol, headers) { Method = method; Path = path; }