I have the following client-side code:
import 'dart:html';
import 'dart:async';
void main() {
WebSocket ws = new WebSocket('ws://127.0.0.1:4949');
ws.onOpen.listen((_) => print('open'));
ws.onMessage.listen((MessageEvent e) => print(e.data));
ws.onClose.listen((_) => print('closed'));
ws.onError.listen((_) => print('error'));
}
And this server-side code:
import 'dart:io';
import 'dart:async';
main() {
ServerSocket.bind('127.0.0.1', 4949).then((ServerSocket server) {
server.listen((Socket client){
print('Connection from '
'${client.remoteAddress.address}:${client.remotePort}');
client.write('hello from server');
});
});
}
The WebSocket connection connects successfully to the ServerSocket. The server code prints:
Connection from 127.0.0.1:55516
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 127.0.0.1:4949
Origin: http://127.0.0.1:3030
Pragma: no-cache
Cache-Control: no-cache
Sec-WebSocket-Key: PrJr2iVElmEsX7ZItHnWHA==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.39 (Dart) Safari/537.36
The problem is that the onOpen and onMessage do not get triggered. I suspect I'm missing something but not sure what.
Yes you are missing something, the complete server side implementation. ServerSocket is just a plain socket used for TCP (or UDP).
But a websocket requires a HTTP server, that does the handling of the HTTP request and that upgrades the connection to a Websocket connection:
import 'dart:io';
void main() {
HttpServer.bind('127.0.0.1', port)
.then((HttpServer server) {
print('listening for connections on $port');
server.listen((HttpRequest request) {
if (request.uri.path == '/ws') {
WebSocketTransformer.upgrade(request).then((WebSocket websocket) {
websocket.listen((message) => print('Message from client'));
print('Client connected');
});
} else {
/* ... */
}
});
},
onError: (error) => print("Error starting HTTP server: $error"));
}
A simple example from SethLadd. But I would probably do also a check if the requests CONNECTION and UPGRADE headers are correct before upgrading to a websocket. After upgrading the connection you have a Websocket instance that is similar to the on one the client side.
Related
I have some ESP8266 and ESP32.
I use them with a MQTT (SSL) server and a web server.
I use two WiFiClientSecure to avoid to be disconnected to my MQTT server.
One WiFiClientSecure instance is dedicate to the MQTT server and the other one to HTTPS requests
The ESP32 works well, I can connect to my MQTT server and send HTTPS request without issue.
The ESP8266 (Wemos d1 mini) doesn't work as expected. The HTTPS request always fail.
It only works if I only use one instance of WiFiClientSecure. However during the HTTPS request I am disconnected of the MQTT server.
Is this behavior is due to hardware limitation ?
Here is the revelant part of code
WiFiClientSecure espClient;
WiFiClientSecure espClient2;
PubSubClient mqttClient(espClient);
const char* rootCABuff = \
"-----BEGIN CERTIFICATE-----\n" \
...
"-----END CERTIFICATE-----\n";
void setup() {
/* Connect to wifi then set cert*/
espClient.setCACert(rootCABuff);
espClient2.setCACert(rootCABuff);
/* Define clock or will fail*/
setClock();
...
client.setServer("myserver", myport); // MQTT server
...
}
void loop() {
...
if (!mqttclient.connected()) {
reconnect();
}
else
{
mqttclient.loop();
}
...
/*Sometimes a https request is needed*/
HTTPClient http;
http.setTimeout(10000);
char* baserequest = "https://myserver.com";
httpRequestData="somedata";
Serial.print("[HTTPS] begin...\n");
if (http.begin(espClient2, baserequest)) { // HTTPS
http.addHeader("Accept", "application/json");
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
Serial.print("[HTTPS] POST...\n");
int httpCode = http.POST(httpRequestData);
http.end();
} else {
Serial.printf("[HTTPS] Unable to connect\n");
}
...
}
I try to search items from eBay API. Within the server.js file at my Apollo Server 2, I pass the token string by context property while instantiation (s. Doku: Apollo context argument). So every request contains the authentication HTTP header property. As a tryout, for now, I just use the fixed token string. This will be changed later if I work for the client.
server.js
import { ApolloServer } from 'apollo-server'
import schema from './schema'
const server = new ApolloServer({
schema,
context: ({ req }) => {
const token = 'Bearer v^1.1#i^1#I^3#f^0#p^1#r^0#t^H4sIAAA...' // my token
return {
...req,
headers: {
...req.headers,
// enrich the header with oauth token
authorization: token,
},
}
},
})
server.listen().then(({ url }) => console.log(`🚀 Server ready at ${url}`))
resolver method
// A map of functions which return data for the schema.
const resolvers = {
Query: {
books(root, { keyword = '' }, context) {
console.log(context.headers)
fetch(`https://api.ebay.com/buy/browse/v1/item_summary/?q=${keyword}`)
.then(response => response.json())
.then(json => console.log(json))
return []
}
}
}
The context.header contains the authorization property:
{ host: 'localhost:4000',
connection: 'keep-alive',
'content-length': '108',
accept: '*/*',
origin: 'http://localhost:4000',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
dnt: '1',
'content-type': 'application/json',
referer: 'http://localhost:4000/',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'de,en;q=0.9',
authorization: 'Bearer v^1.1#i^1#f^0#p^1#r^0#I^3#t^H4sIAAAAAAAAAOV...'
}
The JSON response contains the error with errorId 1002. It says Access token is missing in the Authorization HTTP request header.:
{ errors:
[ { errorId: 1002,
domain: 'OAuth',
category: 'REQUEST',
message: 'Missing access token',
longMessage: 'Access token is missing in the Authorization HTTP request header.' } ] }
Additionally, I use a new browser tab, enter the URL https://api.ebay.com/buy/browse/v1/item_summary/search?q=test and add the same authorization header property (I use the ModHeader chrome extension). I hit enter, the request works and I get the expected JSON.
It is confusing and I don't know what I'm doing wrong while passing the token. Does somebody have an idea?
The headers you see are the ones being sent in the request to your GraphQL server. All you've done is modified them to include the Authorization header and then included your entire request object as your context -- you're not passing any header information to the fetch call actually getting the data from eBay. Minimally, you want to do something like this:
fetch(`https://api.ebay.com/buy/browse/v1/item_summary/?q=${keyword}`, {
headers: {
Authorization: context.headers.authorization,
},
})
Also bear in mind that the fetch call should be returned inside your resolver, otherwise it won't be awaited.
I have a problem when use HTTP post method in the following code:
let body = JSON.stringify(applicationLink);
let requestHeaders = new Headers();
var headers = new Headers();
headers.set('Content-Type', ['application/json']);
headers.set('Access-Control-Allow-Origin', ['*']);
let reqoptions = new RequestOptions({
headers: headers
});
return this._http.post(this._applicationLinksUrl + this._linkServicePath,body,reqoptions).map(res => res.json())
When I execute the code I see an error:
XMLHttpRequest cannot load http..... Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http....' is therefore not allowed access.
Request Method:OPTIONS is mde instead of post
Request URL:http://localhost:7001/workprocess-service/resources/links
Request Method:OPTIONS
Status Code:200 OK
Remote Address:[::1]:7001
Response Headers
view source
Allow:OPTIONS,POST,GET,HEAD
Content-Type:application/vnd.sun.wadl+xml
Date:Thu, 26 May 2016 11:10:25 GMT
Last-Modified:Thu, 26 May 2016 09:02:27 CEST
Transfer-Encoding:chunked
Vary:Accept
X-Powered-By:Servlet/3.0 JSP/2.2
Request Headers
view source
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:access-control-allow-origin, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:localhost:7001
Origin:http....
Referer:http....
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36
and my data is not linked to the request.
If I remove reqoption
return this._http.post(this._applicationLinksUrl + this._linkServicePath,body).map(res => res.json())
the request is a POST but i receive an error 415 ( Unsupported Media Type)
Request URL:http://localhost:7001/workprocess-service/resources/links
Request Method:POST
Status Code:415 Unsupported Media Type
Remote Address:[::1]:7001
Response Headers
view source
Connection:close
Content-Length:22
Content-Type:text/html; charset=UTF-8
Date:Thu, 26 May 2016 11:14:44 GMT
X-Powered-By:Servlet/3.0 JSP/2.2
Request Headers
view source
Accept:*/*
Accept-Encoding:gzip, deflate
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:48
Content-Type:text/plain;charset=UTF-8
Host:localhost:7001
Origin:http:....
Referer:http:.....
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36
Request Payload
view source
{labelFR: "dsds", labelNL: "dsds", url: "dsds"}
labelFR
:
"dsds"
labelNL
:
"dsds"
url
:
"dsds"
My data is linked to the request
You need to make your server set the header Access-Control-Allow-Origin because you are sending a CORS "Cross Origin Resource Sharing" request. Which simply means you are sending the request to a url that's not the same as the url you are sending from.
I see your backend is servlet/3.0, you can enable CORS "for all origins" by adding a web filter
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
#WebFilter(urlPatterns = {"*"})
public class CORSFilter implements Filter{
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader("Access-Control-Allow-Origin", "*");
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
}
I want to get json data from the server(tomcat server)
I was import "package:http/http.dart' as http".
But, Result is "The built-in library 'dart: io' is not available on Dartium" in Datium console.
So "dart build" and run the "Uncaught Unsupported operation: Platform._version" error comes in chrome console.
Also, dart: html and dart: io's "HttpRequest" was using the request fails.
How can I get response data from the server(tomcat or another was)?
Thanks your answer!!!
import 'dart:async';
import "dart:html";
import "dart:convert";
import 'package:http/http.dart' as http;
final ButtonElement loginButton = querySelector("#login");
void main() {
loginButton.onClick.listen((e) {
requestTest2IO();
});
}
void requestTest2IO(){
var url = 'server url';
http.get(url, headers : {'Cookie': 'JSESSIONID : xxxxxxxxxxxxxxxxxxxxxx',
'User-Agent': 'xxxxxxx',
'x-app-stat-json': '(Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36,appversion=8}'
})
.then((response) {
List<String> repos = JSON.decode(response.body);
print(repos);
});
}
import 'dart:io';
import 'dart:async';
void main() {
HttpClient client = new HttpClient();
client.getUrl(Uri.parse('http://api.dartlang.org/docs/releases/latest/dart_io/HttpClientResponse.html'))
.then((HttpClientRequest request) => request.close())
.then((HttpClientResponse response) {
response.listen(print, onError: (e) {
print('error: $e');
});
});
}
The code above doesn't work, using similar method to listen like pipe and fold also throws an exception => Breaking on exception: The null object does not have a method 'cancel'.
Update
Here's the code example for when connect to local machine.
import 'dart:io';
import 'dart:async';
void main() {
HttpServer.bind('127.0.0.1', 8080)
.then((HttpServer server) {
server.listen((HttpRequest request) {
File f = new File('upload.html');
f.openRead().pipe(request.response);
});
HttpClient client = new HttpClient();
client.getUrl(Uri.parse('http://127.0.0.1:8080'))
.then((HttpClientRequest request) => request.close())
.then((HttpClientResponse response) {
response.listen(print, onError: (e) {
print('error: $e');
});
});
});
}
It prints out the bytes first and then throw an exception Breaking on exception: The null object does not have a method 'cancel'.
Dart Editor version 0.7.2_r27268. Dart SDK version 0.7.2.1_r27268. On Windows 64bit machine.
Your example works on my machine.
Please specify your Dart version and other system properties that could help debug the problem.
The code presented looks fine, and I have not been able to reproduce the error on either 0.7.2.1 nor bleeding edge. Do you know whether you network has any kind of proxy setup which could cause a direct HTTP connection to fail? You could try connecting to a server on your local machine instead. If it still fails I suggest opening a bug on https://code.google.com/p/dart/issues/list with detailed information.