dart - Write body of HttpClientReques failing? - dart

I was trying to send data to a local server using HttpClient. However, the data is never added to the request, I'm using this code:
new HttpClient().put('127.0.0.1', 4040, '/employees/1').then((request) {
request.cookies.add(new Cookie('DARTSESSID',sessionId)..path = '/');
request.headers.add(HttpHeaders.ACCEPT_ENCODING, "");
request.headers.add(HttpHeaders.CONTENT_TYPE, "text/json");
request.write('{"id": 1, "name": "luis"}');
print(request.contentLength);
return request.close();
}).then(expectAsync((HttpClientResponse response) {
expect(response.statusCode, 200);
UTF8.decodeStream(response).then(expectAsync((body) {
expect(body, equals('"employee: 1"'));
}));
}));
but that always prints that the request.contentLenght is -1. I saw those links before without luck:
https://code.google.com/p/dart/issues/detail?id=13293
dart - HttpClientRequest failing on adding data
https://code.google.com/p/dart/issues/detail?id=10026

A ContentLength of -1 does not mean that there is no data, it means that the length of the content is unknown and that a streaming content mode is used - for HTTP 1.1, this will usually mean Chunked ContentEncoding.
I've tried to insert your code in a setup including a server, but without the unittest stuff:
import 'dart:convert';
import 'dart:io';
void main() {
HttpServer.bind('127.0.0.1', 4040).then((server) {
server.listen((request) {
UTF8.decodeStream(request).then((body) {
print(body);
request.response.close();
});
});
new HttpClient().put('127.0.0.1', 4040, '/employees/1').then((request) {
request.cookies.add(new Cookie('DARTSESSID', "1")..path = '/');
request.headers.add(HttpHeaders.ACCEPT_ENCODING, "");
request.headers.add(HttpHeaders.CONTENT_TYPE, "text/json");
request.write('{"id": 1, "name": "luis"}');
print(request.contentLength);
return request.close();
}).then((HttpClientResponse response) {
UTF8.decodeStream(response).then((body) {
print(body);
});
});
});
}
When I run the code, I get
-1
{"id": 1, "name": "luis"}
as expected. Perhaps the problem you are having are on the server?

Writing to the request is an asynchronous operation. Just because the contentLength says that it still is -1 doesn't mean that the data isn't added to the request before sending it to the server.
Also: the content-length is not supposed to update whenever you add new data. It is the value that is sent to the server. -1 means that you don't know the size yet.
I'm not sure, if the library automatically updates it, if it knows the size, but it doesn't need to.

Related

How to log the response for a dart shelf request

I'm using the Dart Shelf package and I need to log the response it sends.
I've managed to log the request but the response technique is less clear:
final handler = const shelf.Pipeline()
.addMiddleware(corsHeaders())
.addMiddleware(shelf.logRequests(
logger: (message, isError) =>
_logRequest(message, isError: isError)))
.addHandler((req) async {
final res = await Router().call(req);
return res;
});
There two parts to the question.
how do I log the headers.
is it possible to log the body.
I know there is an issue in that the response body can only be read once.
As some of the responses are likely to be large I need to filter the requests for which the body is logged.
The answer is a bit of Dart-fu. You have an anonymous function returning an anonymous function.
var handler = const Pipeline()
.addMiddleware(
(handler) => (request) async {
final response = await handler(request);
print(response.headers);
// you could read the body here, but you'd also need to
// save the content and pipe it into a new response instance
return response;
},
)
.addHandler(syncHandler);

Save in Cache api response WORKBOX

