Echo WebSocket in Dart - sockets

I'm trying to make an "echo" WebSocket in Dart, but online I could find only outdated examples.
This is my code at the moment:
server:
import 'dart:io';
import 'dart:convert';
void main() async {
final serverSocket = await ServerSocket.bind('127.0.0.1', 5600);
await for (Socket socket in serverSocket) {
socket.transform(utf8.decoder).listen((data) {
print(data);
});
}
}
client:
import 'dart:html';
WebSocket socket;
bool open = false;
void main() {
querySelector('#sendHello').onClick.listen(onClick);
querySelector('#output').text = 'Your Dart app is running.';
socket = WebSocket('ws://localhost:5600');
socket.onMessage.listen(socketMessage);
socket.onOpen.listen((_) => open = true);
}
void socketMessage(MessageEvent event){
print('I recived: ${event.data}');
}
void onClick(MouseEvent event) {
if (!open)
print('Connection is not open!');
else
socket.send('Hello');
}
The first text that is printed is:
GET / HTTP/1.1
Host: localhost:5600
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
Upgrade: websocket
Origin: http://localhost:63572
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,it;q=0.8,lb;q=0.7
Cookie: ....
Sec-WebSocket-Key: ...
Sec-WebSocket-Extensions: ...
then I'm unable to send/recive any more packets.
How can I make the ServerSocket working properly?

I managed to do that, actually I was totally on the wrong path, here is the actual way to do it:
import 'dart:io';
void main() async {
HttpServer server = await HttpServer.bind('localhost', 5600);
server.transform(WebSocketTransformer()).listen(onWebSocketData);
}
void onWebSocketData(WebSocket client){
client.listen((data) {
client.add('Echo: $data');
});
}

Related

flutter how to make http post using form data?

