
В мобильном приложении, которое я разрабатываю, я использую WebSockets для подписки/публикации различной информации. В большинстве случаев веб-сокет на стороне приложения — это просто слушатель. Однако бывают случаи, когда он также публикует сообщение.
На стороне сервера я справляюсь с этим, используя комбинацию [NChan][1], работающую поверх Nginx. Одной из действительно приятных особенностей NChan является возможность "мультиплексировать" веб-сокет, т. е. размещать его на нескольких каналах. При создании мобильного приложения это имеет большое значение для улучшения аккуратности и экономии заряда батареи.
Как объяснено в [документации NChan][2], я настроил мультиплексные pubsub
каналы
location ~ /pubsub/(\w+)/(\w+)/(\w+)/(\w+)/(\w+)$
{
nchan_pubsub;
nchan_channel_id "$1" "$2" "$3" "$4" "$5" "control";
nchan_publisher_upstream_request /alter;
}
Третья строка выше chan_publisher_upstream...
перенаправляет входящие pub
запросы на другой сервер.
location = /alter
{
proxy_pass https://example.com;
proxy_set_header X-Publisher-Type $nchan_publisher_type;
proxy_set_header X-Prev-Message-Id $nchan_prev_message_id;
proxy_set_header X-Channel-Id $nchan_channel_id;
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
На другом сервере я настроил Nginx для запуска PHP index.php
в корневой папке сервера, прочитав:
<?php
trigger_error(json_encode(getallheaders()));//a test to see that the proxy pass is actually
happening
header("HTTP/1.1 304 Not Modified");
//if successful should cause the incoming request to be published without changes
//header("HTTP/1.1 204 No Content");
//when enable will suppress the incoming request (optionally after doing some server-side
//stuff with the incoming request data
//header("HTTP/1.1 200 OK");
//echo 'New Content!';
//when enabled should replace the incoming requestt content with the new one
?>
Насколько я могу судить, я следовал инструкциям NChan довольно старательно. Однако, когда я пытаюсь использовать эту конфигурацию для публикации на определенном pubsub
канале, клиентское соединение неожиданно закрывается. Для выполнения этого теста я использую простой JavaScript
var socket;
function connectPubSub()
{
socket = new WebSocket("wss://app.server.url/pubsub/personal/cause/beacon/grid/chat");
socket.onopen = whenOpen;
socket.onmessage = whenMessage;
socket.onclose = whenClose;
socket.onerror = whenError;
}
function whenOpen(e){console.log("[open] Connection established");}
function whenMessage(event){console.log(`[message]:${event.data}`);}
function whenClose(event)
{
if (event.wasClean)
alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
else console.log('[close] Connection died');
}
function whenError(error){console.log(`[error] ${error.message}`);}
function sendMessage(msg){socket.send(msg);}
В этот момент, когда я пытаюсь выдать следующее
connectPubSub();
sendMessage('Hello world!');
Я получаю вывод, показанный ниже
[open] Connection Established
Websocket connection to app.server.url failed:Close received after close
Событие ошибки показано ниже.
bubbles: false
cancelBubble: false
cancelable: false
composed: false
currentTarget: WebSocket {url: "wss://app.server.url/pubsub/personal/cause/beacon/grid/chat",
readyState: 3, bufferedAmount: 0, onopen: ƒ, onerror: ƒ, …}
defaultPrevented: false
eventPhase: 0
isTrusted: true
path: []
returnValue: true
srcElement: WebSocket {url: "wss://app.server.url/pubsub/personal/cause/beacon/grid/chat",
readyState: 3, bufferedAmount: 0, onopen: ƒ, onerror: ƒ, …}
target: WebSocket {url: "wss://app.server.url/pubsub/personal/cause/beacon/grid/chat",
readyState: 3, bufferedAmount: 0, onopen: ƒ, onerror: ƒ, …}
timeStamp: 83208.4250000189
Это меня озадачило. Я думал, что смогу просто следовать документации NChan и публиковать сообщения на мультиплексном pubsub
канале, а затем проверять содержимое сообщения (JSON) на моем прокси-сервере, прежде чем решить, что делать.
- Пропустить сообщение (выдать HTTP 304)
- Выполнить действие на стороне сервера, а затем подавить сообщение (выдать HTTP 204)
- Измените сообщение и отправьте его (выдайте HTTP 200)
Мои довольно скудные знания директив конфигурации Nginx, вероятно, подводят меня здесь. Что я делаю не так?
Теперь я смог установить, что здесь происходит. Корень проблемы здесь, кажется, в
proxy_pass https://example.com;
директива. Если я изменю ее на
proxy_pass https://example.com/index.php;
все работает как и ожидалось. Однако мне все еще не ясно, почему это может происходить. В верхней части моего файла конфигурации Nginx по умолчанию есть строки
server
{
root /path/to/root;
index index.php;
server_name example.com;
Я думал, что третья строка выше index index.php
— это все, что мне нужно, чтобы сказать Nginx выполнить index.php в любой папке/подпапке. Понятно, не так ли? [1]:https://nchan.io
[2]:https://nchan.io/#начало работы