I would like to save some data that comes from an API in cache in case I lose the connection that data is shown
I have a list of Work Parts, so if I go offline I would like to continue seeing those parts, since when I enter the component again, it makes the call to the API and brings them again and since there is no connection, it leaves it in white here would be to bring them from cache
import {precacheAndRoute} from 'workbox-precaching';
import {clientsClaim, skipWaiting} from 'workbox-core';
import {registerRoute} from 'workbox-routing';
import {CacheFirst, NetworkFirst, NetworkOnly, StaleWhileRevalidate} from 'workbox-strategies';
import {CacheableResponsePlugin} from "workbox-cacheable-response";
import {BackgroundSyncPlugin} from 'workbox-background-sync';
import {Queue} from 'workbox-background-sync';
declare const self: ServiceWorkerGlobalScope;
skipWaiting();
clientsClaim();
const queue = new Queue('cola');
const bgSyncPlugin = new BackgroundSyncPlugin('api-cola', {
onSync: async ({queue}) => {
let entry;
while ((entry = await queue.shiftRequest())) {
try {
let clone = entry.request.clone();
await fetch(clone);
console.error("Replay successful for request", entry.request);
} catch (error) {
console.error("Replay failed for request", entry.request, error);
// Put the entry back in the queue and re-throw the error:
await queue.unshiftRequest(entry);
throw error;
}
}
console.log("Replay complete!");
}
});
registerRoute(
/\/api\/.*\/*.php/,
new NetworkOnly({
plugins: [bgSyncPlugin]
}),
'POST'
);
registerRoute(
({url}) => url.origin === 'https://xxx.xxx.com' &&
url.pathname.startsWith('/api/'),
new CacheFirst({
cacheName: 'api-cache',
plugins: [
new CacheableResponsePlugin({
statuses: [0, 200, 404],
})
]
})
);
registerRoute(
/assets\/images\/icons\/icon-.+\.png$/,
new CacheFirst({
cacheName: 'icons'
})
);
precacheAndRoute(self.__WB_MANIFEST);
When you go offline when you return the connection a sync is done and this works fine.
I noticed that you are using runtime caching, which means the app or the user would have to make the calls to the api at some point before they are offline so that the resources are available in the cache. Maybe warm strategy cache would work for you, if you know the urls before hand.
I also noticed that you are caching responses even if they return with a 404 code, so those would also not be displayed correctly.

Enabling Google Cloud Run chunked encoding

I have tried returning 'transfer-encoding:chunked' header to a large download but the response terminated at "31.9M" which is very close to the documented "32MB" limit.
The Unofficial FAQ states chunked encoding is now possible, but I can't seem to get it working.
Do I have to flip any flags (e.g. https/2) to enable streaming? Is it only possible in some regions? (I am using europe-west1)
The following minimal case does actually stream 45MB over Cloud Run, without any special configuration, confirmed in us-central1 and europe-west1
FROM node:14-slim
COPY index.js index.js
CMD [ "node", "index.js" ]
const http = require('http');
http.createServer((request, response) => {
response.setHeader('Transfer-Encoding', 'chunked');
// 45MB
var i = 1000000;
function nextChunk() {
return new Promise(resolve => {
if (i-- > 0) {
response.write(
'123456781234567812345678123456781234567812345678',
() => nextChunk()
);
} else {
response.end();
}
})
};
Promise.all(nextChunk())
}).listen(process.env.PORT || 8080);
Even after straming into chunks I'm always getting error after 32 MB reponse size, I can't get the response header 'Transfer-Encoding' = 'chunked' from Cloud Run service response :
I'm using NestJs, and I can get it in the response Headers when I execute the app locally.
I'm specifying it as :
#Get('streamable')
#Header('Transfer-Encoding', 'chunked')
async streamable(#Res() res) { etc.. res.write(chunk)}

In dart httpRequest getstring always error

I established a RESTful server, and I can get a simple string with my Chrome or IE using this URL: "http://localhost:8080/WebService/RESTful/getString"
But when using Dart, it always returns a error message:
"[object XMLHttpRequestProgressEvent]"
From the onError() callback method,
I'm sure that server returns a string with "text/plain" MIME type in Java.
Here is the code:
import 'dart:html';
void main() {
HtmlElement btn = document.body.querySelector("#btn");
btn.onClick.listen(onClick);
}
void onClick(Event v) {
var url = "http://localhost:8080/WebService/RESTful/getString";
HttpRequest.getString(url).then((str) {
window.alert(str.toString());
}, onError: (e) {
window.alert(e);
});
}
Who can help me ?
If you try to fetch resources from another server than the one your page was loaded from, this server needs to return CORS headers otherwise your browser refuses to fetch form this other server.
It depends on your server how this can be configured or added.
See for example
- How do you add CORS headers in Redstone interceptor?
- CORS with Dart, how do I get it to work?

Wait while request is running

Here is a problem. When I ran these code:
String responseText = null;
HttpRequest.getString(url).then((resp) {
responseText = resp;
print(responseText);
});
print(responseText);
In console:
{"meta":{"code":200},"data":{"username":"kevin","bio":"CEO \u0026 Co-founder of Instagram","website":"","profile_picture":"http:\/\/images.ak.instagram.com\/profiles\/profile_3_75sq_1325536697.jpg","full_name":"Kevin Systrom","counts":{"media":1349,"followed_by":1110365,"follows":555},"id":"3"}}
null
It running asynchronously. Is there JAVA way with synchronized method? That will be await while request is done?
I found only one tricky way to do it and its funny -- wait for three seconds:
handleTimeout() {
print(responseText);
}
const TIMEOUT = const Duration(seconds: 3);
new Timer(TIMEOUT, handleTimeout);
And of course it works with bugs. So any suggestions?
MattB way work well:
var req = new HttpRequest();
req.onLoad.listen((e) {
responseText = req.responseText;
print(responseText);
});
req.open('GET', url, async: false);
req.send();
First, I'm assuming you're using this as a client-side script and not server-side. Using HttpRequest.getString will strictly return a Future (async method).
If you absolutely must have a synchronous request, you can construct a new HttpRequest object and call the open method passing the named parameter: async: false
var req = new HttpRequest();
req.onLoad.listen((e) => print(req.responseText));
req.open('GET', url, async: false);
req.send();
However it is highly recommended that you use async methods for accessing network resources as a synchronous call like above will cause the script to block and can potentially make it appear as though your page/script has stopped responding on poor network connections.

Resources