Arduino EtherCard Post StaticJsonDocument to a web server - arduino

I am working on a project which I need to post some sensor data to my web service over tcp. I used StaticJsonDocument to hold these sensor data. The problem is that with the code below I can not be able to post any data. Currently I am using ENC28J60 for the ethernet connection.
void sendToApi (StaticJsonDocument<600> root) {
byte sd = stash.create();
String json_string;
serializeJson(root, json_string);
stash.print(json_string);
stash.save();
int stash_size = stash.size();
Serial.println(stash_size);
Stash::prepare(PSTR("POST /api/module HTTP/1.1" "\r\n"
"Host: 192.168.1.5:8181" "\r\n"
"Content-Type: application/json" "\r\n"
"Content-Length: $D" "\r\n"
"Authorization: Basic bWV0Ok1ldEF0czE4Kio=" "\r\n"
"\r\n"
"$H"),
stash_size, sd);
session = ether.tcpSend();
//Serial.println(session);
delay(2000);
}
There is no problem with the ethernet controller (I can ping Google). Also There is no problem with the server side. The postman HTTP request is below.
POST /api/module/ HTTP/1.1
Host: 192.168.1.5:8181
Content-Type: application/json
Authorization: Basic bWV0Ok1ldEF0czE4Kio=
Cache-Control: no-cache
Postman-Token: e160d927-7e05-414f-e0d8-102f3d039ce3
{"id":"0001","module_no":1,"m1":22.5625,"m2":22.5625,"m3":22.5625,"m4":22.5625,"m5":22.5625,"m6":22.5625,"m7":22.5625,"m8":22.5625,"m9":22.5625,"m10":22.5625,"m11":22.5625,"m12":22.5625,"m13":22.5625,"m14":22.5625,"m15":22.5625,"m16":22.5625,"t1":22.5625,"t2":22.5625,"t3":22.5625,"t4":22.5625,"t5":22.5625,"t6":22.5625,"t7":22.5625,"t8":22.5625,"t9":22.5625,"t10":22.5625,"t11":22.5625,"t12":22.5625,"t13":22.5625,"t14":22.5625,"t15":22.5625,"t16":22.5625,"af":22.5625,"uf":22.5625,"sg":22.5625,"sc":22.5625,"a1":946,"a2":946,"a3":946,"a4":946,"a5":946,"a6":32,"a7":32,"a8":32,"a9":946,"a10":946,"a11":946,"a12":946,"a13":32,"a14":32,"a15":32,"a16":32}
So what am I missing here?

Related

content type of http response changes when using external clients but is correct in unit test

I have a strange situation. I want to return the content type application/json; charset=utf-8 from an http handler.
func handleTest() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Accept") != "application/json" {
w.WriteHeader(http.StatusNotAcceptable)
return
}
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(map[string]string{"foo": "bar"})
}
}
When I check for this in my unit tests it is correct. This test does not fail.
func TestTestHandler(t *testing.T) {
request, _ := http.NewRequest(http.MethodGet, "/test", nil)
request.Header.Set("Accept", "application/json")
response := httptest.NewRecorder()
handleTest().ServeHTTP(response, request)
contentType := response.Header().Get("Content-Type")
if contentType != "application/json; charset=utf-8" {
t.Errorf("Expected Content-Type to be application/json; charset=utf-8, got %s", contentType)
return
}
}
But when I try with curl (and other clients) it comes out as text/plain; charset=utf-8.
$ curl -H 'Accept: application/json' localhost:8080/test -v
* Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /test HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.68.0
> Accept: application/json
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Tue, 28 Dec 2021 13:02:27 GMT
< Content-Length: 14
< Content-Type: text/plain; charset=utf-8
<
{"foo":"bar"}
* Connection #0 to host localhost left intact
I have tried this with curl, insomnia and python. In all 3 cases the content type came out as text/plain; charset=utf-8.
What is causing this problem and how can I fix it?
From the http package docs:
WriteHeader sends an HTTP response header with the provided status code.
and
Changing the header map after a call to WriteHeader (or Write) has no effect unless the modified headers are trailers.
So you are setting the "Content-Type" header after the header has already been sent out to the client. While mocking this likely works because the buffer where the headers are stored can be modified after the WriteHeader call. But when actually using a TCP connection you can't do this.
So simply move your w.WriteHeader(http.StatusOK) so it happens after the w.Header().Set(...)

Why is my request returning incomplete multipart?

