Inspect WebSocket packages with Charles proxy in Flutter app - flutter

I'm developing mobile application using Flutter/Dart. What I need is to debug/test my application's network traffic with Charles proxy/Fiddler. It's easy to inspect http requests/responses in dart/flutter using Charles proxy. We only need to tell HttpClient an address of proxy server (IP address of machine where Charles is installed) like this:
final client = HttpClient();
client.findProxy = (uri) {
String proxy = '1.2.3.4:8888';
return "PROXY $proxy;";
};
client.badCertificateCallback =
((X509Certificate cert, String host, int port) => Platform.isAndroid);
But how can I debug WebSocket traffic created via
WebSocket.connect('wss://server_address');? WebSocket doesn't have any api for setting proxy settings and I couldn't find anything on forums.
That being said, I already did such things in the past in another mobile app written on C# and it was pretty easy.

Figured it out. There are actually multiple ways to do that:
Global Proxy settings:
class ProxiedHttpOverrides extends HttpOverrides {
String _proxy;
ProxiedHttpOverrides(this. _proxy);
#override
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)
..findProxy = (uri) {
return _proxy.isNotEmpty ? "PROXY $_proxy;" : 'DIRECT';
}
..badCertificateCallback = (X509Certificate cert, String host, int port) => Platform.isAndroid;
}
}
void main() {
String proxy = '1.2.3.4:8888';
HttpOverrides.global = new ProxiedHttpOverrides(proxy);
runApp(MyApp());
}
Custom HttpClient passed into WebSocket.connect
HttpClient client = HttpClient();
client.findProxy = (uri) => "PROXY $proxy;";
client.badCertificateCallback = (X509Certificate cert, String host, int port) => Platform.isAndroid;
WebSocket.connect(url, customClient: client);
Manual way: We need to upgrade to WebSocket using simple HttpClient with specified proxy settings. In that case we will be able to inspect WebSocket traffic in Charles/Fiddler.
Future<WebSocket> createProxySocket() async {
String url = 'https://echo.websocket.org:443';//address must start from http(s)://, not from ws(s)://, as we are connecting using http
String proxy = '1.2.3.4:8888';//your machine address (or localhost if debugging on the same machine)
Random r = new Random();
String key = base64.encode(List<int>.generate(8, (_) => r.nextInt(255)));
HttpClient client = HttpClient();
client.findProxy = (uri) => "PROXY $proxy;";
client.badCertificateCallback = (X509Certificate cert, String host, int port) => Platform.isAndroid;
Uri uri = Uri.parse(url);
HttpClientRequest request = await client.getUrl(uri);
request.headers.add('Connection', 'upgrade');
request.headers.add('Upgrade', 'websocket');
request.headers.add('Sec-WebSocket-Version', '13');
request.headers.add('Sec-WebSocket-Key', key);
HttpClientResponse response = await request.close();
Socket socket = await response.detachSocket();
return WebSocket.fromUpgradedSocket(socket, serverSide: false);
}

Related

How to use proxies library in flutter?

I want to create an Android app that connects to a proxy by button clicked and I'd like to know what should do to make this event?
My proxy needs(password, username, host, port).
I would be appreciated if you help me with this problem .
Add DIO settings like this
String proxy = '<IP>:<PORT>'; //eg 192.168.1.1:8080
var credentials = HttpClientBasicCredentials(<USERNAME>, <PASSWORD>);
bool isProxyChecked = true;
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(client) {
client.badCertificateCallback = (X509Certificate cert, String host, int port) {
return isProxyChecked && Platform.isAndroid;
};
client.findProxy = (url) {
return isProxyChecked ? 'PROXY $proxy' : 'DIRECT';
};
client.addProxyCredentials(<PROXY_IP>, <PORT>, 'main', credentials);
};

flutter/dart connect to Azure via mqtts

I am trying to connect to our MQTT server on Azure.
I use MQTTBox as our testbed, and it is successfully connecting to
protocol: mqtts
host: .azure-devices.net/$iothub/websocket
user: .azure-devices.net/testdevice/?api-version=2018-06-30
password: 'SharedAccessSignature sr=.azure-devices.net%2Fdevices%2Ftestdevice&sig=xxxxxxx'
I tried the mqtt_client library, which was an issue with "$" sign in the server endpoint and throws ""SocketException: Failed host lookup: 'mqttQueue.azure-devices.net/$iothub/websocket' (OS Error: No address associated with hostname, errno = 7)"
final client = MqttServerClient('<hubname>.azure-devices.net/\$iothub/websocket', '');
client.port = 8883;
client.secure = true;
final connMess = MqttConnectMessage()
.authenticateAs('<hubname>.azure-devices.net/testdevice/?api-version=2018-06-30',
'SharedAccessSignature sr=<hubname>.azure-devices.net%2Fdevices%2Ftestdevice&sig=xxxxxxx')
.withClientIdentifier('testdevice')
.withWillTopic('devices/testdevice/messages/events/') // If you set this you must set a will message
.withWillMessage('My Will message')
.startClean() // Non persistent session for testing
.withWillQos(MqttQos.atLeastOnce);
client.connectionMessage = connMess;
try {
await client.connect();
}
I also tried unsuccessfully
final client = MqttServerClient('HostName=<hubname>.azure-devices.net;DeviceId=testDevice;SharedAccessKey=m....Y=','')
Any working example to Azure in dart/flutter is appreciated, as I fail to map the Azure given parameters to the parameters in the library.
final client = MqttServerClient('<hubname>.azure-devices.net', '');
client.useWebSocket = false;
client.port = 8883;
client.autoReconnect = true;
client.keepAlivePeriod = 3600;
final String user = '<hubname>.azure-devices.net/<your device id>';
late String password; // <== password string is obtained elsewhere
final connMess = MqttConnectMessage()
.withClientIdentifier(clientIdentifier)
.startClean();
client.connectionMessage = connMess;
client.connect(username, password);

