/// <summary> /// Create a new abstract remote charging station operator attached via a computer network (HTTPS/TCP/IP). /// </summary> /// <param name="Hostname">The remote hostname.</param> /// <param name="VirtualHostname">An optional remote virtual hostname.</param> /// <param name="RemotePort">An optional remote HTTPS port.</param> /// <param name="RemoteCertificateValidator">An optional remote SSL/TLS certificate validator.</param> /// <param name="RoamingNetworkId">An optional roaming network identification.</param> /// <param name="RequestTimeout">An optional request timeout.</param> /// <param name="DNSClient">An optional DNS client to use.</param> public ANetworkChargingStationOperator(HTTPHostname Hostname, HTTPHostname?VirtualHostname = null, IPPort?RemotePort = null, RemoteCertificateValidationCallback RemoteCertificateValidator = null, RoamingNetwork_Id?RoamingNetworkId = null, TimeSpan?RequestTimeout = null, DNSClient DNSClient = null) { this.Hostname = Hostname; this.VirtualHostname = VirtualHostname ?? this.Hostname; this.RemotePort = RemotePort ?? DefaultRemotePort; this.RemoteCertificateValidator = RemoteCertificateValidator ?? ((sender, certificate, chain, policyErrors) => true); this.RoamingNetworkId = RoamingNetworkId ?? DefaultRoamingNetworkId; this.RequestTimeout = RequestTimeout ?? DefaultRequestTimeout; this.DNSClient = DNSClient; }
/// <summary> /// Register a EventStream handler. /// </summary> public static void RegisterEventStreamHandler(this HTTPServer HTTPServer, HTTPHostname Hostname, String URITemplate, String EventSource) { HTTPServer.AddMethodCallback(Hostname, HTTPMethod.GET, URITemplate, HTTPDelegate: async Request => { var _LastEventId = 0UL; var _Client_LastEventId = 0UL; var _EventSource = HTTPServer.GetEventSource(EventSource); if (Request.TryGet<UInt64>("Last-Event-Id", out _Client_LastEventId)) _LastEventId = _Client_LastEventId; //_LastEventId = 0; var _HTTPEvents = (from _HTTPEvent in _EventSource.GetAllEventsGreater(_LastEventId) where _HTTPEvent != null select _HTTPEvent.ToString()) .ToArray(); // For thread safety! // Transform HTTP events into an UTF8 string var _ResourceContent = String.Empty; if (_HTTPEvents.Length > 0) _ResourceContent = Environment.NewLine + _HTTPEvents.Aggregate((a, b) => a + Environment.NewLine + b); _ResourceContent += Environment.NewLine + "retry: " + _EventSource.RetryIntervall.TotalMilliseconds + Environment.NewLine + Environment.NewLine; return new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.OK, Server = HTTPServer.DefaultServerName, Date = DateTime.Now, ContentType = HTTPContentType.EVENTSTREAM, CacheControl = "no-cache", Connection = "keep-alive", Content = _ResourceContent.ToUTF8Bytes() }; }); }
/// <summary> /// Create a new OICP Mobile client. /// </summary> /// <param name="ClientId">A unqiue identification of this client.</param> /// <param name="Hostname">The OICP hostname to connect to.</param> /// <param name="RemotePort">An optional OICP TCP port to connect to.</param> /// <param name="RemoteCertificateValidator">A delegate to verify the remote TLS certificate.</param> /// <param name="ClientCertificateSelector">A delegate to select a TLS client certificate.</param> /// <param name="HTTPVirtualHost">An optional HTTP virtual host name to use.</param> /// <param name="HTTPUserAgent">An optional HTTP user agent to use.</param> /// <param name="RequestTimeout">An optional timeout for upstream queries.</param> /// <param name="MaxNumberOfRetries">The default number of maximum transmission retries.</param> /// <param name="DNSClient">An optional DNS client.</param> /// <param name="LoggingContext">An optional context for logging client methods.</param> /// <param name="LogfileCreator">A delegate to create a log file from the given context and log file name.</param> public MobileClient(String ClientId, HTTPHostname Hostname, IPPort?RemotePort = null, RemoteCertificateValidationCallback RemoteCertificateValidator = null, LocalCertificateSelectionCallback ClientCertificateSelector = null, HTTPHostname?HTTPVirtualHost = null, HTTPPath?URLPrefix = null, String MobileAuthorizationURL = DefaultMobileAuthorizationURL, String HTTPUserAgent = DefaultHTTPUserAgent, TimeSpan?RequestTimeout = null, Byte?MaxNumberOfRetries = DefaultMaxNumberOfRetries, DNSClient DNSClient = null, String LoggingContext = MobileClientLogger.DefaultContext, LogfileCreatorDelegate LogfileCreator = null) : base(ClientId, Hostname, RemotePort ?? DefaultRemotePort, RemoteCertificateValidator, ClientCertificateSelector, HTTPVirtualHost, URLPrefix ?? DefaultURLPrefix, null, HTTPUserAgent, RequestTimeout, null, MaxNumberOfRetries, DNSClient) { #region Initial checks if (ClientId.IsNullOrEmpty()) { throw new ArgumentNullException(nameof(ClientId), "The given client identification must not be null or empty!"); } #endregion this.MobileAuthorizationURL = MobileAuthorizationURL ?? DefaultMobileAuthorizationURL; this.Logger = new MobileClientLogger(this, LoggingContext, LogfileCreator); }
/// <summary> /// Register a SOAP delegate. /// </summary> /// <param name="Hostname">The HTTP hostname.</param> /// <param name="URITemplate">The URI template.</param> /// <param name="Description">A description of this SOAP delegate.</param> /// <param name="SOAPMatch">A delegate to check whether this dispatcher matches the given XML.</param> /// <param name="SOAPHeaderAndBodyDelegate">A delegate to process a matching SOAP request.</param> public void RegisterSOAPDelegate(HTTPHostname Hostname, String URITemplate, String Description, SOAPMatch SOAPMatch, SOAPHeaderAndBodyDelegate SOAPHeaderAndBodyDelegate) { SOAPDispatcher _SOAPDispatcher = null; // Check if there are other SOAP dispatchers at the given URI template. var _Handler = GetHandler(HTTPHostname.Any, URITemplate, HTTPMethod.POST, ContentTypes => SOAPContentType); if (_Handler == null) { _SOAPDispatcher = new SOAPDispatcher(URITemplate, SOAPContentType); _SOAPDispatchers.Add(URITemplate, _SOAPDispatcher); // Register a new SOAP dispatcher AddMethodCallback(Hostname, HTTPMethod.POST, URITemplate, SOAPContentType, HTTPDelegate: _SOAPDispatcher.Invoke); // Register some information text for people using HTTP GET AddMethodCallback(Hostname, HTTPMethod.GET, URITemplate, SOAPContentType, HTTPDelegate: _SOAPDispatcher.EndpointTextInfo); } else { _SOAPDispatcher = _Handler.Target as SOAPDispatcher; } _SOAPDispatcher.RegisterSOAPDelegate(Description, SOAPMatch, SOAPHeaderAndBodyDelegate); }
public HTTPNotificationSender(UsersAPI UsersAPI, HTTPHostname Hostname, IPPort?HTTPPort = null, TimeSpan?SendNotificationsEvery = null, Boolean DisableSendNotifications = false, PgpPublicKeyRing PublicKeyRing = null, PgpSecretKeyRing SecretKeyRing = null, DNSClient DNSClient = null) : base(UsersAPI, SendNotificationsEvery, DisableSendNotifications, PublicKeyRing, SecretKeyRing, DNSClient) { this.Hostname = Hostname; this.HTTPPort = HTTPPort ?? DefaultHTTPPort; }
/// <summary> /// Creates a new hostname node. /// </summary> /// <param name="Hostname">The hostname(s) for this (virtual) http service.</param> /// <param name="RequestHandler">The default delegate to call for any request to this hostname.</param> /// <param name="HostAuthentication">This and all subordinated nodes demand an explicit host authentication.</param> /// <param name="DefaultErrorHandler">The default error handling delegate.</param> internal HostnameNode(HTTPHostname Hostname, HTTPAuthentication HostAuthentication = null, HTTPDelegate RequestHandler = null, HTTPDelegate DefaultErrorHandler = null) { #region Check Hostname if (Hostname == null) throw new ArgumentNullException("Hostname", "The given HTTP hostname must not be null!"); var HostHeader = Hostname.ToString().Split(new Char[1] { ':' }, StringSplitOptions.None).Select(v => v.Trim()).ToArray(); UInt16 HostPort = 80; // 1.2.3.4 => 1.2.3.4:80 // 1.2.3.4:80 => ok // 1.2.3.4 : 80 => ok // 1.2.3.4:* => ok // 1.2.3.4:a => invalid // 1.2.3.4:80: => ok // 1.2.3.4:80:0 => invalid // rfc 2616 - 3.2.2 // If the port is empty or not given, port 80 is assumed. if (HostHeader.Length == 1) this._Hostname = HTTPHostname.Parse(Hostname + ":" + HostPort); else if ((HostHeader.Length == 2 && (!UInt16.TryParse(HostHeader[1], out HostPort) && HostHeader[1] != "*")) || HostHeader.Length > 2) throw new ArgumentException("Invalid Hostname!", "Hostname"); else this._Hostname = HTTPHostname.Parse(HostHeader[0] + ":" + HostHeader[1]); #endregion this._HostAuthentication = (HostAuthentication != null) ? HostAuthentication : _ => true; this._RequestHandler = RequestHandler; this._DefaultErrorHandler = DefaultErrorHandler; this._URINodes = new Dictionary<String, URINode>(); this._ErrorHandlers = new Dictionary<HTTPStatusCode, HTTPDelegate>(); }
/// <summary> /// Returns internal resources embedded within the given assembly. /// </summary> /// <param name="HTTPServer">A HTTP server.</param> /// <param name="Hostname">The HTTP hostname.</param> /// <param name="URITemplate">An URI template.</param> /// <param name="ResourceAssembly">The assembly where the resources are located.</param> /// <param name="ResourceFilename">The path to the file within the assembly.</param> /// <param name="ResponseContentType">Set the HTTP MIME content-type of the file. If null try to autodetect the content type based on the filename extention.</param> /// <param name="CacheControl">Set the HTTP cache control response header.</param> public static void RegisterResourcesFile(this HTTPServer HTTPServer, HTTPHostname Hostname, String URITemplate, Assembly ResourceAssembly, String ResourceFilename, HTTPContentType ResponseContentType = null, String CacheControl = "no-cache") { #region Get the appropriate content type based on the suffix of the requested resource if (ResponseContentType == null) switch (ResourceFilename.Remove(0, ResourceFilename.LastIndexOf(".") + 1)) { case "htm": ResponseContentType = HTTPContentType.HTML_UTF8; break; case "html": ResponseContentType = HTTPContentType.HTML_UTF8; break; case "css": ResponseContentType = HTTPContentType.CSS_UTF8; break; case "gif": ResponseContentType = HTTPContentType.GIF; break; case "jpg": ResponseContentType = HTTPContentType.JPEG; break; case "jpeg": ResponseContentType = HTTPContentType.JPEG; break; case "svg": ResponseContentType = HTTPContentType.SVG; break; case "png": ResponseContentType = HTTPContentType.PNG; break; case "ico": ResponseContentType = HTTPContentType.ICO; break; case "swf": ResponseContentType = HTTPContentType.SWF; break; case "js": ResponseContentType = HTTPContentType.JAVASCRIPT_UTF8; break; case "txt": ResponseContentType = HTTPContentType.TEXT_UTF8; break; case "xml": ResponseContentType = HTTPContentType.XML_UTF8; break; default: ResponseContentType = HTTPContentType.OCTETSTREAM; break; } #endregion HTTPServer.AddMethodCallback(Hostname, HTTPMethod.GET, URITemplate, HTTPContentType: ResponseContentType, HTTPDelegate: async Request => { var FileStream = ResourceAssembly.GetManifestResourceStream(ResourceFilename); if (FileStream != null) return new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.OK, Server = HTTPServer.DefaultServerName, Date = DateTime.Now, ContentType = ResponseContentType, ContentStream = FileStream, CacheControl = CacheControl, Connection = "close", }; else { #region Try to find a appropriate customized errorpage... Stream ErrorStream = null; Request.BestMatchingAcceptType = Request.Accept.BestMatchingContentType(new HTTPContentType[] { HTTPContentType.HTML_UTF8, HTTPContentType.TEXT_UTF8 }); if (Request.BestMatchingAcceptType == HTTPContentType.HTML_UTF8) { ResponseContentType = HTTPContentType.HTML_UTF8; ErrorStream = ResourceAssembly.GetManifestResourceStream(ResourceFilename.Substring(0, ResourceFilename.LastIndexOf(".")) + ".ErrorPages." + "404.html"); } else if (Request.BestMatchingAcceptType == HTTPContentType.TEXT_UTF8) { ResponseContentType = HTTPContentType.TEXT_UTF8; ErrorStream = ResourceAssembly.GetManifestResourceStream(ResourceFilename.Substring(0, ResourceFilename.LastIndexOf(".")) + ".ErrorPages." + "404.txt"); } else if (Request.BestMatchingAcceptType == HTTPContentType.JSON_UTF8) { ResponseContentType = HTTPContentType.JSON_UTF8; ErrorStream = ResourceAssembly.GetManifestResourceStream(ResourceFilename.Substring(0, ResourceFilename.LastIndexOf(".")) + ".ErrorPages." + "404.js"); } else if (Request.BestMatchingAcceptType == HTTPContentType.XML_UTF8) { ResponseContentType = HTTPContentType.XML_UTF8; ErrorStream = ResourceAssembly.GetManifestResourceStream(ResourceFilename.Substring(0, ResourceFilename.LastIndexOf(".")) + ".ErrorPages." + "404.xml"); } else if (Request.BestMatchingAcceptType == HTTPContentType.ALL) { ResponseContentType = HTTPContentType.HTML_UTF8; ErrorStream = ResourceAssembly.GetManifestResourceStream(ResourceFilename.Substring(0, ResourceFilename.LastIndexOf(".")) + ".ErrorPages." + "404.html"); } if (ErrorStream != null) return new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.NotFound, ContentType = ResponseContentType, ContentStream = ErrorStream, CacheControl = "no-cache", Connection = "close", }; #endregion #region ...or send a default error page! else return new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.NotFound, Server = HTTPServer.DefaultServerName, Date = DateTime.Now, CacheControl = "no-cache", Connection = "close", }; #endregion } }, AllowReplacement: URIReplacement.Fail); return; }
/// <summary> /// Return the RAW request header. /// </summary> public static void RegisterRAWRequestHandler(this HTTPServer HTTPServer, HTTPHostname Hostname, String URITemplate, HTTPMethod HTTPMethod = null) { HTTPServer.AddMethodCallback(Hostname, HTTPMethod: HTTPMethod ?? HTTPMethod.GET, URITemplate: URITemplate, HTTPDelegate: async Request => { return new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.OK, Server = HTTPServer.DefaultServerName, Date = DateTime.Now, CacheControl = "no-cache", Connection = "close", ContentType = HTTPContentType.TEXT_UTF8, Content = ("Incoming http connection from '" + Request.RemoteSocket + "'" + Environment.NewLine + Environment.NewLine + Request.RawHTTPHeader + Environment.NewLine + Environment.NewLine + "Method => " + Request.HTTPMethod + Environment.NewLine + "URL => " + Request.URI + Environment.NewLine + "QueryString => " + Request.QueryString + Environment.NewLine + "Protocol => " + Request.ProtocolName + Environment.NewLine + "Version => " + Request.ProtocolVersion + Environment.NewLine).ToUTF8Bytes() }; }); }
/// <summary> /// Register a MovedTemporarily handler. /// </summary> public static void RegisterMovedTemporarilyHandler(this HTTPServer HTTPServer, HTTPHostname Hostname, String URITemplate, String URITarget) { HTTPServer.AddMethodCallback(Hostname, HTTPMethod.GET, URITemplate, HTTPDelegate: async Request => HTTPTools.MovedTemporarily(Request, URITarget)); }
/// <summary> /// Returns resources from the given file system location. /// </summary> /// <param name="HTTPServer">A HTTP server.</param> /// <param name="Hostname">The HTTP hostname.</param> /// <param name="URITemplate">An URI template.</param> /// <param name="ResourcePath">The path to the file within the assembly.</param> /// <param name="DefaultFilename">The default file to load.</param> public static void RegisterFilesystemFolder(this HTTPServer HTTPServer, HTTPHostname Hostname, String URITemplate, Func<String[], String> ResourcePath, String DefaultFilename = "index.html") { HTTPServer.AddMethodCallback(Hostname, HTTPMethod.GET, URITemplate + (URITemplate.EndsWith("/", StringComparison.InvariantCulture) ? "{ResourceName}" : "/{ResourceName}"), HTTPContentType: HTTPContentType.PNG, HTTPDelegate: async Request => { HTTPContentType ResponseContentType = null; var NumberOfTemplateParameters = URITemplate.Count(c => c == '{'); var FilePath = (Request.ParsedURIParameters != null && Request.ParsedURIParameters.Length > NumberOfTemplateParameters) ? Request.ParsedURIParameters.Last().Replace('/', Path.DirectorySeparatorChar) : DefaultFilename.Replace('/', Path.DirectorySeparatorChar); var FileStream = File.OpenRead(ResourcePath(Request.ParsedURIParameters) + Path.DirectorySeparatorChar + FilePath); if (FileStream != null) { #region Choose HTTP Content Type based on the file name extention... var FileName = FilePath.Substring(FilePath.LastIndexOf("/") + 1); // Get the appropriate content type based on the suffix of the requested resource switch (FileName.Remove(0, FileName.LastIndexOf(".") + 1)) { case "htm": ResponseContentType = HTTPContentType.HTML_UTF8; break; case "html": ResponseContentType = HTTPContentType.HTML_UTF8; break; case "css": ResponseContentType = HTTPContentType.CSS_UTF8; break; case "gif": ResponseContentType = HTTPContentType.GIF; break; case "jpg": ResponseContentType = HTTPContentType.JPEG; break; case "jpeg": ResponseContentType = HTTPContentType.JPEG; break; case "svg": ResponseContentType = HTTPContentType.SVG; break; case "png": ResponseContentType = HTTPContentType.PNG; break; case "ico": ResponseContentType = HTTPContentType.ICO; break; case "swf": ResponseContentType = HTTPContentType.SWF; break; case "js": ResponseContentType = HTTPContentType.JAVASCRIPT_UTF8; break; case "txt": ResponseContentType = HTTPContentType.TEXT_UTF8; break; default: ResponseContentType = HTTPContentType.OCTETSTREAM; break; } #endregion #region Create HTTP Response return new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.OK, Server = HTTPServer.DefaultServerName, Date = DateTime.Now, ContentType = ResponseContentType, ContentStream = FileStream, CacheControl = "public, max-age=300", //Expires = "Mon, 25 Jun 2015 21:31:12 GMT", KeepAlive = new KeepAliveType(TimeSpan.FromMinutes(5), 500), Connection = "Keep-Alive", }; #endregion } else return new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.NotFound, Server = HTTPServer.DefaultServerName, Date = DateTime.Now, CacheControl = "no-cache", Connection = "close", }; }, AllowReplacement: URIReplacement.Fail); return; }
/// <summary> /// Returns resources from the given file system location. /// </summary> /// <param name="HTTPServer">A HTTP server.</param> /// <param name="URITemplate">An URI template.</param> /// <param name="DefaultFilename">The default file to load.</param> public static void RegisterWatchedFileSystemFolder(this HTTPServer HTTPServer, HTTPHostname Hostname, String URITemplate, String FileSystemLocation, String HTTPSSE_EventIdentification, String HTTPSSE_URITemplate, // Func<String[], String> ResourcePath, String DefaultFilename = "index.html") { #region Setup file system watcher var watcher = new FileSystemWatcher() { Path = FileSystemLocation, NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite, //Filter = "*.html",//|*.css|*.js|*.json", IncludeSubdirectories = true, InternalBufferSize = 4 * 4096 }; watcher.Created += (s, e) => FileWasChanged(HTTPServer, HTTPSSE_EventIdentification, e.ChangeType.ToString(), e.FullPath.Remove(0, FileSystemLocation.Length).Replace(Path.DirectorySeparatorChar, '/')); watcher.Changed += (s, e) => FileWasChanged(HTTPServer, HTTPSSE_EventIdentification, e.ChangeType.ToString(), e.FullPath.Remove(0, FileSystemLocation.Length).Replace(Path.DirectorySeparatorChar, '/')); watcher.Renamed += FileWasRenamed; watcher.Deleted += (s, e) => FileWasChanged(HTTPServer, HTTPSSE_EventIdentification, e.ChangeType.ToString(), e.FullPath.Remove(0, FileSystemLocation.Length).Replace(Path.DirectorySeparatorChar, '/')); watcher.Error += FileWatcherError; #endregion HTTPServer.AddEventSource(HTTPSSE_EventIdentification, URITemplate: HTTPSSE_URITemplate); HTTPServer.AddMethodCallback(Hostname, HTTPMethod.GET, URITemplate + (URITemplate.EndsWith("/", StringComparison.InvariantCulture) ? "{ResourceName}" : "/{ResourceName}"), HTTPContentType.PNG, HTTPDelegate: async Request => { HTTPContentType ResponseContentType = null; var NumberOfTemplateParameters = URITemplate.Count(c => c == '{'); var FilePath = (Request.ParsedURIParameters != null && Request.ParsedURIParameters.Length > NumberOfTemplateParameters) ? Request.ParsedURIParameters.Last().Replace('/', Path.DirectorySeparatorChar) : DefaultFilename.Replace('/', Path.DirectorySeparatorChar); try { var FileStream = File.OpenRead(FileSystemLocation + Path.DirectorySeparatorChar + FilePath); if (FileStream != null) { #region Choose HTTP Content Type based on the file name extention... ResponseContentType = HTTPContentType.ForFileExtention(FilePath.Remove(0, FilePath.LastIndexOf(".", StringComparison.InvariantCulture) + 1), () => HTTPContentType.OCTETSTREAM).FirstOrDefault(); #endregion #region Create HTTP Response return new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.OK, Server = HTTPServer.DefaultServerName, Date = DateTime.Now, ContentType = ResponseContentType, ContentStream = FileStream, CacheControl = "public, max-age=300", //Expires = "Mon, 25 Jun 2015 21:31:12 GMT", KeepAlive = new KeepAliveType(TimeSpan.FromMinutes(5), 500), Connection = "Keep-Alive" }; #endregion } } catch (FileNotFoundException e) { } return new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.NotFound, Server = HTTPServer.DefaultServerName, Date = DateTime.Now, ContentType = HTTPContentType.TEXT_UTF8, Content = "Error 404 - Not found!".ToUTF8Bytes(), CacheControl = "no-cache", Connection = "close", }; }, AllowReplacement: URIReplacement.Fail); // And now my watch begins... watcher.EnableRaisingEvents = true; }
// Method Callbacks #region (internal) AddHandler(HTTPDelegate, Hostname = "*", URITemplate = "/", HTTPMethod = null, HTTPContentType = null, HostAuthentication = null, URIAuthentication = null, HTTPMethodAuthentication = null, ContentTypeAuthentication = null, DefaultErrorHandler = null) /// <summary> /// Add a method callback for the given URI template. /// </summary> /// <param name="HTTPDelegate">A delegate called for each incoming HTTP request.</param> /// <param name="Hostname">The HTTP hostname.</param> /// <param name="URITemplate">The URI template.</param> /// <param name="HTTPMethod">The HTTP method.</param> /// <param name="HTTPContentType">The HTTP content type.</param> /// <param name="HostAuthentication">Whether this method needs explicit host authentication or not.</param> /// <param name="URIAuthentication">Whether this method needs explicit uri authentication or not.</param> /// <param name="HTTPMethodAuthentication">Whether this method needs explicit HTTP method authentication or not.</param> /// <param name="ContentTypeAuthentication">Whether this method needs explicit HTTP content type authentication or not.</param> /// <param name="DefaultErrorHandler">The default error handler.</param> internal void AddHandler(HTTPDelegate HTTPDelegate, HTTPHostname Hostname = null, String URITemplate = "/", HTTPMethod HTTPMethod = null, HTTPContentType HTTPContentType = null, HTTPAuthentication HostAuthentication = null, HTTPAuthentication URIAuthentication = null, HTTPAuthentication HTTPMethodAuthentication = null, HTTPAuthentication ContentTypeAuthentication = null, HTTPDelegate DefaultErrorHandler = null, URIReplacement AllowReplacement = URIReplacement.Fail) { lock (Lock) { #region Initial Checks if (HTTPDelegate == null) throw new ArgumentNullException("HTTPDelegate", "The given parameter must not be null!"); if (Hostname == null) Hostname = HTTPHostname.Any; if (URITemplate.IsNullOrEmpty()) URITemplate = "/"; if (HTTPMethod == null && HTTPContentType != null) throw new ArgumentNullException("If HTTPMethod is null the HTTPContentType must also be null!"); #endregion #region AddOrUpdate HostNode HostnameNode _HostnameNode = null; if (!_HostnameNodes.TryGetValue(Hostname, out _HostnameNode)) { _HostnameNode = new HostnameNode(Hostname, HostAuthentication, HTTPDelegate, DefaultErrorHandler); _HostnameNodes.Add(Hostname, _HostnameNode); } #endregion _HostnameNode.AddHandler(HTTPDelegate, URITemplate, HTTPMethod, HTTPContentType, URIAuthentication, HTTPMethodAuthentication, ContentTypeAuthentication, DefaultErrorHandler, AllowReplacement); } }
/// <summary> /// Add a method call back for the given URI template and /// add a HTTP Sever Sent Events source. /// </summary> /// <param name="EventIdentification">The unique identification of the event source.</param> /// <param name="MaxNumberOfCachedEvents">Maximum number of cached events.</param> /// <param name="RetryIntervall">The retry intervall.</param> /// /// <param name="Hostname">The HTTP host.</param> /// <param name="URITemplate">The URI template.</param> /// <param name="HTTPMethod">The HTTP method.</param> /// <param name="HTTPContentType">The HTTP content type.</param> /// /// <param name="HostAuthentication">Whether this method needs explicit host authentication or not.</param> /// <param name="URIAuthentication">Whether this method needs explicit uri authentication or not.</param> /// <param name="HTTPMethodAuthentication">Whether this method needs explicit HTTP method authentication or not.</param> /// /// <param name="DefaultErrorHandler">The default error handler.</param> internal HTTPEventSource AddEventSource(String EventIdentification, UInt32 MaxNumberOfCachedEvents = 500, TimeSpan? RetryIntervall = null, HTTPHostname Hostname = null, String URITemplate = "/", HTTPMethod HTTPMethod = null, HTTPContentType HTTPContentType = null, HTTPAuthentication HostAuthentication = null, HTTPAuthentication URIAuthentication = null, HTTPAuthentication HTTPMethodAuthentication = null, HTTPDelegate DefaultErrorHandler = null) { lock (Lock) { #region Get or Create Event Source HTTPEventSource _HTTPEventSource; if (!_EventSources.TryGetValue(EventIdentification, out _HTTPEventSource)) _HTTPEventSource = _EventSources.AddAndReturnValue(EventIdentification, new HTTPEventSource(EventIdentification, MaxNumberOfCachedEvents, RetryIntervall)); #endregion #region Define HTTP Delegate HTTPDelegate _HTTPDelegate = Request => { var _LastEventId = 0UL; var _Client_LastEventId = 0UL; var _EventSource = GetEventSource(EventIdentification); if (Request.TryGet<UInt64>("Last-Event-ID", out _Client_LastEventId)) _LastEventId = _Client_LastEventId; var _HTTPEvents = (from _HTTPEvent in _EventSource.GetAllEventsGreater(_LastEventId) where _HTTPEvent != null select _HTTPEvent.ToString()) .ToArray(); // For thread safety! // Transform HTTP events into an UTF8 string var _ResourceContent = String.Empty; if (_HTTPEvents.Length > 0) _ResourceContent = Environment.NewLine + _HTTPEvents.Aggregate((a, b) => a + Environment.NewLine + b) + Environment.NewLine; else _ResourceContent += Environment.NewLine + "retry: " + ((UInt32) _EventSource.RetryIntervall.TotalMilliseconds) + Environment.NewLine + Environment.NewLine; return Task.FromResult<HTTPResponse>( new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.OK, ContentType = HTTPContentType.EVENTSTREAM, CacheControl = "no-cache", Connection = "keep-alive", KeepAlive = new KeepAliveType(TimeSpan.FromSeconds(2*_EventSource.RetryIntervall.TotalSeconds)), Content = _ResourceContent.ToUTF8Bytes() }); }; #endregion AddHandler(_HTTPDelegate, Hostname, URITemplate, HTTPMethod ?? HTTPMethod.GET, HTTPContentType.EVENTSTREAM, HostAuthentication, URIAuthentication, HTTPMethodAuthentication, null, DefaultErrorHandler); return _HTTPEventSource; } }
/// <summary> /// Return the best matching method handler for the given parameters. /// </summary> internal HTTPDelegate GetHandler(HTTPHostname Host, String URI, HTTPMethod HTTPMethod = null, Func<HTTPContentType[], HTTPContentType> HTTPContentTypeSelector = null, Action<IEnumerable<String>> ParsedURIParametersDelegate = null) { Host = Host ?? HTTPHostname.Any; URI = URI.IsNullOrEmpty() ? "/" : URI; HTTPMethod = HTTPMethod ?? HTTPMethod.GET; HTTPContentTypeSelector = HTTPContentTypeSelector ?? (v => HTTPContentType.HTML_UTF8); lock (Lock) { #region Get HostNode or "*" or fail HostnameNode _HostNode = null; if (!_HostnameNodes.TryGetValue(Host, out _HostNode)) if (!_HostnameNodes.TryGetValue(HTTPHostname.Any, out _HostNode)) return null; //return GetErrorHandler(Host, URL, HTTPMethod, HTTPContentType, HTTPStatusCode.BadRequest); #endregion #region Try to find the best matching URLNode... var _RegexList = from __URLNode in _HostNode.URINodes.Values select new { URLNode = __URLNode, Regex = __URLNode.URIRegex }; var _AllTemplates = from _RegexTupel in _RegexList select new { URLNode = _RegexTupel.URLNode, Match = _RegexTupel.Regex.Match(URI) }; var _Matches = from _Match in _AllTemplates where _Match.Match.Success orderby 100*_Match.URLNode.SortLength + _Match.URLNode.ParameterCount descending select new { URLNode = _Match.URLNode, Match = _Match.Match }; #endregion #region ...or return HostNode if (!_Matches.Any()) { //if (_HostNode.RequestHandler != null) // return _HostNode.RequestHandler; return null; } #endregion HTTPMethodNode _HTTPMethodNode = null; ContentTypeNode _HTTPContentTypeNode = null; // Caused e.g. by the naming of the variables within the // URI templates, there could be multiple matches! //foreach (var _Match in _Matches) //{ // Use best matching URL Handler! var _Match2 = _Matches.First(); #region Copy MethodHandler Parameters var _Parameters = new List<String>(); for (var i = 1; i < _Match2.Match.Groups.Count; i++) _Parameters.Add(_Match2.Match.Groups[i].Value.RemoveLastSlash()); var ParsedURIParametersDelegateLocal = ParsedURIParametersDelegate; if (ParsedURIParametersDelegateLocal != null) ParsedURIParametersDelegate(_Parameters); #endregion // If HTTPMethod was found... if (_Match2.URLNode.HTTPMethods.TryGetValue(HTTPMethod, out _HTTPMethodNode)) { var BestMatchingContentType = HTTPContentTypeSelector(_HTTPMethodNode.HTTPContentTypes.Keys.ToArray()); // Get HTTPContentTypeNode if (!_HTTPMethodNode.HTTPContentTypes.TryGetValue(BestMatchingContentType, out _HTTPContentTypeNode)) return _HTTPMethodNode.RequestHandler; return _HTTPContentTypeNode.RequestHandler; } //} // No HTTPMethod was found => return best matching URL Handler return _Match2.URLNode.RequestHandler; //return GetErrorHandler(Host, URL, HTTPMethod, HTTPContentType, HTTPStatusCode.BadRequest); } }
/// <summary> /// Register a SOAP delegate. /// </summary> /// <param name="Hostname">The HTTP hostname.</param> /// <param name="URITemplate">The URI template.</param> /// <param name="Description">A description of this SOAP delegate.</param> /// <param name="SOAPMatch">A delegate to check whether this dispatcher matches the given XML.</param> /// <param name="SOAPHeaderAndBodyDelegate">A delegate to process a matching SOAP request.</param> public void RegisterSOAPDelegate(HTTPHostname Hostname, String URITemplate, String Description, SOAPMatch SOAPMatch, SOAPHeaderAndBodyDelegate SOAPHeaderAndBodyDelegate) { SOAPDispatcher _SOAPDispatcher = null; // Check if there are other SOAP dispatchers at the given URI template. var _Handler = GetHandler(HTTPHostname.Any, URITemplate, HTTPMethod.POST, ContentTypes => SOAPContentType); if (_Handler == null) { _SOAPDispatcher = new SOAPDispatcher(URITemplate, SOAPContentType); _SOAPDispatchers.Add(URITemplate, _SOAPDispatcher); // Register a new SOAP dispatcher AddMethodCallback(Hostname, HTTPMethod.POST, URITemplate, SOAPContentType, HTTPDelegate: _SOAPDispatcher.Invoke); // Register some information text for people using HTTP GET AddMethodCallback(Hostname, HTTPMethod.GET, URITemplate, SOAPContentType, HTTPDelegate: _SOAPDispatcher.EndpointTextInfo); } else _SOAPDispatcher = _Handler.Target as SOAPDispatcher; _SOAPDispatcher.RegisterSOAPDelegate(Description, SOAPMatch, SOAPHeaderAndBodyDelegate); }
/// <summary> /// Returns internal resources embedded within the given assembly. /// </summary> /// <param name="HTTPServer">A HTTP server.</param> /// <param name="Hostname">The HTTP hostname.</param> /// <param name="URITemplate">An URI template.</param> /// <param name="ResourcePath">The path to the file within the assembly.</param> /// <param name="ResourceAssembly">Optionally the assembly where the resources are located (default: the calling assembly).</param> /// <param name="DefaultFilename">The default file to load.</param> /// <param name="HTTPRealm">An optional realm for HTTP basic authentication.</param> /// <param name="HTTPLogin">An optional login for HTTP basic authentication.</param> /// <param name="HTTPPassword">An optional password for HTTP basic authentication.</param> public static void RegisterResourcesFolder(this HTTPServer HTTPServer, HTTPHostname Hostname, String URITemplate, String ResourcePath, Assembly ResourceAssembly = null, String DefaultFilename = "index.html", String HTTPRealm = null, String HTTPLogin = null, String HTTPPassword = null) { if (ResourceAssembly == null) ResourceAssembly = Assembly.GetCallingAssembly(); HTTPDelegate GetEmbeddedResources = async Request => { #region Check HTTP Basic Authentication if (HTTPLogin.IsNotNullOrEmpty() && HTTPPassword.IsNotNullOrEmpty()) { if (Request.Authorization == null || Request.Authorization.Username != HTTPLogin || Request.Authorization.Password != HTTPPassword) return new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.Unauthorized, Server = HTTPServer.DefaultServerName, Date = DateTime.Now, WWWAuthenticate = @"Basic realm=""" + HTTPRealm + @"""", ContentType = HTTPContentType.TEXT_UTF8, Content = "Unauthorized Access!".ToUTF8Bytes(), Connection = "close" }; } #endregion HTTPContentType ResponseContentType = null; var FilePath = (Request.ParsedURIParameters != null && Request.ParsedURIParameters.Length > 0) ? Request.ParsedURIParameters.Last().Replace("/", ".") : DefaultFilename.Replace("/", "."); var FileStream = ResourceAssembly.GetManifestResourceStream(ResourcePath + "." + FilePath); if (FileStream != null) { #region Choose HTTP Content Type based on the file name extention... var FileName = FilePath.Substring(FilePath.LastIndexOf("/") + 1); // Get the appropriate content type based on the suffix of the requested resource switch (FileName.Remove(0, FileName.LastIndexOf(".") + 1)) { case "htm" : ResponseContentType = HTTPContentType.HTML_UTF8; break; case "html": ResponseContentType = HTTPContentType.HTML_UTF8; break; case "css" : ResponseContentType = HTTPContentType.CSS_UTF8; break; case "gif" : ResponseContentType = HTTPContentType.GIF; break; case "jpg" : ResponseContentType = HTTPContentType.JPEG; break; case "jpeg": ResponseContentType = HTTPContentType.JPEG; break; case "svg" : ResponseContentType = HTTPContentType.SVG; break; case "png" : ResponseContentType = HTTPContentType.PNG; break; case "ico" : ResponseContentType = HTTPContentType.ICO; break; case "swf" : ResponseContentType = HTTPContentType.SWF; break; case "js" : ResponseContentType = HTTPContentType.JAVASCRIPT_UTF8; break; case "txt" : ResponseContentType = HTTPContentType.TEXT_UTF8; break; case "xml": ResponseContentType = HTTPContentType.XML_UTF8; break; default: ResponseContentType = HTTPContentType.OCTETSTREAM; break; } #endregion #region Create HTTP Response return new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.OK, Server = HTTPServer.DefaultServerName, Date = DateTime.Now, ContentType = ResponseContentType, ContentStream = FileStream, CacheControl = "public, max-age=300", //Expires = "Mon, 25 Jun 2015 21:31:12 GMT", KeepAlive = new KeepAliveType(TimeSpan.FromMinutes(5), 500), Connection = "Keep-Alive", }; #endregion } else { #region Try to find a appropriate customized errorpage... Stream ErrorStream = null; Request.BestMatchingAcceptType = Request.Accept.BestMatchingContentType(new HTTPContentType[] { HTTPContentType.HTML_UTF8, HTTPContentType.TEXT_UTF8 }); if (Request.BestMatchingAcceptType == HTTPContentType.HTML_UTF8) { ResponseContentType = HTTPContentType.HTML_UTF8; ErrorStream = ResourceAssembly.GetManifestResourceStream(ResourcePath.Substring(0, ResourcePath.LastIndexOf(".")) + ".ErrorPages." + "404.html"); } else if (Request.BestMatchingAcceptType == HTTPContentType.TEXT_UTF8) { ResponseContentType = HTTPContentType.TEXT_UTF8; ErrorStream = ResourceAssembly.GetManifestResourceStream(ResourcePath.Substring(0, ResourcePath.LastIndexOf(".")) + ".ErrorPages." + "404.txt"); } else if (Request.BestMatchingAcceptType == HTTPContentType.JSON_UTF8) { ResponseContentType = HTTPContentType.JSON_UTF8; ErrorStream = ResourceAssembly.GetManifestResourceStream(ResourcePath.Substring(0, ResourcePath.LastIndexOf(".")) + ".ErrorPages." + "404.js"); } else if (Request.BestMatchingAcceptType == HTTPContentType.XML_UTF8) { ResponseContentType = HTTPContentType.XML_UTF8; ErrorStream = ResourceAssembly.GetManifestResourceStream(ResourcePath.Substring(0, ResourcePath.LastIndexOf(".")) + ".ErrorPages." + "404.xml"); } else if (Request.BestMatchingAcceptType == HTTPContentType.ALL) { ResponseContentType = HTTPContentType.HTML_UTF8; ErrorStream = ResourceAssembly.GetManifestResourceStream(ResourcePath.Substring(0, ResourcePath.LastIndexOf(".")) + ".ErrorPages." + "404.html"); } if (ErrorStream != null) return new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.NotFound, Server = HTTPServer.DefaultServerName, Date = DateTime.Now, ContentType = ResponseContentType, ContentStream = ErrorStream, CacheControl = "no-cache", Connection = "close", }; #endregion #region ...or send a default error page! else return new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.NotFound, Server = HTTPServer.DefaultServerName, Date = DateTime.Now, CacheControl = "no-cache", Connection = "close", }; #endregion } }; // ~/map HTTPServer.AddMethodCallback(Hostname, HTTPMethod.GET, URITemplate.EndsWith("/", StringComparison.InvariantCulture) ? URITemplate.Substring(0, URITemplate.Length) : URITemplate, HTTPDelegate: GetEmbeddedResources); // ~/map/ HTTPServer.AddMethodCallback(Hostname, HTTPMethod.GET, URITemplate + (URITemplate.EndsWith("/", StringComparison.InvariantCulture) ? "" : "/"), HTTPDelegate: GetEmbeddedResources); // ~/map/file.name HTTPServer.AddMethodCallback(Hostname, HTTPMethod.GET, URITemplate + (URITemplate.EndsWith("/", StringComparison.InvariantCulture) ? "{ResourceName}" : "/{ResourceName}"), HTTPDelegate: GetEmbeddedResources); }
/// <summary> /// Add a method callback for the given URL template. /// </summary> /// <param name="CommonAPI">The OCPI Common API.</param> /// <param name="Hostname">The HTTP hostname.</param> /// <param name="HTTPMethod">The HTTP method.</param> /// <param name="URLTemplate">The URL template.</param> /// <param name="HTTPContentType">The HTTP content type.</param> /// <param name="URLAuthentication">Whether this method needs explicit uri authentication or not.</param> /// <param name="HTTPMethodAuthentication">Whether this method needs explicit HTTP method authentication or not.</param> /// <param name="ContentTypeAuthentication">Whether this method needs explicit HTTP content type authentication or not.</param> /// <param name="OCPIRequestLogger">A OCPI request logger.</param> /// <param name="OCPIResponseLogger">A OCPI response logger.</param> /// <param name="DefaultErrorHandler">The default error handler.</param> /// <param name="OCPIRequestHandler">The method to call.</param> public static void AddOCPIMethod(this CommonAPI CommonAPI, HTTPHostname Hostname, HTTPMethod HTTPMethod, HTTPPath URLTemplate, HTTPContentType HTTPContentType = null, HTTPAuthentication URLAuthentication = null, HTTPAuthentication HTTPMethodAuthentication = null, HTTPAuthentication ContentTypeAuthentication = null, OCPIRequestLogHandler OCPIRequestLogger = null, OCPIResponseLogHandler OCPIResponseLogger = null, HTTPDelegate DefaultErrorHandler = null, OCPIRequestDelegate OCPIRequestHandler = null, URLReplacement AllowReplacement = URLReplacement.Fail) { CommonAPI.HTTPServer. AddMethodCallback(Hostname, HTTPMethod, URLTemplate, HTTPContentType, URLAuthentication, HTTPMethodAuthentication, ContentTypeAuthentication, (timestamp, httpAPI, httpRequest) => OCPIRequestLogger?.Invoke(timestamp, null, HTTP.OCPIRequest.Parse(httpRequest, CommonAPI)), (timestamp, httpAPI, httpRequest, httpResponse) => OCPIResponseLogger?.Invoke(timestamp, null, httpRequest.SubprotocolRequest as OCPIRequest, (httpResponse.SubprotocolResponse as OCPIResponse) ?? new OCPIResponse(httpRequest.SubprotocolRequest as OCPIRequest, 2000, "OCPIResponse is null!", httpResponse.HTTPBodyAsUTF8String, httpResponse.Timestamp, httpResponse)), DefaultErrorHandler, async httpRequest => { try { // When no OCPIRequestLogger was used! if (httpRequest.SubprotocolRequest is null) { httpRequest.SubprotocolRequest = OCPIRequest.Parse(httpRequest, CommonAPI); } var OCPIResponseBuilder = await OCPIRequestHandler(httpRequest.SubprotocolRequest as OCPIRequest); var httpResponseBuilder = OCPIResponseBuilder.ToHTTPResponseBuilder(); httpResponseBuilder.SubprotocolResponse = new OCPIResponse(OCPIResponseBuilder.Request, OCPIResponseBuilder.StatusCode ?? 3000, OCPIResponseBuilder.StatusMessage, OCPIResponseBuilder.AdditionalInformation, OCPIResponseBuilder.Timestamp ?? DateTime.UtcNow, httpResponseBuilder.AsImmutable); return(httpResponseBuilder); } catch (Exception e) { return(new HTTPResponse.Builder(HTTPStatusCode.InternalServerError) { ContentType = HTTPContentType.JSON_UTF8, Content = new OCPIResponse <JObject>( JSONObject.Create( new JProperty("description", e.Message), new JProperty("stacktrace", e.StackTrace.Split(new String[] { Environment.NewLine }, StringSplitOptions.None).ToArray()), new JProperty("source", e.TargetSite.Module.Name), new JProperty("type", e.TargetSite.ReflectedType.Name) ), 2000, e.Message, null, DateTime.UtcNow, null, (httpRequest.SubprotocolRequest as OCPIRequest)?.RequestId, (httpRequest.SubprotocolRequest as OCPIRequest)?.CorrelationId ).ToJSON(json => json).ToUTF8Bytes(), Connection = "close" }); } }, AllowReplacement); }
/// <summary> /// Returns a resource from the given file system location. /// </summary> /// <param name="HTTPServer">A HTTP server.</param> /// <param name="Hostname">The HTTP hostname.</param> /// <param name="URITemplate">An URI template.</param> /// <param name="ResourceFilenameBuilder">The path to the file within the assembly.</param> /// <param name="DefaultFile">If an error occures, return this file.</param> /// <param name="ResponseContentType">Set the HTTP MIME content-type of the file. If null try to autodetect the content type based on the filename extention.</param> /// <param name="CacheControl">Set the HTTP cache control response header.</param> public static void RegisterFilesystemFile(this HTTPServer HTTPServer, HTTPHostname Hostname, String URITemplate, Func<String[], String> ResourceFilenameBuilder, String DefaultFile = null, HTTPContentType ResponseContentType = null, String CacheControl = "no-cache") { #region Get the appropriate content type based on the suffix returned by the ResourceFilenameBuilder // NumberOfTemplateParameters var _ResourceFilename = ResourceFilenameBuilder(Enumerable.Repeat("", URITemplate.Count(c => c == '{')).ToArray()); if (ResponseContentType == null) switch (_ResourceFilename.Remove(0, _ResourceFilename.LastIndexOf(".") + 1)) { case "htm": ResponseContentType = HTTPContentType.HTML_UTF8; break; case "html": ResponseContentType = HTTPContentType.HTML_UTF8; break; case "css": ResponseContentType = HTTPContentType.CSS_UTF8; break; case "gif": ResponseContentType = HTTPContentType.GIF; break; case "jpg": ResponseContentType = HTTPContentType.JPEG; break; case "jpeg": ResponseContentType = HTTPContentType.JPEG; break; case "svg": ResponseContentType = HTTPContentType.SVG; break; case "png": ResponseContentType = HTTPContentType.PNG; break; case "ico": ResponseContentType = HTTPContentType.ICO; break; case "swf": ResponseContentType = HTTPContentType.SWF; break; case "js": ResponseContentType = HTTPContentType.JAVASCRIPT_UTF8; break; case "txt": ResponseContentType = HTTPContentType.TEXT_UTF8; break; default: ResponseContentType = HTTPContentType.OCTETSTREAM; break; } #endregion HTTPServer.AddMethodCallback(Hostname, HTTPMethod.GET, URITemplate, HTTPContentType: ResponseContentType, HTTPDelegate: async Request => { var ResourceFilename = ResourceFilenameBuilder(Request.ParsedURIParameters); if (!File.Exists(ResourceFilename) && DefaultFile != null) ResourceFilename = DefaultFile; if (File.Exists(ResourceFilename)) { var FileStream = File.OpenRead(ResourceFilename); if (FileStream != null) { return new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.OK, Server = HTTPServer.DefaultServerName, Date = DateTime.Now, ContentType = ResponseContentType, ContentStream = FileStream, CacheControl = CacheControl, Connection = "close", }; } } return new HTTPResponseBuilder(Request) { HTTPStatusCode = HTTPStatusCode.NotFound, Server = HTTPServer.DefaultServerName, Date = DateTime.Now, CacheControl = "no-cache", Connection = "close", }; }, AllowReplacement: URIReplacement.Fail); return; }
/// <summary> /// Create a new OICP roaming client for CPOs. /// </summary> /// <param name="ClientId">A unqiue identification of this client.</param> /// <param name="RemoteHostname">The hostname of the remote OICP service.</param> /// <param name="RemoteTCPPort">An optional TCP port of the remote OICP service.</param> /// <param name="RemoteCertificateValidator">A delegate to verify the remote TLS certificate.</param> /// <param name="ClientCertificateSelector">A delegate to select a TLS client certificate.</param> /// <param name="RemoteHTTPVirtualHost">An optional HTTP virtual hostname of the remote OICP service.</param> /// <param name="HTTPUserAgent">An optional HTTP user agent identification string for this HTTP client.</param> /// <param name="RequestTimeout">An optional timeout for upstream queries.</param> /// <param name="MaxNumberOfRetries">The default number of maximum transmission retries.</param> /// /// <param name="ServerName">An optional identification string for the HTTP server.</param> /// <param name="ServiceName">An optional identification for this SOAP service.</param> /// <param name="ServerTCPPort">An optional TCP port for the HTTP server.</param> /// <param name="ServerURLPrefix">An optional prefix for the HTTP URLs.</param> /// <param name="ServerContentType">An optional HTTP content type to use.</param> /// <param name="ServerRegisterHTTPRootService">Register HTTP root services for sending a notice to clients connecting via HTML or plain text.</param> /// <param name="ServerAutoStart">Whether to start the server immediately or not.</param> /// /// <param name="ClientLoggingContext">An optional context for logging client methods.</param> /// <param name="ServerLoggingContext">An optional context for logging server methods.</param> /// <param name="LogfileCreator">A delegate to create a log file from the given context and log file name.</param> /// /// <param name="DNSClient">An optional DNS client to use.</param> public CPORoaming(String ClientId, HTTPHostname RemoteHostname, IPPort?RemoteTCPPort = null, RemoteCertificateValidationCallback RemoteCertificateValidator = null, LocalCertificateSelectionCallback ClientCertificateSelector = null, HTTPHostname?RemoteHTTPVirtualHost = null, HTTPPath?URLPrefix = null, String EVSEDataURL = CPOClient.DefaultEVSEDataURL, String EVSEStatusURL = CPOClient.DefaultEVSEStatusURL, String AuthorizationURL = CPOClient.DefaultAuthorizationURL, String AuthenticationDataURL = CPOClient.DefaultAuthenticationDataURL, String HTTPUserAgent = CPOClient.DefaultHTTPUserAgent, TimeSpan?RequestTimeout = null, Byte?MaxNumberOfRetries = CPOClient.DefaultMaxNumberOfRetries, String ServerName = CPOSOAPServer.DefaultHTTPServerName, IPPort?ServerTCPPort = null, String ServiceName = null, HTTPPath?ServerURLPrefix = null, String ServerAuthorizationURL = CPOSOAPServer.DefaultAuthorizationURL, String ServerReservationURL = CPOSOAPServer.DefaultReservationURL, HTTPContentType ServerContentType = null, Boolean ServerRegisterHTTPRootService = true, Boolean ServerAutoStart = false, String ClientLoggingContext = CPOClient.CPOClientLogger.DefaultContext, String ServerLoggingContext = CPOServerLogger.DefaultContext, LogfileCreatorDelegate LogfileCreator = null, DNSClient DNSClient = null) : this(new CPOClient(ClientId, RemoteHostname, RemoteTCPPort, RemoteCertificateValidator, ClientCertificateSelector, RemoteHTTPVirtualHost, URLPrefix ?? CPOClient.DefaultURLPrefix, EVSEDataURL, EVSEStatusURL, AuthorizationURL, AuthenticationDataURL, HTTPUserAgent, RequestTimeout, MaxNumberOfRetries, DNSClient, ClientLoggingContext, LogfileCreator), new CPOSOAPServer(ServerName, ServerTCPPort, ServiceName, ServerURLPrefix ?? CPOSOAPServer.DefaultURLPathPrefix, ServerAuthorizationURL, ServerReservationURL, ServerContentType, ServerRegisterHTTPRootService, DNSClient, false), ServerLoggingContext, LogfileCreator) { if (ServerAutoStart) { Start(); } }