I am using my Arduino to do a multipart/form-data request. I am generating the request completely by myself as follows:
client.println(HTTP_METHOD + " " + PATH_NAME + " HTTP/1.1");
client.println("Host: " + String(HOST_NAME));
client.print(F("Content-Type: multipart/form-data; "));
client.print(F("boundary=\"AaB03x\"\r\n"));
client.print(F("Content-Length: "));
client.print(strlen("{json data here}")
+ strlen("{json data here}"));
client.print("\r\nConnection: close\r\n");
// First part
// Boundary
client.print(F("\r\n--AaB03x\r\n"));
// Headers
client.print(F("Content-Disposition: form-data; name=\"header\"\r\n"));
client.print(F("Content-Type: application/ld+json\r\n"));
client.print(F("Content-Length: "));
client.print(strlen("{json data here}"));
client.print(F("\r\n\r\n"));
// Content
client.print(F("{json data here}"));
// Second part
// Boundary
client.print(F("\r\n--AaB03x\r\n"));
// Headers
client.print(F("Content-Disposition: form-data; name=\"payload\"\r\n"));
client.print(F("Content-Type: application/ld+json\r\n"));//
client.print(F("Content-Length: "));
client.print(strlen("{json data here}"));
client.print(F("\r\n\r\n"));
// Content
client.print(F("{json data here}"));
// End of boundary
client.print(F("\r\n--AaB03x--\r\n\r\n"));
However the server returns "Incomplete multipart" which I don't understand since the multipart seems completely fine. I thought it was maybe due to incorrect newlines but I haven't been able to figure out a solution.
The output can be seen below and includes the request sent as well as the response from the server.
GET /router HTTP/1.1
Host: 192.168.178.147
Content-Type: multipart/form-data; boundary="AaB03x"
Content-Length: 4363
Connection: close
--AaB03x
Content-Disposition: form-data; name="header"
Content-Type: application/ld+json
Content-Length: 4143
{
some data here
}
--AaB03x
Content-Disposition: form-data; name="payload"
Content-Type: application/ld+json
Content-Length: 220
{
some data here
}
--AaB03x--
connected to 192.168.178.147
HTTP/1.1 500 Server Error
Connection: close
Cache-Control: must-revalidate,no-cache,no-store
Content-Type: text/html;charset=iso-8859-1
Content-Length: 597
Server: Jetty(9.4.41.v20210516)
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Error 500 Server Error</title>
</head>
<body><h2>HTTP ERROR 500 Server Error</h2>
<table>
<tr><th>URI:</th><td>/router</td></tr>
<tr><th>STATUS:</th><td>500</td></tr>
<tr><th>MESSAGE:</th><td>Server Error</td></tr>
<tr><th>SERVLET:</th><td>org.apache.camel.component.jetty.CamelContinuationServlet-38cb1606</td></tr>
<tr><th>CAUSED BY:</th><td>java.io.IOException: Incomplete Multipart</td></tr>
</table>
<hr>Powered by Jetty:// 9.4.41.v20210516<hr/>
</body>
</html>
Your Content-Length calculations are suspect.
Drop the Content-Length headers for each sub-section in the multipart and try again, those are not needed for multipart, and your calculations are just wrong anyway.
You can see captures of various multipart requests from various libraries and browsers in the Jetty tests.
https://github.com/eclipse/jetty.project/tree/jetty-9.4.41.v20210516/jetty-http/src/test/resources/multipart
(Look at the ones ending in *.raw, which you can generally open in a text editor)
Tip: don't do this yourself, multipart mime is full of edge cases, traps, and ancient tricks. Go grab apache httpcomponents httpmime jar and just use it to generate your raw mime multipart section properly.
Artifacts - https://search.maven.org/artifact/org.apache.httpcomponents/httpmime
Javadoc - https://javadoc.io/doc/org.apache.httpcomponents/httpmime/latest/index.html

Sending simple HTTP Post Request on ESP8266