Udp socket in Flutter does not receive anything

I'm trying to use a udp socket as server in Flutter. I'd like to bind this socket on my localhost at 6868 port always in listening. Unfortunately when i try to send something from a client,it never prints the string "RECEIVED".
Here's the code:
static Future openPortUdp(Share share) async {
await RawDatagramSocket.bind(InternetAddress.anyIPv4,6868)
.then(
(RawDatagramSocket udpSocket) {
udpSocket.listen((e) {
switch (e) {
case RawSocketEvent.read:
print("RECEIVED");
print(String.fromCharCodes(udpSocket.receive().data));
break;
case RawSocketEvent.readClosed:
print("READCLOSED");
break;
case RawSocketEvent.closed:
print("CLOSED");
break;
}
});
},
);
}
Am I doing something wrong?
Anyway this is the client side, it is written is Lua:
local udp1 = socket.udp()
while true do
udp1:setpeername("192.168.1.24", 6868)
udp1:send("HI")
local data1 = udp1:receive()
if (not data1 == nil) then print(data1) break end
udp1:close()
end
I tested it with another server and it works well, so i don't think the client is the problem.
Thanks!
If it can help you, here my code for my SocketUDP (as singleton) in my app.
I used it in localhost and it works very well :
class SocketUDP {
RawDatagramSocket _socket;
// the port used by this socket
int _port;
// emit event when receive a new request. Emit the request
StreamController<Request> _onRequestReceivedCtrl = StreamController<Request>.broadcast();
// to give access of the Stream to listen when new request is received
Stream<Request> get onRequestReceived => _onRequestReceivedCtrl.stream;
// as singleton to maintain the connexion during the app life and be accessible everywhere
static final SocketUDP _instance = SocketUDP._internal();
factory SocketUDP() {
return _instance;
}
SocketUDP._internal();
void startSocket(int port) {
_port = port;
RawDatagramSocket.bind(InternetAddress.anyIPv4, _port)
.then((RawDatagramSocket socket) {
_socket = socket;
// listen the request from server
_socket.listen((e) {
Datagram dg = _socket.receive();
if (dg != null) {
_onRequestReceivedCtrl.add(RequestConvert.decodeRequest(dg.data, dg.address));
}
});
});
}
void send(Request requestToSend, {bool isBroadCast:false}) {
_socket.broadcastEnabled = isBroadCast;
final String requestEncoded = RequestConvert.encodeRequest(requestToSend);
List<int> requestAsUTF8 = utf8.encode(requestEncoded);
_socket.send(requestAsUTF8, requestToSend.address, _port);
}
}

How to Access using Xamarin.Forms local Web API with Emulator for Visual Studio?

