internal void OnActionCalled(CpAction action, IList <object> inParams, object clientState) { if (!action.IsConnected) { throw new UPnPDisconnectedException("Action '{0}' is not connected to a UPnP network action", action.FullQualifiedName); } CpService service = action.ParentService; ServiceDescriptor sd = GetServiceDescriptor(service); string message = SOAPHandler.EncodeCall(action, inParams, _rootDescriptor.SSDPRootEntry.UPnPVersion); HttpWebRequest request = CreateActionCallRequest(sd, action); ActionCallState state = new ActionCallState(action, clientState, request); state.SetRequestMessage(message); lock (_cpData.SyncObj) _pendingCalls.Add(state); IAsyncResult result = state.Request.BeginGetResponse(OnCallResponseReceived, state); NetworkHelper.AddTimeout(request, result, PENDING_ACTION_CALL_TIMEOUT * 1000); }
private void OnCallResponseReceived(IAsyncResult ar) { ActionCallState state = (ActionCallState)ar.AsyncState; lock (_cpData.SyncObj) _pendingCalls.Remove(state); HttpWebResponse response = null; Stream body = null; try { Encoding contentEncoding; try { response = (HttpWebResponse)state.Request.EndGetResponse(ar); body = CompressionHelper.Decompress(response); string mediaType; if (!EncodingUtils.TryParseContentTypeEncoding(response.ContentType, Encoding.UTF8, out mediaType, out contentEncoding) || mediaType != "text/xml") { SOAPHandler.ActionFailed(state.Action, state.ClientState, "Invalid content type"); return; } } catch (WebException e) { response = (HttpWebResponse)e.Response; if (response == null) { SOAPHandler.ActionFailed(state.Action, state.ClientState, string.Format("Network error when invoking action '{0}': {1}", state.Action.Name, e.Message)); } else if (response.StatusCode == HttpStatusCode.InternalServerError) { string mediaType; if (!EncodingUtils.TryParseContentTypeEncoding(response.ContentType, Encoding.UTF8, out mediaType, out contentEncoding) || mediaType != "text/xml") { SOAPHandler.ActionFailed(state.Action, state.ClientState, "Invalid content type"); return; } using (TextReader reader = new StreamReader(response.GetResponseStream(), contentEncoding)) SOAPHandler.HandleErrorResult(reader, state.Action, state.ClientState); } else { SOAPHandler.ActionFailed(state.Action, state.ClientState, string.Format("Network error {0} when invoking action '{1}'", response.StatusCode, state.Action.Name)); } return; } UPnPVersion uPnPVersion; lock (_cpData.SyncObj) uPnPVersion = _rootDescriptor.SSDPRootEntry.UPnPVersion; SOAPHandler.HandleResult(body, contentEncoding, state.Action, state.ClientState, uPnPVersion); } finally { if (body != null) { body.Dispose(); } if (response != null) { response.Close(); } } }
/// <summary> /// Handles all kinds of HTTP over TCP requests - Description, Control and Event subscriptions. /// </summary> /// <param name="context">HTTP client context of the current request.</param> protected async Task HandleHTTPRequest(IOwinContext context) { var request = context.Request; var response = context.Response; Uri uri = request.Uri; string hostName = uri.Host; string pathAndQuery = uri.LocalPath; // Unfortunately, Uri.PathAndQuery doesn't decode characters like '{' and '}', so we use the Uri.LocalPath property try { DvService service; ICollection <EndpointConfiguration> endpoints; lock (_serverData.SyncObj) endpoints = _serverData.UPnPEndPoints; foreach (EndpointConfiguration config in endpoints) { if (!NetworkHelper.HostNamesEqual(hostName, NetworkHelper.IPAddrToHostName(config.EndPointIPAddress))) { continue; } // Common check for supported encodings string acceptEncoding = request.Headers.Get("ACCEPT-ENCODING") ?? string.Empty; // Handle different HTTP methods here if (request.Method == "GET") { // GET of descriptions if (pathAndQuery.StartsWith(config.DescriptionPathBase)) { string acceptLanguage = request.Headers.Get("ACCEPT-LANGUAGE"); CultureInfo culture = GetFirstCultureOrDefault(acceptLanguage, CultureInfo.InvariantCulture); string description = null; DvDevice rootDevice; lock (_serverData.SyncObj) if (config.RootDeviceDescriptionPathsToRootDevices.TryGetValue(pathAndQuery, out rootDevice)) { description = rootDevice.BuildRootDeviceDescription(request, _serverData, config, culture); } else if (config.SCPDPathsToServices.TryGetValue(pathAndQuery, out service)) { description = service.BuildSCPDDocument(config, _serverData); } if (description != null) { response.StatusCode = (int)HttpStatusCode.OK; response.ContentType = "text/xml; charset=utf-8"; if (!string.IsNullOrEmpty(acceptLanguage)) { response.Headers["CONTENT-LANGUAGE"] = culture.ToString(); } using (MemoryStream responseStream = new MemoryStream(UPnPConsts.UTF8_NO_BOM.GetBytes(description))) await CompressionHelper.WriteCompressedStream(acceptEncoding, response, responseStream); return; } } } else if (request.Method == "POST") { // POST of control messages if (config.ControlPathsToServices.TryGetValue(pathAndQuery, out service)) { string contentType = request.Headers.Get("CONTENT-TYPE"); string userAgentStr = request.Headers.Get("USER-AGENT"); int minorVersion; if (string.IsNullOrEmpty(userAgentStr)) { minorVersion = 0; } else if (!ParserHelper.ParseUserAgentUPnP1MinorVersion(userAgentStr, out minorVersion)) { response.StatusCode = (int)HttpStatusCode.BadRequest; return; } string mediaType; Encoding encoding; if (!EncodingUtils.TryParseContentTypeEncoding(contentType, Encoding.UTF8, out mediaType, out encoding)) { throw new ArgumentException("Unable to parse content type"); } if (mediaType != "text/xml") { // As specified in (DevArch), 3.2.1 response.StatusCode = (int)HttpStatusCode.UnsupportedMediaType; return; } response.Headers["DATE"] = DateTime.Now.ToUniversalTime().ToString("R"); response.Headers["SERVER"] = UPnPConfiguration.UPnPMachineInfoHeader; response.Headers["CONTENT-TYPE"] = "text/xml; charset=\"utf-8\""; string result; HttpStatusCode status; try { CallContext callContext = new CallContext(request, context, config); status = SOAPHandler.HandleRequest(service, request.Body, encoding, minorVersion >= 1, callContext, out result); } catch (Exception e) { UPnPConfiguration.LOGGER.Warn("Action invocation failed", e); result = SOAPHandler.CreateFaultDocument(501, "Action failed"); status = HttpStatusCode.InternalServerError; } response.StatusCode = (int)status; using (MemoryStream responseStream = new MemoryStream(encoding.GetBytes(result))) await CompressionHelper.WriteCompressedStream(acceptEncoding, response, responseStream); return; } } else if (request.Method == "SUBSCRIBE" || request.Method == "UNSUBSCRIBE") { GENAServerController gsc; lock (_serverData.SyncObj) gsc = _serverData.GENAController; if (gsc.HandleHTTPRequest(request, context, config)) { return; } } else { context.Response.StatusCode = (int)HttpStatusCode.MethodNotAllowed; //context.Respond(HttpHelper.HTTP11, HttpStatusCode.MethodNotAllowed, null); return; } } // Url didn't match context.Response.StatusCode = (int)HttpStatusCode.NotFound; //context.Respond(HttpHelper.HTTP11, HttpStatusCode.NotFound, null); } catch (Exception e) { UPnPConfiguration.LOGGER.Error("UPnPServer: Error handling HTTP request '{0}'", e, uri); response.StatusCode = (int)HttpStatusCode.InternalServerError; } }
private void SOAPCallback(IAsyncResult result) { SOAPHandler handler = (SOAPHandler)result.AsyncState; handler.EndInvoke(result); }