I need to send simple HTTP POST Request using ESP8266. It's containing data in "form-data".
It should look like this:
POST http://testserver.com
{
"auth_key":"key",
"data":[
{
"key":"temperature",
"value":31.2
},
{
"key":"humidity",
"value":50
}
]
}
For the testing I was using Chrome application - Postman. And the HTTP Request code which I send was looking like this (it was automatically generated):
POST /api/mes HTTP/1.1
Host: testserver.com
Cache-Control: no-cache
Postman-Token: 9b910ed2-afdc-2a11-4963-2f85626cfa4e
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="auth_key"
79bde0ff1efeaee90b1e432c08d324ecfdb532ac42406d7a9a87dd911e95f87e
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="data"
[{"key":"humidity", "value":55}]
------WebKitFormBoundary7MA4YWxkTrZu0gW--
And through Postman everything was okay. So then I sent it through ESP8266:
client.setNoDelay(true);
client.println("POST /api/mes HTTP/1.1");
client.println("Host: testserver.com");
client.println("Cache-Control: no-cache");
client.println("Postman-Token: 9b910ed2-afdc-2a11-4963-2f85626cfa4e");
client.println("Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW");
client.println("");
client.println("------WebKitFormBoundary7MA4YWxkTrZu0gW");
client.println("Content-Disposition: form-data; name=\"auth_key\"");
client.println("");
client.println("79bde0ff1efeaee90b1e432c08d324ecfdb532ac42406d7a9a87dd911e95f87e");
client.println("------WebKitFormBoundary7MA4YWxkTrZu0gW");
client.println("Content-Disposition: form-data; name=\"data\"");
client.println("");
client.println("[{\"key\":\"humidity\", \"value\":55}]");
client.println("------WebKitFormBoundary7MA4YWxkTrZu0gW--");
But unfortunately server is returning that the request is not correct. What can cause the problem? I'm struggling with it but I don't have any more ideas.

IP Camera viewer in android

I am doing a project in which ,I have to stream the ip camera's live in my app.
For demo I am using DLink camera(DCS 942L) .Please help me how to stream this ip camera's live in my app.
i succeed access to your camera, i make a http query using dlink:dlink as user:password :
1) before all, i encode dlink:dlink using Base64 encoder it give me ZGxpbms6ZGxpbms=
2) i send the query in c/c++ as follow:
sockaddr_in sin;
char *ip="203.125.227.73";
int port=80;
sin.sin_addr.s_addr=inet_addr(ip); // IP of server
sin.sin_family= AF_INET;
sin.sin_port=htons(port);
int sock=socket(AF_INET,SOCK_STREAM,0);
connect(sock, (sockaddr *)&sin, sizeof(sin));
char s[1024];
strcpy(s,"GET /video/mjpg.cgi HTTP/1.1\r\n"
"Authorization: Basic ZGxpbms6ZGxpbms=\r\n\r\n");
send(sock,s,strlen(s),0);
the server send me back this:
HTTP/1.0 200 OK
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Content-Type: multipart/x-mixed-replace;boundary=myboundary
--myboundary
Content-Type: image/jpeg
Content-Length: 32616
X-Status: 00000000
X-Tag: 830892
X-Flags: 0
X-Framerate: 20.92
X-Resolution: 640*360
X-Audio: 0
X-Timestamp: 1386117639854
\r\n\r\n followed_by_jpeg_data_stream_that_you_have_to_decode_in_bmp_and_display_it
in my case i use libjpeg in android and turbo-jpeg in windows to decode the stream.
dont forget to change the username and password, you can encode quickly the new user:pass in this web website:
http://www.motobit.com/util/base64-decoder-encoder.asp

PUT request using `UrlFetchApp` returns 'Bad Request' but the same request outside Google Apps Script works

This is the Google Apps Script that makes the request:
UrlFetchApp.fetch('https://user:password#sitename.com/api', {
method: 'put',
headers: { 'Accept': '*/*' },
payload: 'foo=bar&baz=qux'
});
Which can be successfully posted to http://requestb.in/, for examination:
PUT /1bfk94g1 HTTP/1.1
User-Agent: Mozilla/5.0 (compatible; GoogleDocs; script; +http://docs.google.com)
Host: requestb.in
Content-Type: application/x-www-form-urlencoded
Content-Length: 43
Connection: keep-alive
Accept-Encoding: gzip
Accept: */*
foo=bar&baz=qux
But fails when the request URL is https://user:password#sitename.com/api. The only error information shown is Bad request: https://user:password#sitename.com/api.
I've constructed a curl command which yields the exact same HTTP request:
curl -XPUT \
-H 'Accept-Encoding: gzip' \
--user-agent 'Mozilla/5.0 (compatible; GoogleDocs; script; +http://docs.google.com)' \
-d foo='bar' \
-d baz='qux'\
https://user:password#sitename.com/api
Which successfully posts to https://user:password#sitename.com/api. How can two identical requests have different outcomes? Am I missing something with regards to my Google Apps Script? I've tried using the debugger, but it didn't help.
It appears that UrlFetchApp doesn't yet support requests with credentials in the URL. The Authorization HTTP header needs to be built manually. The following works:
var response = UrlFetchApp.fetch('https://sitename.com/api', {
headers: {
'Authorization': 'Basic ' + Utilities.base64Encode(user + ':' + password)
}
});

Resources