I've created .NET Framework API which contains authentication, I launch the Web API using Jetbrains Rider and I run my Xamarin.Forms application using Visual Studio and I can't access any data from my Web API nor post any.
The Webservice class:
private readonly HttpClient _client;
public AccountService()
{
_client = new HttpClient
{
MaxResponseContentBufferSize = 256000
};
}
public async Task RegisterAsync(string email, string password, string confirmPassword)
{
var url = "http://169.254.80.80:61348/api/Account/Register";
var model = new RegisterBindingModel()
{
Email = email,
Password = password,
ConfirmPassword = confirmPassword
};
var json = JsonConvert.SerializeObject(model);
HttpContent content = new StringContent(json);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await _client.PostAsync(url, content);
}
The initiation of the registration
private async void Register()
{
try
{
using (UserDialogs.Instance.Loading())
{
await _accountServices.RegisterAsync
(Email, Password, ConfirmPassword);
}
UserDialogs.Instance.Alert("Register Successful");
await _navigation.PushAsync(new LoginPage());
}
catch
{
UserDialogs.Instance.Alert("Something wrong happened, Try again");
}
}
I've tried to access the localhost through Emulator using these IPs:
10.0.3.2
10.0.2.2
169.254.80.80
And I've tried my default gateways and my local IP address with and without ports, in regardless using postman i can work with my api flawlessly.
I don't get errors but the connection status code is not successful and I don't get any data and the newly registered account won't be posted to the api.
EDIT:
As for the answers I've changed my method to this:
public async Task<string> RegisterAsync(string email, string password, string confirmPassword)
{
var client = new HttpClient()
{
BaseAddress = new Uri("http://169.254.80.80:61348/")
};
var postData = new List<KeyValuePair<string, string>>();
var nvc = new List<KeyValuePair<string, string>>();
nvc.Add(new KeyValuePair<string, string>("email", email));
nvc.Add(new KeyValuePair<string, string>("password", password));
nvc.Add(new KeyValuePair<string, string>("confirmPassword", confirmPassword));
var req = new HttpRequestMessage(HttpMethod.Post, "api/Account/Register") { Content = new FormUrlEncodedContent(nvc) };
var res = await client.SendAsync(req);
if (res.IsSuccessStatusCode)
{
string result = await res.Content.ReadAsStringAsync();
string test = JsonConvert.DeserializeObject<string>(result);
return test;
}
return null;
}
and i call the web api using postman like this:
http://localhost:61348/api/Account/Register
I always prefer Newtonsoft Json.NET to carry out web request here is the code I have implemented in my case and it works great for me.
public static async Task<string> ResgisterUser(string email, string password, string confirmPassword)
{
var client = new HttpClient(new NativeMessageHandler());
client.BaseAddress = new Uri("http://192.168.101.119:8475/");
var postData = new List<KeyValuePair<string, string>>();
var nvc = new List<KeyValuePair<string, string>>();
nvc.Add(new KeyValuePair<string, string>("email", email));
nvc.Add(new KeyValuePair<string, string>("password", password));
nvc.Add(new KeyValuePair<string, string>("confirmPassword",confirmPassword));
var req = new HttpRequestMessage(HttpMethod.Post, "api/Vendor/Register") { Content = new FormUrlEncodedContent(nvc) };
var res = await client.SendAsync(req);
if (res.IsSuccessStatusCode)
{
string result = await res.Content.ReadAsStringAsync();
string test= JsonConvert.DeserializeObject<string>(result);
return test;
}
}
Hope it works for you.

Invoking Camel Rest services gives me 401 using restlet

I am trying write a route to call a restful services. all of them donot have a body but query parameters. when i call(https) the rest service i get 401(unauthorised).
but if i use simple non-ssl (http) and invoke it works fine on other apis.
here is my Route and producer template.
Route
public static final String MONITOR_URI = "https://lsapi.thomson-pharma.com//ls-api-ws/ws/rs/opportunity-v1/match?drug=bevacizumab&company=Genentech Inc&fmt=json";
public static final String DIRECT_MONITOR = "direct:getDrugInfo";
from(DIRECT_MONITOR).to(MONITOR_URI).convertBodyTo(String.class);
=========================Main Class===============================
public static void main(String[] args) throws Exception {
CamelContext context = createCamelContext();
context.start();
final String text = "paracetamol";
final String fmt = "json";
final String authMethod = "Digest";
final String authUsername = "TR_Internal_024";
final String authPassword="ZTYA5S1KLF7WCDMN";
final String query = String.format("text=%s&fmt=%s&authMethod=%s&authUsername=%s&authPassword=%s",text,fmt,authMethod,authUsername,authPassword);
Map<String,Object> headers = new HashMap<String, Object>(){
{
put(Exchange.HTTP_METHOD,"POST");
put(Exchange.AUTHENTICATION,"Digest");
put("authUsername","TR_Internal_024");
put("authPassword","ZTYA5S1KLF7WCDMN");
put(Exchange.HTTP_QUERY,query);
}
};
ProducerTemplate template = context.createProducerTemplate();
String request = template.requestBodyAndHeaders(Constants.DIRECT_MONITOR,null,headers,String.class);
System.out.println("Body is : "+request);
}
Can someone help how to configure SSL using camel cxf or restlet ?
How do i add Credentials Provider to CamelContext or Spring Context ?
APologies for the delay. i got it worked by retriving the component from camelContext below is the code.
=========================================================================
HttpComponent http = (HttpComponent) camelContext.getComponent("https");
HttpClientConfigurer httpClientConfigurer = http.getHttpClientConfigurer();
if(httpClientConfigurer == null){
System.out.println("httpClientConfigurer is null");
if(http.getHttpClientConfigurer() == null ){
HttpConfiguration httpConfiguration = new HttpConfiguration();
httpConfiguration.setAuthMethod(AuthMethod.Digest);
httpConfiguration.setAuthUsername("xxxxx");
httpConfiguration.setAuthPassword("xxxxxx");
http.setHttpConfiguration(httpConfiguration);
}
}
Regards
Ram