Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
HttpxGetter.cs
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Security.Cryptography.X509Certificates;
5using System.Threading;
6using System.Threading.Tasks;
7using Waher.Content;
11
13{
21 {
28 public HttpxGetter()
29 {
30 }
31
35 public const string HttpxUriScheme = "httpx";
36
40 public string[] UriSchemes => new string[] { HttpxUriScheme };
41
48 public bool CanGet(Uri Uri, out Grade Grade)
49 {
50 switch (Uri.Scheme)
51 {
52 case HttpxUriScheme:
53 Grade = Grade.Ok;
54 return true;
55
56 default:
57 Grade = Grade.NotAtAll;
58 return false;
59 }
60 }
61
70 public Task<object> GetAsync(Uri Uri, X509Certificate Certificate, RemoteCertificateEventHandler RemoteCertificateValidator,
71 params KeyValuePair<string, string>[] Headers)
72 {
73 return this.GetAsync(Uri, Certificate, RemoteCertificateValidator, 60000, Headers);
74 }
75
93 public async Task<object> GetAsync(Uri Uri, X509Certificate Certificate,
94 RemoteCertificateEventHandler RemoteCertificateValidator, int TimeoutMs, params KeyValuePair<string, string>[] Headers)
95 {
96 KeyValuePair<string, TemporaryStream> Rec = await this.GetTempStreamAsync(Uri, Certificate, RemoteCertificateValidator, TimeoutMs, Headers);
97 string ContentType = Rec.Key;
98 TemporaryStream File = Rec.Value;
99
100 try
101 {
102 if (File is null)
103 return null;
104
105 File.Position = 0;
106
107 if (File.Length > int.MaxValue)
108 throw new OutOfMemoryException("Resource too large.");
109
110 int Len = (int)File.Length;
111 byte[] Bin = new byte[Len];
112 if (await File.ReadAsync(Bin, 0, Len) != Len)
113 throw new IOException("Unable to read from file.");
114
115 return await InternetContent.DecodeAsync(ContentType, Bin, Uri);
116 }
117 finally
118 {
119 File?.Dispose();
120 }
121 }
122
136 public Task<KeyValuePair<string, TemporaryStream>> GetTempStreamAsync(Uri Uri, X509Certificate Certificate,
137 RemoteCertificateEventHandler RemoteCertificateValidator, params KeyValuePair<string, string>[] Headers)
138 {
139 return this.GetTempStreamAsync(Uri, Certificate, RemoteCertificateValidator, 60000, Headers);
140 }
141
156 public async Task<KeyValuePair<string, TemporaryStream>> GetTempStreamAsync(Uri Uri, X509Certificate Certificate,
157 RemoteCertificateEventHandler RemoteCertificateValidator, int TimeoutMs, params KeyValuePair<string, string>[] Headers)
158 {
160 string BareJid = Uri.UserInfo + "@" + Uri.Authority;
161 string FullJid;
162 string LocalUrl;
163
164 if (Types.TryGetModuleParameter("HTTPX", out object Obj) && Obj is HttpxProxy Proxy)
165 {
166 if (Proxy.DefaultXmppClient.Disposed || Proxy.ServerlessMessaging.Disposed)
167 throw new InvalidOperationException("Service is being shut down.");
168
169 if (string.Compare(BareJid, Proxy.DefaultXmppClient.BareJID, true) == 0 &&
170 Proxy.DefaultXmppClient.TryGetExtension(out HttpxServer Server))
171 {
172 return await Server.GetLocalTempStreamAsync(Uri.PathAndQuery + Uri.Fragment);
173 }
174 else
175 {
176 GetClientResponse Rec = await Proxy.GetClientAsync(Uri);
177
178 BareJid = Rec.BareJid;
179 FullJid = Rec.FullJid;
181 LocalUrl = Rec.LocalUrl;
182 }
183 }
184 else if (Types.TryGetModuleParameter("XMPP", out Obj) && Obj is XmppClient XmppClient)
185 {
187 throw new InvalidOperationException("Service is being shut down.");
188
189 if (string.Compare(BareJid, XmppClient.BareJID, true) == 0 &&
191 {
192 return await Server.GetLocalTempStreamAsync(Uri.PathAndQuery + Uri.Fragment);
193 }
194 else
195 {
196 if (!XmppClient.TryGetExtension(out HttpxClient HttpxClient2))
197 throw new InvalidOperationException("No HTTPX Extesion has been registered on the XMPP Client.");
198
199 HttpxClient = HttpxClient2;
200
201 if (string.IsNullOrEmpty(Uri.UserInfo))
202 FullJid = BareJid = Uri.Authority;
203 else
204 {
205 BareJid = Uri.UserInfo + "@" + Uri.Authority;
206
207 RosterItem Item = XmppClient.GetRosterItem(BareJid);
208
209 if (Item is null)
210 throw new ConflictException("No approved presence subscription with " + BareJid + ".");
211 else if (!Item.HasLastPresence || !Item.LastPresence.IsOnline)
212 throw new ServiceUnavailableException(BareJid + " is not online.");
213 else
214 FullJid = Item.LastPresenceFullJid;
215 }
216
217 LocalUrl = Uri.PathAndQuery + Uri.Fragment;
218 }
219 }
220 else
221 throw new InvalidOperationException("An HTTPX Proxy or XMPP Client Module Parameter has not been registered.");
222
223 List<HttpField> Headers2 = new List<HttpField>();
224 bool HasHost = false;
225
226 foreach (KeyValuePair<string, string> Header in Headers)
227 {
228 switch (Header.Key.ToLower())
229 {
230 case "host":
231 Headers2.Add(new HttpField("Host", BareJid));
232 HasHost = true;
233 break;
234
235 case "cookie":
236 case "set-cookie":
237 // Do not forward cookies.
238 break;
239
240 default:
241 Headers2.Add(new HttpField(Header.Key, Header.Value));
242 break;
243 }
244 }
245
246 if (!HasHost)
247 Headers2.Add(new HttpField("Host", Uri.Authority));
248
249 State State = null;
250 Timer Timer = null;
251
252 try
253 {
254 State = new State();
255 Timer = new Timer((P) =>
256 {
257 State.Done.TrySetResult(false);
258 }, null, TimeoutMs, Timeout.Infinite);
259
260 // TODO: Transport public part of Client certificate, if provided.
261
262 if (HttpxClient is null)
263 throw new Exception("No HTTPX client available.");
264
265 await HttpxClient.Request(FullJid, "GET", LocalUrl, async (Sender, e) =>
266 {
267 if (e.Ok)
268 {
269 State.HttpResponse = e.HttpResponse;
270 State.StatusCode = e.StatusCode;
271 State.StatusMessage = e.StatusMessage;
272
273 if (e.HasData)
274 {
275 State.File = new TemporaryStream();
276
277 if (!(e.Data is null))
278 {
279 await State.File.WriteAsync(e.Data, 0, e.Data.Length);
280 State.Done.TrySetResult(true);
281 }
282 }
283 else
284 State.Done.TrySetResult(true);
285 }
286 else
287 State.Done.TrySetException(e.StanzaError ?? new Exception("Unable to get resource."));
288
289 }, async (Sender, e) =>
290 {
291 await (State.File?.WriteAsync(e.Data, 0, e.Data.Length) ?? Task.CompletedTask);
292 if (e.Last)
293 State.Done?.TrySetResult(true);
294
295 }, State, Headers2.ToArray());
296
297 if (!await State.Done.Task)
298 throw new TimeoutException("Request timed out.");
299
300 Timer.Dispose();
301 Timer = null;
302
303 if (State.StatusCode >= 200 && State.StatusCode < 300)
304 {
305 TemporaryStream Result = State.File;
306 State.File = null;
307
308 return new KeyValuePair<string, TemporaryStream>(State.HttpResponse?.ContentType, Result);
309 }
310 else
311 {
312 string ContentType = string.Empty;
313 byte[] Data;
314
315 if (State.File is null)
316 Data = null;
317 else
318 {
319 int Len = (int)State.File.Length;
320
321 ContentType = State.HttpResponse.ContentType;
322 State.File.Position = 0;
323 Data = new byte[Len];
324 await State.File.ReadAsync(Data, 0, Len);
325 }
326
327 throw GetExceptionObject(State.StatusCode, State.StatusMessage,
328 State.HttpResponse, Data, ContentType);
329 }
330 }
331 finally
332 {
333 State.File?.Dispose();
334 State.File = null;
335
336 if (!(State.HttpResponse is null))
337 {
338 await State.HttpResponse.DisposeAsync();
339 State.HttpResponse = null;
340 }
341
342 Timer?.Dispose();
343 Timer = null;
344 }
345 }
346
347 internal static Exception GetExceptionObject(int StatusCode, string StatusMessage,
348 HttpResponse Response, byte[] Data, string ContentType)
349 {
350 switch (StatusCode)
351 {
352 // Client Errors
353 case BadRequestException.Code: throw new BadRequestException(Data, ContentType);
354 case ConflictException.Code: throw new ConflictException(Data, ContentType);
355 case FailedDependencyException.Code: throw new FailedDependencyException(Data, ContentType);
356 case ForbiddenException.Code: throw new ForbiddenException(Data, ContentType);
357 case GoneException.Code: throw new GoneException(Data, ContentType);
358 case LockedException.Code: throw new LockedException(Data, ContentType);
359 case MethodNotAllowedException.Code: throw new MethodNotAllowedException(GetMethods(Response.GetFirstHeader("Allow")), Data, ContentType);
360 case MisdirectedRequestException.Code: throw new MisdirectedRequestException(Data, ContentType);
361 case NotAcceptableException.Code: throw new NotAcceptableException(Data, ContentType);
362 case NotFoundException.Code: throw new NotFoundException(Data, ContentType);
363 case PreconditionFailedException.Code: throw new PreconditionFailedException(Data, ContentType);
364 case PreconditionRequiredException.Code: throw new PreconditionRequiredException(Data, ContentType);
365 case RangeNotSatisfiableException.Code: throw new RangeNotSatisfiableException(Data, ContentType);
366 case RequestTimeoutException.Code: throw new RequestTimeoutException(Data, ContentType);
367 case TooManyRequestsException.Code: throw new TooManyRequestsException(Data, ContentType);
368 case UnauthorizedException.Code: throw new UnauthorizedException(Data, ContentType, Response.GetChallenges());
370 case UnprocessableEntityException.Code: throw new UnprocessableEntityException(Data, ContentType);
371 case UnsupportedMediaTypeException.Code: throw new UnsupportedMediaTypeException(Data, ContentType);
372 case UpgradeRequiredException.Code: throw new UpgradeRequiredException(Response.GetFirstHeader("Upgrade"), Data, ContentType);
373
374 // Redirections
375 case MovedPermanentlyException.Code: throw new MovedPermanentlyException(Response.GetFirstHeader("Location"), Data, ContentType);
376 case FoundException.Code: throw new FoundException(Response.GetFirstHeader("Location"), Data, ContentType);
377 case SeeOtherException.Code: throw new SeeOtherException(Response.GetFirstHeader("Location"), Data, ContentType);
379 case UseProxyException.Code: throw new UseProxyException(Response.GetFirstHeader("Location"), Data, ContentType);
380 case TemporaryRedirectException.Code: throw new TemporaryRedirectException(Response.GetFirstHeader("Location"), Data, ContentType);
381 case PermanentRedirectException.Code: throw new PermanentRedirectException(Response.GetFirstHeader("Location"), Data, ContentType);
382
383 // Server Errors
384 case BadGatewayException.Code: throw new BadGatewayException(Data, ContentType);
385 case GatewayTimeoutException.Code: throw new GatewayTimeoutException(Data, ContentType);
386 case InsufficientStorageException.Code: throw new InsufficientStorageException(Data, ContentType);
387 case InternalServerErrorException.Code: throw new InternalServerErrorException(Data, ContentType);
388 case LoopDetectedException.Code: throw new LoopDetectedException(Data, ContentType);
390 case NotExtendedException.Code: throw new NotExtendedException(Data, ContentType);
391 case HTTP.NotImplementedException.Code: throw new HTTP.NotImplementedException(Data, ContentType);
392 case ServiceUnavailableException.Code: throw new ServiceUnavailableException(Data, ContentType);
393 case VariantAlsoNegotiatesException.Code: throw new VariantAlsoNegotiatesException(Data, ContentType);
394
395 default:
396 throw new HttpException(StatusCode, StatusMessage, Data, ContentType);
397 }
398 }
399
400 private static string[] GetMethods(string Allow)
401 {
402 if (string.IsNullOrEmpty(Allow))
403 return new string[0];
404
405 string[] Result = Allow.Split(',');
406 int i, c = Result.Length;
407
408 for (i = 0; i < c; i++)
409 Result[i] = Result[i].Trim();
410
411 return Result;
412 }
413
414 private class State
415 {
416 public HttpResponse HttpResponse = null;
417 public TemporaryStream File = null;
418 public TaskCompletionSource<bool> Done = new TaskCompletionSource<bool>();
419 public string StatusMessage = string.Empty;
420 public int StatusCode = 0;
421 }
422
423 }
424}
Static class managing encoding and decoding of internet content.
static Task< object > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
The server, while acting as a gateway or proxy, received an invalid response from the upstream server...
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repe...
The request could not be completed due to a conflict with the current state of the resource....
The request failed due to failure of a previous request (e.g., a PROPPATCH).
The server understood the request, but is refusing to fulfill it. Authorization will not help and the...
The requested resource resides temporarily under a different URI. Since the redirection might be alte...
The server, while acting as a gateway or proxy, did not receive a timely response from the upstream s...
The requested resource is no longer available at the server and no forwarding address is known....
Base class of all HTTP Exceptions.
Base class for all HTTP fields.
Definition: HttpField.cs:7
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:21
string GetFirstHeader(string FieldName)
Gets the first header value matching a given header field name.
string[] GetChallenges()
Gets available WWW-Authenticate challenges returned in the response.
The server is unable to store the representation needed to complete the request.
The server encountered an unexpected condition which prevented it from fulfilling the request.
The resource that is being accessed is locked.
The server detected an infinite loop while processing the request.
The server has not found anything matching the Request-URI. No indication is given of whether the con...
The request was directed at a server that is not able to produce a response (for example because a co...
The requested resource has been assigned a new permanent URI and any future references to this resour...
The client needs to authenticate to gain network access. Intended for use by intercepting proxies use...
The resource identified by the request is only capable of generating response entities which have con...
Further extensions to the request are required for the server to fulfil it.
The server has not found anything matching the Request-URI. No indication is given of whether the con...
If the client has performed a conditional GET request and access is allowed, but the document has not...
This means that the resource is now permanently located at another URI, specified by the Location: HT...
The precondition given in one or more of the request-header fields evaluated to false when it was tes...
The origin server requires the request to be conditional. Intended to prevent "the 'lost update' prob...
A server SHOULD return a response with this status code if a request included a Range request-header ...
The client did not produce a request within the time that the server was prepared to wait....
The response to the request can be found under a different URI and SHOULD be retrieved using a GET me...
The server is currently unable to handle the request due to a temporary overloading or maintenance of...
The requested resource resides temporarily under a different URI. Since the redirection MAY be altere...
The user has sent too many requests in a given amount of time. Intended for use with rate limiting sc...
Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or ...
The request was well-formed but was unable to be followed due to semantic errors.
The server is refusing to service the request because the entity of the request is in a format not su...
The client should switch to a different protocol such as TLS/1.0, given in the Upgrade header field.
The requested resource MUST be accessed through the proxy given by the Location field....
Transparent content negotiation for the request results in a circular reference.
Response to the HttpxProxy.GetClientAsync(Uri) method call.
string BareJid
Bare JID of entity hosting the resource.
HttpxClient HttpxClient
Corresponding HttpxClient object to use for the request..
string FullJid
Full JID of entity hosting the resource.
Task Request(string To, string Method, string LocalResource, EventHandlerAsync< HttpxResponseEventArgs > Callback, EventHandlerAsync< HttpxResponseDataEventArgs > DataCallback, object State, params HttpField[] Headers)
Performs an HTTP request.
Definition: HttpxClient.cs:252
Content Getter, retrieving content using the HTTPX URI Scheme.
Definition: HttpxGetter.cs:21
string[] UriSchemes
Supported URI schemes.
Definition: HttpxGetter.cs:40
Task< KeyValuePair< string, TemporaryStream > > GetTempStreamAsync(Uri Uri, X509Certificate Certificate, RemoteCertificateEventHandler RemoteCertificateValidator, params KeyValuePair< string, string >[] Headers)
Gets a (possibly big) resource, using a Uniform Resource Identifier (or Locator).
Definition: HttpxGetter.cs:136
Task< object > GetAsync(Uri Uri, X509Certificate Certificate, RemoteCertificateEventHandler RemoteCertificateValidator, params KeyValuePair< string, string >[] Headers)
Gets a resource, using a Uniform Resource Identifier (or Locator).
Definition: HttpxGetter.cs:70
async Task< KeyValuePair< string, TemporaryStream > > GetTempStreamAsync(Uri Uri, X509Certificate Certificate, RemoteCertificateEventHandler RemoteCertificateValidator, int TimeoutMs, params KeyValuePair< string, string >[] Headers)
Gets a (possibly big) resource, using a Uniform Resource Identifier (or Locator).
Definition: HttpxGetter.cs:156
bool CanGet(Uri Uri, out Grade Grade)
If the getter is able to get a resource, given its URI.
Definition: HttpxGetter.cs:48
async Task< object > GetAsync(Uri Uri, X509Certificate Certificate, RemoteCertificateEventHandler RemoteCertificateValidator, int TimeoutMs, params KeyValuePair< string, string >[] Headers)
Gets a resource, using a Uniform Resource Identifier (or Locator).
Definition: HttpxGetter.cs:93
HttpxGetter()
Content Getter, retrieving content using the HTTPX URI Scheme.
Definition: HttpxGetter.cs:28
Implements a Proxy resource that allows Web clients to fetch HTTP-based resources over HTTPX.
Definition: HttpxProxy.cs:19
Maintains information about an item in the roster.
Definition: RosterItem.cs:75
bool HasLastPresence
If the roster item has received presence from an online resource having the given bare JID.
Definition: RosterItem.cs:425
string LastPresenceFullJid
Full JID of last resource sending online presence.
Definition: RosterItem.cs:343
PresenceEventArgs LastPresence
Last presence received from a resource having this bare JID.
Definition: RosterItem.cs:356
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Definition: XmppClient.cs:59
bool Disposed
If the client has been disposed.
Definition: XmppClient.cs:1192
bool TryGetExtension(Type Type, out IXmppExtension Extension)
Tries to get a registered extension of a specific type from the client.
Definition: XmppClient.cs:7318
RosterItem GetRosterItem(string BareJID)
Gets a roster item.
Definition: XmppClient.cs:4522
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
Definition: Types.cs:583
Manages a temporary stream. Contents is kept in-memory, if below a memory threshold,...
override void Dispose(bool disposing)
Releases the unmanaged resources used by the System.IO.Stream and optionally releases the managed res...
override Task< int > ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
Asynchronously reads a sequence of bytes from the current stream, advances the position within the st...
override long Length
When overridden in a derived class, gets the length in bytes of the stream.
Basic interface for Internet Content getters. A class implementing this interface and having a defaul...
delegate void RemoteCertificateEventHandler(object Sender, RemoteCertificateEventArgs e)
Delegate for remote certificate event handlers.
Grade
Grade enumeration
Definition: Grade.cs:7