Ok some digging, I found that ionic request is block on request method: OPTION. I add lot of debugging statement in my server.
If I make a request with the web page, it does the put request immediately. The Ionic app ask for method OPTION before, and I believe that I don't send back the good answer after because the put request is never send to the server after.
here the handler of request on the server:
EthernetClient uHTTP::available(){
EthernetClient client;
memset(__uri, 0, sizeof(__uri));
memset(__query, 0, sizeof(__query));
memset(__body, 0, sizeof(__body));
memset(&__head, 0, sizeof(__head));
if(client = EthernetServer::available()){
uint16_t cursor = 0, cr = 0;
char buffer[uHTTP_BUFFER_SIZE] = {0};
bool sub = false;
enum state_t {METHOD, URI, QUERY, PROTO, KEY, VALUE, BODY};
state_t state = METHOD;
enum header_t {START, AUTHORIZATION, CONTENT_TYPE, CONTENT_LENGTH, ORIGIN};
header_t header = START;
uHTTP_PRINTLN();
uHTTP_PRINTLN();
uHTTP_PRINTLN();
while(client.connected() && client.available()){
char c = client.read();
if(c == '/r') uHTTP_PRINTLN();
uHTTP_PRINT(c);
(c == '\r' || c == '\n') ? cr++ : cr = 0;
switch(state){
case METHOD:
if(c == ' '){
if(strncmp(buffer, PSTR("OP"), 2) == 0) __method = uHTTP_METHOD_OPTIONS;
else if(strncmp(buffer, PSTR("HE"), 2) == 0) __method = uHTTP_METHOD_HEAD;
else if(strncmp(buffer, PSTR("PO"), 2) == 0) __method = uHTTP_METHOD_POST;
else if(strncmp(buffer, PSTR("PU"), 2) == 0) __method = uHTTP_METHOD_PUT;
else if(strncmp(buffer, PSTR("PA"), 2) == 0) __method = uHTTP_METHOD_PATCH;
else if(strncmp(buffer, PSTR("DE"), 2) == 0) __method = uHTTP_METHOD_DELETE;
else if(strncmp(buffer, PSTR("TR"), 2) == 0) __method = uHTTP_METHOD_TRACE;
else if(strncmp(buffer, PSTR("CO"), 2) == 0) __method = uHTTP_METHOD_CONNECT;
else __method = uHTTP_METHOD_GET;
state = URI;
cursor = 0;
}else if(cursor < uHTTP_METHOD_SIZE - 1){
buffer[cursor++] = c;
buffer[cursor] = '\0';
}
break;
case URI:
if(c == ' '){
state = PROTO;
cursor = 0;
}else if(c == '?'){
state = QUERY;
cursor = 0;
}else if(cursor < uHTTP_URI_SIZE - 1){
__uri[cursor++] = c;
__uri[cursor] = '\0';
}
break;
case QUERY:
if(c == ' '){
state = PROTO;
cursor = 0;
}else if(cursor < uHTTP_QUERY_SIZE - 1){
__query[cursor++] = c;
__query[cursor] = '\0';
}
break;
case PROTO:
if(cr == 2){ state = KEY; cursor = 0; }
break;
case KEY:
if (cr == 4){ state = BODY; cursor = 0; }
else if(c == ' '){ state = VALUE; cursor = 0; }
else if(c != ':' && cursor < uHTTP_BUFFER_SIZE){ buffer[cursor++] = c; buffer[cursor] = '\0'; }
break;
case VALUE:
if(cr == 2){
switch(header){
case AUTHORIZATION:
strncpy(__head.auth, buffer, uHTTP_AUTH_SIZE);
break;
case CONTENT_TYPE:
strncpy(__head.type, buffer, uHTTP_TYPE_SIZE);
break;
case ORIGIN:
strncpy(__head.orig, buffer, uHTTP_ORIG_SIZE);
break;
case CONTENT_LENGTH:
__head.length = atoi(buffer);
break;
break;
}
state = KEY; header = START; cursor = 0; sub = false;
}else if(c != '\r' && c!= '\n'){
if(header == START){
if(strncmp(buffer, PSTR("Auth"), 4) == 0) header = AUTHORIZATION;
else if(strncmp(buffer, PSTR("Content-T"), 9) == 0) header = CONTENT_TYPE;
else if(strncmp(buffer, PSTR("Content-L"), 9) == 0) header = CONTENT_LENGTH;
}
// Fill buffer
if(cursor < uHTTP_BUFFER_SIZE - 1){
switch(header){
case AUTHORIZATION:
if(sub){ buffer[cursor++] = c; buffer[cursor] = '\0'; }
else if(c == ' ') sub = true;
break;
case CONTENT_TYPE:
case CONTENT_LENGTH:
buffer[cursor++] = c; buffer[cursor] = '\0';
break;
}
}
}
break;
case BODY:
if(cr == 2 || __head.length == 0) client.flush();
else if(cursor < uHTTP_BODY_SIZE - 1){ __body[cursor++] = c; __body[cursor] = '\0'; }
break;
}
}
}
return client;
void uHTTP::send_headers(uint16_t code){
Serial.println("send headers");
header_t head = __head;
uHTTP_PRINT("HTTP/1.1 ");
response->print("HTTP/1.1 ");
switch(code){
case 200:
uHTTP_PRINTLN("200 OK");
uHTTP_PRINTLN("Content-Type: application/json");
uHTTP_PRINTLN("Access-Control-Allow-Origin: *");
response->println("200 OK");
response->println("Content-Type: application/json");
response->println("Access-Control-Allow-Origin: *");
if(strlen(head.orig)){
//response->print("Access-Control-Allow-Origin: ");//to alllow CORS, allow origin *
//response->println(head.orig); //to alllow CORS, allow origin *
uHTTP_PRINTLN("Access-Control-Allow-Origin: *");
uHTTP_PRINTLN("Access-Control-Allow-Methods: GET,PUT,HEAD");
uHTTP_PRINTLN("Access-Control-Allow-Headers: Authorization, Content-Type");
uHTTP_PRINTLN("Access-Control-Allow-Credentials: true");
uHTTP_PRINTLN("Access-Control-Max-Age: 1000");
response->println("Access-Control-Allow-Origin: *");
response->println("Access-Control-Allow-Methods: GET,PUT,HEAD");
response->println("Access-Control-Allow-Headers: Authorization, Content-Type");
response->println("Access-Control-Allow-Credentials: true");
response->println("Access-Control-Max-Age: 1000");
}
break;
case 204:
uHTTP_PRINTLN("204 OK");
uHTTP_PRINTLN("100 continue");
response->println("204 OK");
response->println("100 continue");
break;
}
}
}
Then here the request send by the Ionic app:
OPTIONS /switch HTTP/1.1
Host: 107.171.185.29:8081
Connection: keep-alive
Access-Control-Request-Method: PUT
Origin: http://192.168.0.101:8100
User-Agent: Mozilla/5.0 (Linux; Android 7.0; Moto G (5) Build/NPPS25.137-93-4; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/65.0.3325.109 Mobile Safari/537.36
Access-Control-Request-Headers: content-type
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: fr-CA,en-US;q=0.9
X-Requested-With: io.ionic.starter
receive request
URI: switch
OPTION request= switch
send headers
HTTP/1.1 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: *
I think that my void uHTTP::send_headers(uint16_t code) function do not return all line needed... maybe related to CORS also...