I have a voip application for iOS based on pjsip 2.3.0.
The device in question runs iOS 9.3.4 and the problem also seem to happen on iOS 10 beta. It is connected to IPV6 wifi network created from macbook's nat64/dns64.
The client is able to register properly with our server and also has no trouble making outbound call in UDP mode ( Everything works fine in TCP ).
The trouble is with inbound call where it fails intermittently due to the following reason when responding to INVITE with 200 OK message even though it is able to send 100 TRYING successfully just before it.
pjsua_core.c ....
Sent:
2016-07-31T16:49:32.443
<srcipv6:port> -> <dstipv6:port>
1005
SIP/2.0 200 OK Via: SIP/2.0/UDP
--end msg--
tsx0x1401d82a8 ....Error sending Response msg
200/INVITE/cseq=95989621(tdta0x140866000): No route to host
....Transport error, terminating transaction. Err=120065 (No route to host)
pjsua_call.c .Error sending response: No route to host [status=120065]
APIException: API call '::pjsua_call_answer( CALL_REFERENCE,
PJSUA_SIP_RESPONSE_STATUS_OKAY, NULL, NULL )'
failed with result code = 120065 ('No route to host')
Note that this happens only intermittently. Once an inbound call succeeds after sending 200 OK to INVITE then the subsequent calls do succeed. The problem does not exist in IPV4 networks.
I also did a packet capture and found that there is no trace of 200 OK message in it even though pjsua_core.c's trace tells otherwise as seen above.
Sincerely welcome any inputs on how to proceed further or if there are any fixes.
Edit 1 :
I have managed to debug all the way from ::pjsua_call_answer to the method “sendto” () calledin pjlib/src/pj/sock_bsd.c
/*
* Send data.
*/
PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock,
const void *buf,
pj_ssize_t *len,
unsigned flags,
const pj_sockaddr_t *to,
int tolen)
{
PJ_CHECK_STACK();
PJ_ASSERT_RETURN(len, PJ_EINVAL);
CHECK_ADDR_LEN(to, tolen);
*len = sendto(sock, (const char*)buf, (int)(*len), flags,
(const struct sockaddr*)to, tolen);
if (*len < 0)
return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
else
return PJ_SUCCESS;
}
sendto here returns -1 when trying to send 200 OK message alone in ipv6.
Related
I'm trying to do a POST request through proxy using https.
Code looks like:
FHttp := TIdHttp.Create(nil);
FHttp.ProxyParams.ProxyServer := Host;
FHttp.ProxyParams.ProxyPort := Port;
FHttp.ProxyParams.ProxyUsername := User;
FHttp.ProxyParams.ProxyPassword := Password;
FHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
FHandler.SSLOptions.Method := sslvTLSv1_2;
FHandler.PassThrough := true;
FHttp.IOHandler := FHandler;
FHttp.HandleRedirects := true;
FHttp.Request.ContentType := 'application/x-www-form-urlencoded';
FHttp.Request.Connection := 'keep-alive';
FHttp.Request.ProxyConnection := 'keep-alive';
...
FParams.Add('username=user');
FParams.Add('password=pwd');
FHttp.Post('https://my.service/login', FParams);
Proxy server is Squid.
Code generates error "Socket Error # 10054 Connection reset by peer."
Now, the interesting part comes:
If not using proxy at all (i.e. not setting FHttp.ProxyParams settings) - everything is OK.
If not setting any POST parameters (i.e. empty FParams), but still using proxy - everything is OK.
The most strange one: If I'm debugging the Indy code step by step (TIdCustomHTTP.DoRequest method) - everything is OK with above example (proxy settings + parameters).
POST parameters are not sent properly for some reason?
And why step 3 is happening?
Indy is up to date, just pulled from repository
UPDATE
After intercepting TIdHTTP calls (thanks Remy) there is a little bit more clarity. (failing log, working log).
Short version: when doing debug, Indy does 3 CONNECT + POST + DISCONNECT requests (because there are redirection on the service I believe) and it works.
When running test without debug - CONNECT + DISCONNECT + POST - and it fails obviously (i.e. POST is executed without CONNECT in front).
See attached log files for details.
You have found some logic bugs in TIdHTTP that need to be fixed. I have opened a new ticket for that:
#315: Bugs in TIdHTTP proxy handling
Here is what I see happening in your "failing" scenario:
TIdHTTP connects to the proxy, sends a CONNECT request that successfully connects to my.service.com:443, then sends a POST request (using HTTP 1.0 rather than HTTP 1.1 a).
a) to send a POST request with HTTP 1.1, you have to set the TIdHTTP.ProtocolVersion property to pv1_1, AND enable the hoKeepOrigProtocol flag in the TIdHTTP.HTTPOptions property. Otherwise, TIdHTTP.Post() forces the ProtocolVersion to pv1_0.
The HTTP server replies with a 302 Found response redirecting to a different URL, including a Keep-Alive header indicating the server will close the connection if a new request is not sent in the next 5 seconds.
When TIdHTTP is done processing the POST response, it knows it is going to re-send the same request to a new URL. On the next loop iteration, it sees that the target server is the same, and the proxy is still connected, and so the connection is not closed, and the code that would have sent a new CONNECT request is skipped.
Just before the POST request is sent, the Response.KeepAlive property is checked to know whether or not to close the socket connection anyway. The KeepAlive property getter sees the ProtocolVersion property is pv1_0 and that there is no Proxy-Connection: keep-alive header present in the response (even though there is a Connection: keep-alive header), so it returns False, and then the socket connection is closed.
TIdHTTP then re-connects to the proxy again, but does not send a new CONNECT request before sending the POST request. The proxy does not know what to do with the POST, so it fails the request with a 400 Bad Request response.
Here is what I see happening in your "working" scenario:
Everything is the same as above, up to the point where the 1st POST request is processed. Then there is a delay of roughly 16 seconds (likely since you are stepping through code) - more than the 5-second Keep-Alive delay allows - so the HTTP server closes its connection with the proxy, which then closes its connection to TIdHTTP.
By the time TIdHTTP is ready to send the 2nd POST request, it knows it has been disconnected from the proxy, so it re-connects to the proxy, sends a new CONNECT request, and then sends the POST request.
Until I can fix the bugs properly, try the following:
enable the hoKeepOrigProtocol flag in the TIdHTTP.HTTPOptions property to allow TIdHTTP.Post() to use HTTP 1.1. That in itself may fix the issue with the connection being closed unnecessarily before sending the 2nd POST request to the redirected URL.
if that doesn't solve the issue, try editing IdHTTP.pas yourself and recompile Indy, to update the TIdCustomHTTP.ConnectToHost() method to force a Disconnect() if the Response.KeepAlive property is False BEFORE the local LUseConnectVerb variable is set to not Connected in the case where ARequest.UseProxy is ctSSLProxy (and ctProxy, too). That way, the 2nd POST request will disconnect from the proxy and re-connect with a new CONNECT request.
I am using nodemcu with an esp-32 and recently came across an annoying problem. I refer to this sample from the NodeMCU Github page:
-- a simple HTTP server
srv = net.createServer(net.TCP)
srv:listen(80, function(conn)
conn:on("receive", function(sck, payload)
print(payload)
sck:send("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1> Hello, NodeMCU.</h1>")
end)
conn:on("sent", function(sck) sck:close() end)
end)
This doesn't seem to work in every case.
If I try it with telnet, there is no issue:
$ telnet 172.17.10.59 80
Trying 172.17.10.59...
Connected to 172.17.10.59.
Escape character is '^]'.
GET / HTTP/1.1
HTTP/1.0 200 OK
Content-Type: text/html
<h1> Hello, NodeMCU.</h1>
Connection closed by foreign host.
But when using wget, it hangs most of the time:
$ wget http://172.17.10.59/
--2017-05-12 15:00:09-- http://172.17.10.59/
Connecting to 172.17.10.59:80... connected.
HTTP request sent, awaiting response...
After some research, the root cause seems to be, that the receive callback is registered after the first data was received from the client. This doesn't happen when testing manually with telnet, but with a client like wget or a browser, the delay between connecting and receiving the first data seems to be too small to register the receive handler first.
I have looked into the nodemcu code and there doesn't seem to be an easy way to work around this problem. Or do I miss something here?
In HTTP/1.0, you need the "Content-Length" in the HTTP header when there is a message-body.
For example:
"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nContent-Length: 25 \r\n\r\n<h1> Hello, NodeMCU.</h1>"
ref: https://www.w3.org/Protocols/HTTP/1.0/spec.html#Content-Length
I was trying to explore Microsoft's Bing Speech Recognition API for iOS https://github.com/Microsoft/Cognitive-Speech-STT-iOS. I followed all the steps written in the read me. The app runs and it seems to be detecting the speech from microphone and sending it to the Microsoft server but it gives an error in the logs and the button disables itself without giving any text on the app.
Here are the logs that came in the console. Please help.
Application Name: com.Microsoft.SpeechRecognitionServerExample/1.0.1
Refreshing token /sts/v1.0/issueToken
Initializing Audio Services
Initializing Speech Services
No application id provided to controller
GetIdentityPropertyValue 3
Useragent Value iOS Assistant (iOS; 10.0.1;Mobile;ProcessName/AppName=com.Microsoft.SpeechRecognitionServerExample/1.0.1;DeviceType=Near;SpeechClient=1.0.160824)
Url: 'https://websockets.platform.bing.com/ws/speech/recognize'
Locale: 'en-us'
Application Id: ''
Version: 4.0.150429
UserAuthorizationToken:
ServerLoggingLevel: 1
Initiating websocket connection. m_connection=0x0 host=websockets.platform.bing.com port=443
Auth token status: 200
Authorization token hr 0 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzY29wZSI6Imh0dHBzOi8vc3BlZWNoLnBsYXRmb3JtLmJpbmcuY29tIiwic3Vic2NyaXB0aW9uLWlkIjoiZmNlYThiNTE3NGZmNDdkODk4ZWZiN2ZjYzNjZjM4MzMiLCJwcm9kdWN0LWlkIjoiQmluZy5TcGVlY2guUHJldmlldyIsImNvZ25pdGl2ZS1zZXJ2aWNlcy1lbmRwb2ludCI6Imh0dHBzOi8vYXBpLmNvZ25pdGl2ZS5taWNyb3NvZnQuY29tL2ludGVybmFsL3YxLjAvIiwiYXp1cmUtcmVzb3VyY2UtaWQiOiIiLCJpc3MiOiJ1cm46bXMuY29nbml0aXZlc2VydmljZXMiLCJhdWQiOiJ1cm46bXMuc3BlZWNoIiwiZXhwIjoxNDc1NDg3MDQ1fQ.4LF0gyhXU0T1iwDahlYlanKJ_wVjOOLhLyalFeDqIzA'
Successfully initialized client connection
Create ImpressionId: f43586374f8d8e455e48b090f2aaa5cd
Create ImpressionId: f627a603daa0cd4a16f59d9581118cdd
Reset
Create ImpressionId: dad2c09e82b8eaaacec3b20890de9bb8
ImpressionId: a9776c0ba8dc079c7d9658e6746a46ea
Adding requestId: 'bf17eb3310b2e39944d85dfe3d2868eb' for 'text/cu.client.context'
Subscribing request [bf17eb3310b2e39944d85dfe3d2868eb]
Waiting for connection/send completion.
Audio stream created
Adding requestId: 'dd3d699f39139326bedbb1dafd8816fb' for 'audio/x-wav'
Subscribing request [dd3d699f39139326bedbb1dafd8816fb]
Audio Stream Created
Creating transcoder 2
Microphone permissions: 0
Upgrade request returned with HTTP status code: 101.
Web socket handshake completed
CU Client connected
ConnectionStateChanged
Sent first chunk of audio stream, requestId='dd3d699f-3913-9326-bedb-b1dafd8816fb'
Received message: 'audio.stream.response'
Response request id: 'dd3d699f-3913-9326-bedb-b1dafd8816fb'
Response impression: 'a9776c0b-a8dc-079c-7d96-58e6746a46ea'
LanguageGeneration OK
Received message: 'audio.stream.response'
Response request id: 'dd3d699f-3913-9326-bedb-b1dafd8816fb'
Response impression: 'a9776c0b-a8dc-079c-7d96-58e6746a46ea'
LanguageGeneration OK
Received message: 'audio.stream.response'
Response request id: 'dd3d699f-3913-9326-bedb-b1dafd8816fb'
Response impression: 'a9776c0b-a8dc-079c-7d96-58e6746a46ea'
LanguageGeneration OK
Received message: 'audio.stream.response'
Response request id: 'dd3d699f-3913-9326-bedb-b1dafd8816fb'
Response impression: 'a9776c0ba8dc079c7d9658e6746a46ea'
Response Conversation: 'ab7082f3c4de8feb0d6210e8ec07dcb3'
LanguageGeneration OK
Sending audio stream endpoint, requestId='dd3d699f-3913-9326-bedb-b1dafd8816fb'
Sent audio stream endpoint, requestId='dd3d699f-3913-9326-bedb-b1dafd8816fb'
signaling OnAudioEvent(AUDIO_EVENT_RECORD_STOP)
originating error 0x80070057
Client UPL: 32880000 ticks
originating error 0x8000ffff
originating error 0x80070057
originating error 0x80004005
originating error 0x80004005
Failed to 'hresult', HR=80004005, WebSocket connection failed
No messages to retry, closing.
Closing web socket channel
CU Client connection dropped
ConnectionStateChanged
WebSocket closed unexpectedly, status: 0
Web socket channel already closed.
My iOS application uses SocketRocket to establish a connection with my websocket server. But after establishing a connection and sending the first message, the server (using gorilla/websocket) tries to parse the frame, but failed:
message_type, r, err := ws.NextReader()
if err != nil {
goto end
}
An error is reported when calling NextReader():
Websocket Read Failed: [websocket: control frame length > 125]
The strange thing is, I use SocketRocket's send() method to send data, so there should never be any control frames (ping/pong).
Has anyone seen this problem before? Help!
We are trying to merge two Mirth servers. One server (let's call it Server 1) is keeping all records and another server (Server 2) is getting HL7 message from the first one and writes messages to the database.
Everything was perfect so far. But Server 1, after sending each HL7 message, waits for ACK to consider this transaction as completed and to send another message from the list.
The success status coming from the Server 2 (which writes to the database) contains MySQL response such as "Success: Database write success. 1 rows updated.". This is not what Server 1 is expecting.
Therefore, the Server 1 considers this ACK as invalid, produces an error "Message Read Error - Will Retry" and keeps trying to send the same message again, causing Server 2 to duplicate messages in the database.
We are using Mirth Connect HTTP listener and we could not find any solution to send ACK msg to our first server the same screen HTTP listener.
Is there any way to do this? Any Suggestion?
Really need help.
The problem is you are not setting the response from server 2 correctly, so it just returns what the destination has. You can create an ACK by code on the destination transformer:
var ackMessage = ACKGenerator.generateAckResponse(connectorMessage.getRawData(), "AA", "Message Successfully Received");
responseMap.put("ackresp", ResponseFactory.getSentResponse(ackMessage));
And on your source connector select "ackresp" as response. Your server 1 will receive that ACK instead of the log of the database write.