I'm trying to make http post and I tried many times to make data-form about under parameters.
But I got a error message -{"IsOK":"false","ResultMessage":"Unexpected character encountered while parsing value: G. Path 'MethodName', line 1, position 13.","ResultCode":null,"EventResultYn":null}-
is there any way to make data-form with parameters in pictures?
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:html/parser.dart';
void main() async {
var url = 'https://www.lottecinema.co.kr/LCWS/Ticketing/TicketingData.aspx';
// 1.
/* var dic = {
"MethodName": "GetPlaySequence",
"channelType": "HO",
"osType": "W",
"osVersion":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36",
"playDate": "2022-06-29",
"cinemaID": "1|0001|1013",
"representationMovieCode": ""
};
var parameters = {"paramList:" : dic};*/
//2.
var map = Map<String, String>();
map["MethodName"] = "GetPlaySequence";
map["channelType"] = "HO";
map["osType"] = "W";
map["osVersion"] =
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36";
map["playDate"] = "2022-06-29";
map["cinemaID"] = "1|0001|1013";
map["representationMovieCode"] = "";
var parameters = {"paramList": map};
final response = await http.post(Uri.parse(url), headers: {
// "Content-Type": "text/plain",
// "Content-Type": "Application/json"
}, body: parameters
);
runApp(const MyApp());
And This is cURL.
I expect to response has data like this.
{Items: [{CinemaNameKR: "가산디지털", CinemaNameUS: "Gasan", MovieNameKR: "탑건: 매버릭",…},…], ItemCount: 10}
you must use multipart request
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:html/parser.dart';
void main() async {
var url = 'https://www.lottecinema.co.kr/LCWS/Ticketing/TicketingData.aspx';
// 1.
/* var dic = {
"MethodName": "GetPlaySequence",
"channelType": "HO",
"osType": "W",
"osVersion":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36",
"playDate": "2022-06-29",
"cinemaID": "1|0001|1013",
"representationMovieCode": ""
};
var parameters = {"paramList:" : dic};*/
//2.
var map = Map<String, String>();
map["MethodName"] = "GetPlaySequence";
map["channelType"] = "HO";
map["osType"] = "W";
map["osVersion"] =
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36";
map["playDate"] = "2022-06-29";
map["cinemaID"] = "1|0001|1013";
map["representationMovieCode"] = "";
// var parameters = {"paramList": map};
var request = http.MultipartRequest("POST", Uri.parse("$url"));
request.fields.addAll(map);
http.StreamedResponse response = await request.send();
var responseString = await response.stream.bytesToString();
pring("STATUS CODE : ${response.statusCode}");
pring("RESPONSE BODY : ${responseString}");
runApp(const MyApp());

Getting Blank httpException message while trying to pass client certificate with dio

I am getting blank httpException message while trying to submit client certificate by using Dio package.
DioErrorType (DioErrorType.DEFAULT) : HttpException (HttpException: , uri = [API_PATH])
Same certificate is working when requesting with postman.
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
Date: Thu, 21 Jan 2021 05:56:55 GMT
Below is the code I am using
Future<List<int>> loadCertificate(String assetPath) async {
return (await rootBundle.load(assetPath)).buffer.asUint8List();
}
final List<int> certificateChainBytes =
await loadCertificate('assets/certs/client-cert1.pfx');
final List<int> keyBytes =
await loadCertificate('assets/certs/client-cert1.pfx');
final Dio dio = Dio();
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(client) {
final SecurityContext sc = SecurityContext()
..useCertificateChainBytes(certificateChainBytes, password: 'Secret')
..usePrivateKeyBytes(keyBytes, password: 'Secret');
final HttpClient httpClient = HttpClient(context: sc);
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) {
print('badcertificatecallback');
print(cert.issuer);
return true;
};
return httpClient;
};
try {
url = url.contains('http') ? url : '${PsConfig.ps_app_url}$url';
print('API Response from $url');
final Response response =
await dio.get(url, options: Options(headers: getHeaders()));
return processHttpResponse(obj, response);
} on diolib.DioError catch (e) {
inspect(e);
return processDioResponse(obj, e);
} finally {
dio.close();
}
I am new to this and got stuck. Thank you in advance.

Flutter Chopper does not handle error response

I am using chopper in my flutter project like this.
AuthRestService class:
import 'package:chopper/chopper.dart';
import 'package:flutter_meal_app/utils/constants.dart';
import 'package:injectable/injectable.dart';
part 'auth_rest_service.chopper.dart';
#prod
#singleton
#ChopperApi()
abstract class AuthRestService extends ChopperService {
#Post(path: '/accounts:signUp?key={authKey}')
Future<Response> signup(#Path('authKey') String authKey, #Body() Map<String, dynamic> body);
#Post(path: '/accounts:signInWithPassword?key={authKey}')
Future<Response> login(#Path('authKey') String authKey, #Body() Map<String, dynamic> body);
#factoryMethod
static AuthRestService create() {
final client = ChopperClient(
baseUrl: Constants.AUTH_BASE_URL,
converter: JsonConverter(),
services: [_$AuthRestService()],
interceptors: [HttpLoggingInterceptor()]);
return _$AuthRestService(client);
}
}
The way I use signup call..
final response = await _authRestService.signup(AUTH_KEY,
{'email': email, 'password': password, 'returnSecureToken': true});
Here is the log of webcall (both success and failure)..
--> POST https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=yourauthkey
content-type: application/json; charset=utf-8
{"email":"palaksdarji#gmail.com","password":"123456","returnSecureToken":true}
--> END POST (78-byte body)
Success:
{
"idToken":"....",
........
}
Failure:
<-- 400 https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=yourauthkey
cache-control: no-cache, no-store, max-age=0, must-revalidate
date: Sat, 17 Oct 2020 09:10:32 GMT
transfer-encoding: chunked
content-encoding: gzip
vary: Origin,X-Origin,Referer
content-type: application/json; charset=UTF-8
pragma: no-cache
x-xss-protection: 0
server: ESF
alt-svc: h3-Q050=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-27=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
expires: Mon, 01 Jan 1990 00:00:00 GMT
{
"error": {
"code": 400,
"message": "EMAIL_EXISTS",
"errors": [
{
"message": "EMAIL_EXISTS",
"domain": "global",
"reason": "invalid"
}
]
}
}
--> END POST
And The error I got in my logs is:
error is Instance of 'Response<dynamic>'
I used the same type of RestService in my project which is pointed to another server and It works, this AuthRestService is exact copy of restservice for firebase authentication. The error that I got in my logs is from the line where we call "signup" API, which makes me curious about my ChopperClient.
Do you guys know what is going wrong?? Please help. Thanks.
Maybe you can parse error response like this
Response response = await _authRestService.signup(AUTH_KEY,
{'email': email, 'password': password, 'returnSecureToken': true});
var res = SignUpResponse.fromJson(response.body);
SignUpResponse
part 'sign_up_response.g.dart';
#JsonSerializable()
class SignUpResponse extends BaseResponse {
var error = new Error();
SignUpResponse();
factory SignUpResponse.fromJson(Map<String, dynamic> json) =>
_$SignUpResponseFromJson(json);
Map<String, dynamic> toJason() => _$SignUpResponseToJson(this);
}

Flutter http streamedResponse.stream and streamedResponse.sink

I'm learning the third example of Flutter http package, this is the base of the code: https://pub.dev/packages/http
When the request is sent via BaseClient.send, only the headers and whatever data has already been written to StreamedRequest.stream will be sent immediately. More data will be sent as soon as it's written to StreamedRequest.sink, and when the sink is closed the request will end.
https://pub.dev/documentation/http/latest/http/StreamedRequest-class.html
From the docs, I don't understand how we should write to StreamedRequest.stream? (To send data immediately)
Isn't StreamedResponse.sink basically where we add our HTTP POST's Request Body: Why does it only accept a List<int>? Shouldn't it be a Map<String, String>? If it's not then where should we add the request body? NEW: Even when I encode it with ut8.encode, it still doesn't show up on Fiddler's WebForms when I'm debugging, how do I send a x-www-form-urlencoded properly?:
Code:
userAgentClient = UserAgentClient(userAgent, client);
streamedRequest = http.StreamedRequest('POST', Uri(scheme: 'http', path: '/posts/', host: 'jsonplaceholder.typicode.com'));
streamedRequest.sink.add([123, 456]); // It has to be a List<int>
//NEW:
streamedRequest.sink.add(utf8.encode('username=123&password=456'));
streamedRequest.sink.add(utf8.encode('{"username":"123","password":"456"}'));
Why do I have to close the sink to be able to access StreamedResponse's properties?
streamedRequest.sink.close();
Update:
class UserAgentClient extends http.BaseClient {
final String userAgent;
final http.Client client;
UserAgentClient(this.userAgent, this.client);
Future<http.StreamedResponse> send(http.BaseRequest request){
request.headers['user-agent'] = userAgent;
return client.send(request);
}
}
dynamic _status = '';
dynamic _body = '';
dynamic _headers = '';
String _reason = '';
http.Client client = http.Client();
String userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36';
UserAgentClient userAgentClient;
http.StreamedRequest streamedRequest;
void _httpStreamed(){
userAgentClient = UserAgentClient(userAgent, client);
streamedRequest = http.StreamedRequest('POST', Uri(scheme: 'http', path: '/posts/', host: 'jsonplaceholder.typicode.com'));
streamedRequest.sink.add(utf8.encode('{"username":"123","password":"456"}'));
setState(() {
_status = streamedRequest.url;
_body = '';
_headers = '';
_reason = '';
});
}
void _httpSend() async{
http.StreamedResponse streamedResponse;
streamedResponse = await userAgentClient.send(streamedRequest);
streamedResponse.stream.listen(
(value) async{
_body = http.ByteStream.fromBytes(value);
_body = await _body.bytesToString();
},
onError: (e, sT) {
SnackBar sBar = SnackBar(content: Text('$e\n$sT',));
Scaffold.of(context).showSnackBar(sBar);
},
onDone: () {
SnackBar sBar = SnackBar(content: Text('Done lol'),);
Scaffold.of(context).showSnackBar(sBar);
},
);
setState(() {
_body;
_status = streamedResponse.statusCode;
_headers = streamedResponse.headers;
_reason = streamedResponse.reasonPhrase;
});
}
}
void _httpClose(){
if (streamedRequest != null){
streamedRequest.sink.close();
}
}
So I run the first 2 functions but the _body variable doesn't show up on my screen until I run the _httpClose() function.
yes
Map<String,String> can not be streamed.
Streamed means the data is sent in chunks every time a chunk of data is emitted by the stream and the server (or the browsers send buffer) is ready to receive more data.
List<int> can be chunked because it doesn't matter how many bytes are sent at once.
If you have all data readily available, you probably do not want to use a StreamedRequest, expecially if it is not a blob of data. https://en.wikipedia.org/wiki/Binary_large_object
utf8.encode can be used to encode chunks emitted by a stream, but it doesn't provide a stream by itself, so you can't add the result of utf8.encode to a sink.
I do not understand that question. What properties doe you want to access and what problems do you run into when you try?
To me it doesn't look like you don't need to use StreamedRequest for your use case.

http Dart package examples are outdated, how to use client.get()

Here's the second example given by the http package: https://pub.dev/packages/http
var client = http.Client();
try {
var uriResponse = await client.post('https://example.com/whatsit/create',
body: {'name': 'doodle', 'color': 'blue'});
print(await client.get(uriResponse.bodyFields['uri']));
} finally {
client.close();
}
I get error: uriResponse.bodyFields['uri'] no such method.
I can see that there's no property named bodyFields in the class Response: https://pub.dev/documentation/http/latest/http/Response-class.html
So how should I use the client.get()?
I checked the docs for the function https://pub.dev/documentation/http/latest/http/get.html and I wrote this:
print(await client.get(uriResponse.request.url, {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36',
}));
But this also fails with error:
Class 'IOClient' has no instance method 'get' with matching arguments.
E/flutter (19613): Receiver: Instance of 'IOClient'
E/flutter (19613): Tried calling: get(Instance of '_SimpleUri', Instance of '_CompactLinkedHashSet<Map<String, String>>')
E/flutter (19613): Found: get(dynamic, {Map<String, String> headers}) => Future<Response>
write your post as below and print the response separate.
final response = await client.post(
'*endPoint url*',
headers: *headers if you have any*,
body: jsonEncode({'name': 'doodle', 'color': 'blue'}
));
print(json.decode((response.body)));
and follow the same structure for the get method as well.
String url = "url";
Response response = await client.get(url);
print('Response status: ${response.statusCode}');
print('Response body: ${json.decode((response.body))}');