Cliente HTML + JS para sistema de vigilancia de línea



En Geektimes, a menudo me encuentro y disfruto leyendo publicaciones de la serie DIY. Habiendo decidido hacer una pequeña contribución al tesoro de valiosa experiencia reunida aquí, voy a describir en detalle el proceso de creación de un cliente para la web basado en los servidores de Line.

El sistema de vigilancia de línea proporciona una API abierta, y los desarrolladores dicen que es posible escribir su propio cliente basado en él para ver el archivo de video y las cámaras en línea. Además, si lo desea, puede implementar funciones como agregar eventos al archivo, superponer OSD en la parte superior del video. Una descripción de todas las características se presenta en la especificación del sitio web oficial .

Este artículo es un ejemplo real de cómo yo, un usuario con un conocimiento inicial de JS, HTML, escribí mi propia aplicación que implementa los principios básicos de trabajar con servidores Line a través del servidor web incorporado.

Datos de entrada

El autor es un principiante en el desarrollo de un cliente HTML y participa en el desarrollo del sistema de videovigilancia Line.
Nivel de conocimiento de JS, HTML - inicial.
La tarea es escribir un cliente HTML para trabajar con dispositivos basados ​​en el software Line utilizando la especificación del sitio.

Revelaré la intriga principal de inmediato: llegué a dos conclusiones:

  1. La especificación es real, se describe con bastante claridad, puede escribir un cliente usando C ++, PHP.
  2. No puede escribir un cliente HTML completo utilizando solo JS, solo monitoreo en línea de acuerdo con la especificación anterior a RPC.

La primera conclusión es bastante lógica, dada la gran cantidad de integraciones con programas de terceros. Todos ellos se describen en el sitio: hay sistemas de control de acceso , peso , sistemas POS , programas para determinar los números de automóviles y 1C .

La segunda conclusión es más interesante, considérelo a continuación.

¿Por qué no puede crear un cliente completo en HTML + JS?


Respuesta: solicitudes de dominio cruzado.

Por el momento, el servidor web de la Línea es limitado, y simplemente copiando el código a la carpeta www, no se puede obtener acceso. Sin embargo, los desarrolladores prometen que en la nueva versión para Linux y en la "Línea 8.0" el servidor web funcionará como estándar: en caso de una solicitud, si hay un archivo, lo devolverá.

Ahora cree un nuevo proyecto y comience a codificar. Como todos los recién llegados a la programación para la web, especificando que el servidor "Líneas" responde "*" en el encabezado Access-Control-Allow-Origin, comencé a trabajar duro en el código, verificando el resultado en Firefox 57.0.4 (64 bits). Las solicitudes al servidor fueron enviadas por XMLHttpRequest.

Inicialmente, sería útil estudiar la información sobre este recurso . Todo se describe con gran detalle allí, pero realmente quería completar rápidamente la tarea. Y, desafortunadamente, debido a la falta de información, se perdió medio día en un golpe en la pared de la política de seguridad de los navegadores modernos.

Al momento de escribir esto, cuatro navegadores modernos principales no permiten leer encabezados recibidos del servidor. Según la especificación, es necesario implementar la autenticación implícita, que es imposible sin encabezados.

Al final del primer día, me di cuenta de que sin agregar el procesamiento de OPCIONES al servidor web de la Línea, nada funcionará, ya que para solicitudes con un método "difícil" o encabezados especiales, el navegador realiza una solicitud previa de OPCIONES, indicándolas en el Método de solicitud de control de acceso y encabezados de solicitud de control de acceso. Entonces comencé a buscar otras opciones de autorización, pero el verdadero Basic o Digest no despegó.

Ya se ha descrito un método alternativo en la especificación, quedaba pasar algún tiempo correspondencia con el departamento de programa de "Líneas". Dado que tales dificultades no surgen por primera vez, ya existe una muleta para la autorización, e incluso se menciona en la especificación:
En los clientes en los que es imposible autorizar la solicitud utilizando medios estándar (HTTP Digest / Autenticación básica), el encabezado de autorización se puede enviar utilizando uno de los parámetros de la solicitud, por ejemplo
/kfd3ado1sdrms/streaming/main.flv?authorization=Basic%20d2ViOg==

Después de todas las manipulaciones, ¡la solicitud estándar de dominio cruzado comenzó a ejecutarse correctamente! También es necesario agregar el encabezado Aceptar con el tipo correcto a la solicitud. Decidí usar JSON.

Código de solicitud:

function get_request_url(method,current_server_data, resource, additional){ var request = current_server_data.server_ip + ':' +current_server_data.port +resource+'?authorization=Basic '+ utf8_to_b64(current_server_data.user+':'+current_server_data.password); if (additional != '' && typeof additional != "undefined") { request += '&' + additional; } return request; } function http_request_of_resource (server_index , resource, auth_attempt) { var request = get_request_url('GET', servers_array[server_index], resource,''); var req_ = new XMLHttpRequest(); req_.open('GET', 'http://'+ request, true); //req_.timeout = 9000; //  ,    req_.onreadystatechange = function() { if (this.readyState == 2) { if (this.status == 401) { //console.log('---unauthorized'); hideModal(); update_nessecary_structure(resource, 'unauthorized', server_index); } } else if (this.readyState === 4) { if (this.status === 0) { hideModal(); update_nessecary_structure(resource, 'server_down',server_index) } if (this.status == 200) { if (auth_attempt) hideModal(); else resource =(resource =='/cameras') ? resource+'_update_info': resource; //console.log('200' + this.responseText); update_nessecary_structure(resource, this.responseText, server_index); } else if (this.status == 404) { //console.log('404'); update_nessecary_structure(resource, '404', server_index); } } }; //   req_.setRequestHeader('Content-type', 'text/plain; charset=utf-8'); req_.setRequestHeader('Accept', 'application/json'); req_.send(); } 

Cambiamos el recurso al que necesitamos de acuerdo con la especificación y obtenemos ciertos datos. La variable adicional contiene parámetros adicionales para la solicitud, si es necesario. En este sentido, el desarrollo de la primera mitad de la especificación, a saber, la recepción / envío de datos de texto a través de solicitudes GET, puede considerarse cerrado.

Además, me encontré con el hecho de que la etiqueta IMG en IE no reproduce la transmisión MJPEG, y necesita implementar de forma independiente la actualización de imágenes de las cámaras. El código está abierto, se puede ver y cambiar si lo desea. En la implementación actual, está disponible la reproducción simultánea de un máximo de seis transmisiones MJPEG, por lo que tendrá que hacer el trabajo con una vista que muestre más cámaras. Todo esto está en el ejemplo , si lo desea, puede encontrar y comprender, pero si tiene preguntas, asegúrese de preguntar en los comentarios.

Especificación RPC


Estamos invitados a enviar y recibir datos ya sea en JSON (versión del servidor "Líneas 7.1.1" y superior) o MessagePack (versión "Línea 7.0" y superior). Se menciona que MessagePack pesa menos y funciona más rápido, pero para ser honesto, elegiría JSON (ya está integrado en JS), si no es por una cosa, pero en la especificación: recibir marcos del archivo solo es posible en MessagePack. Tuve que ir a su sitio web oficial y descargar el archivo JS, que tiene los métodos de codificación y decodificación a bordo.

¡La función de envío de solicitudes está lista! Pero es demasiado pronto para celebrar la victoria: cuando intenta cambiar el encabezado de la solicitud de tipo de contenido, el navegador jura y no envía datos al servidor. El hecho es que el servidor de Líneas analiza este campo y lo analiza según el tipo. No podría hacerlo solo.

Envié una solicitud al departamento de programas, y después de la discusión, me agregaron una muleta, como en el caso de la autorización, el tipo de contenido se transmitirá en la solicitud de URL:

 function rpc_request_of_resource (current_server_data , rpc_method, rpc_request) { var request = get_request_url('POST', current_server_data, '/rpc',''); //console.log("i'm here request = " + request + ' '+ current_server_data.user); request += "&content-type='application/x-msgpack'"; var req_ = new XMLHttpRequest(); req_.open('POST', 'http://'+ request, true); //  ,    req_.responseType = 'arraybuffer'; req_.onreadystatechange = function() { if (this.readyState == 2) { if (this.status == 401) { //console.log('401' + this.getAllResponseHeaders()); console.log('unauthorized'); } } else if (this.readyState == 4) { if (this.status == 200) { //if (auth_attempt) hideModal(); //console.log('200' + this.responseText); rpc_update_nessecary_method(rpc_method, this.response); } else if (this.status == 404) { console.log('404'); } else if (this.status == 500) { //console.log('500'); rpc_update_nessecary_method(rpc_method, '500'); } } }; //   //req_.setRequestHeader('Content-type', 'text/plain; charset=utf-8'); //req_.setRequestHeader('Content-type', 'application/x-msgpack'); req_.setRequestHeader('Accept', 'application/x-msgpack'); req_.send(rpc_request); } 

Este cambio funcionará con la versión "Línea 7.4.1" y superior. Para todos los servidores por debajo de esta versión, el trabajo con el recurso / rpc no estará disponible.

Al final, quiero agradecer a todos los clientes que nos enviaron preguntas / deseos relacionados con la implementación de aplicaciones basadas en nuestra API. Gracias a usted, se realizó un estudio, en cuyo marco se identificaron y corrigieron algunas deficiencias.

El ejemplo que se describe en este artículo se convertirá gradualmente en un cliente HTML completo para Lines. Todo el código será legible, puede cambiarlo o usarlo como base para crear sus propias soluciones. La API, con el tiempo, se completará con aún más funciones, sobre las cuales definitivamente informaremos.

Source: https://habr.com/ru/post/es409999/


All Articles