I am working with a 3rd party device that hosts its own webserver. I am trying to hit this webserver with a get request. It succeeds with curl, or postman, but the get response is interpreted as erroneous from dart:io. This code receives an error: HttpException (HttpException: Failed to parse HTTP, 32 is expected to be a Hex digit, uri = http://10.22.2.214/settings/time)
import 'dart:convert';
import 'dart:io';
Future<void> main(List<String> arguments) async {
var request = await HttpClient().openUrl(
'get',
Uri.parse('http://10.22.2.214/settings/time'),
);
var response = await request.close();
var chunks = [];
var stringStream = response.transform(Utf8Decoder(allowMalformed: true));
// stringStream = stringStream.handleError((e) => null);
await for (var chunk in stringStream) {
chunks.add(chunk);
}
print(chunks);
}
The request succeeds and gives the full expected response in a number of other places. IE: Python 3+(requests), Postman, Curl, Google Chrome. So I know it is possible to parse the request successfully.
Anyone have any ideas on how I might circumvent the error? I would like to stay in dart/flutter if possible. I know I could run python code with a package like starflut, but its a lot of setup for a single request.
Additional Info:
Ignoring the error view the handleError function does not seem to keep the stream open to receive the rest of the response.
The webserver fails to properly implement HTTP 1.1 by incorrectly using the Transfer-Encoding: chunked header.
Http and Dio both use dart:io under the hood and both gave me the same error.
result from curl -D - http:/10.22.2.214/settings/time
HTTP/1.1 200 OK
Content-Type: text/html
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: *
Access-Control-Allow-Headers: *
Connection: close
Accept-Ranges: none
Transfer-Encoding: chunked
<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=500">
<meta charset="utf-8"><title>Time Settings</title><script>
var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings")}function B(){window.open("/settings","_self")}function S(){BTa(),GetV(),Cs(),FC()}function gId(t){return d.getElementById(t)}function Cs(){gId("cac").style.display="none",gId("coc").style.display="block",gId("ccc").style.display="none",gId("ca").selected&&(gId("cac").style.display="block"),gId("cc").selected&&(gId("coc").style.display="none",gId("ccc").style.display="block"),gId("cn").selected&&(gId("coc").style.display="none")}function BTa(){var t="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";for(i=0;i<8;i++)for(t+='<tr><td><input name="W'+i+'" id="W'+i+'" type="number" style="display:none"><input id="W'+i+'0" type="checkbox"></td><td><input name="H'+i+'" type="number" min="0" max="24"></td><td><input name="N'+i+'" type="number" min="0" max="59"></td><td><input name="T'+i+'" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W'+i+j+'" type="checkbox"></td>';for(t+='<tr><td><input name="W8" id="W8" type="number" style="display:none"><input id="W80" type="checkbox"></td><td>Sunrise<input name="H8" value="255" type="hidden"></td><td><input name="N8" type="number" min="-59" max="59"></td><td><input name="T8" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W8'+j+'" type="checkbox"></td>';for(t+='<tr><td><input name="W9" id="W9" type="number" style="display:none"><input id="W90" type="checkbox"></td><td>Sunset<input name="H9" value="255" type="hidden"></td><td><input name="N9" type="number" min="-59" max="59"><td><input name="T9" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W9'+j+'" type="checkbox"></td>';gId("TMT").innerHTML=t}function FC(){for(j=0;j<8;j++)for(i=0;i<10;i++)gId("W"+i+j).checked=gId("W"+i).value>>j&1}function Wd(){for(a=[0,0,0,0,0,0,0,0,0,0],i=0;i<10;i++){for(m=1,j=0;j<8;j++)a[i]+=gId("W"+i+j).checked*m,m*=2;gId("W"+i).value=a[i]}}function addRow(t,e,n,i){var d=gId("macros"),u=d.rows.length,c=d.insertRow(u);document.createElement("td");c.insertCell(0).innerHTML=`Button ${t}:`,c.insertCell(1).innerHTML=`<input name="MP${t}" type="number" min="0" max="250" value="${e}" required>`,c.insertCell(2).innerHTML=`<input name="ML${t}" type="number" min="0" max="250" value="${n}" required>`,c.insertCell(3).innerHTML=`<input name="MD${t}" type="number" min="0" max="250" value="${i}" required>`}function GetV() {
d.Sf.NT.checked=1;d.Sf.NS.value="Ntp-wwv.nist.gov";d.Sf.CF.checked=0;d.Sf.TZ.selectedIndex=6;d.Sf.UO.value=0;d.Sf.LN.value="0.00";d.Sf.LT.value="0.00";d.getElementsByClassName("times")[0].innerHTML="2106-2-7, 12:39:17 AM";d.Sf.OL.selectedIndex=0;d.Sf.O1.value=0;d.Sf.O2.value=0;d.Sf.OM.value=0;d.Sf.OS.checked=0;d.Sf.O5.checked=0;d.Sf.CX.value="HHMMSS";d.Sf.CB.checked=1;d.Sf.CE.checked=0;d.Sf.CY.value=22;d.Sf.CI.value=1;d.Sf.CD.value=1;d.Sf.CH.value=2;d.Sf.CM.value=1;d.Sf.CS.value=1;d.Sf.A0.value=0;d.Sf.A1.value=0;d.Sf.MC.value=0;d.Sf.MN.value=0;addRow(0,0,0,0);addRow(1,0,0,0);addRow(2,0,0,0);addRow(3,0,0,0);d.Sf.H0.value=9;d.Sf.N0.value=24;d.Sf.T0.value=1;d.Sf.W0.value=255;d.Sf.H1.value=9;d.Sf.N1.value=38;d.Sf.T1.value=2;d.Sf.W1.value=255;d.Sf.H2.value=0;d.Sf.N2.value=0;d.Sf.T2.value=0;d.Sf.W2.value=255;d.Sf.H3.value=0;d.Sf.N3.value=0;d.Sf.T3.value=0;d.Sf.W3.value=255;d.Sf.H4.value=0;d.Sf.N4.value=0;d.Sf.T4.value=0;d.Sf.W4.value=255;d.Sf.H5.value=0;d.Sf.N5.value=0;d.Sf.T5.value=0;d.Sf.W5.value=255;d.Sf.H6.value=0;d.Sf.N6.value=0;d.Sf.T6.value=0;d.Sf.W6.value=255;d.Sf.H7.value=0;d.Sf.N7.value=0;d.Sf.T7.value=0;d.Sf.W7.value=255;d.Sf.N8.value=0;d.Sf.T8.value=0;d.Sf.W8.value=0;d.Sf.N9.value=0;d.Sf.T9.value=0;d.Sf.W9.value=0;}</script><style>body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%;margin:0}hr{border-color:#666}a{color:#28f;text-decoration:none}.btn,button{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:12px 8px 8px;padding:1px 6px;cursor:pointer;text-decoration:none}.lnk{border:0}.helpB{text-align:left;position:absolute;width:60px}input{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}input:disabled{color:#888}input[type=number]{width:4em;margin:2px}input[type=number].xxl{width:100px}input[type=number].xl{width:85px}input[type=number].l{width:63px}input[type=number].m{width:56px}input[type=number].s{width:49px}input[type=number].xs{width:42px}input[type=checkbox]{transform:scale(1.5);margin-right:10px}select{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}td{padding:2px}.d5{width:4.5em!important}#toast{opacity:0;background-color:#444;border-radius:5px;bottom:64px;color:#fff;font-size:17px;padding:16px;pointer-events:none;position:fixed;text-align:center;z-index:5;transform:translateX(-50%);max-width:90%;left:50%}#toast.show{opacity:1;background-color:#264;animation:fadein .5s,fadein .5s 2.5s reverse}#toast.error{opacity:1;background-color:#b21;animation:fadein .5s}</style></head><body onload="S()"><form
id="form_s" name="Sf" method="post" onsubmit="Wd()"><div class="helpB"><button
type="button" onclick="H()">?</button></div><button type="button" onclick="B()">
Back</button><button type="submit">Save</button><hr><h2>Time setup</h2>
Get time from NTP server: <input type="checkbox" name="NT"><br><input name="NS"
maxlength="32"><br>Use 24h format: <input type="checkbox" name="CF"><br>
Time zone: <select name="TZ"><option value="0" selected="selected">GMT(UTC)
</option><option value="1">GMT/BST</option><option value="2">CET/CEST</option>
<option value="3">EET/EEST</option><option value="4">US-EST/EDT</option><option
value="5">US-CST/CDT</option><option value="6">US-MST/MDT</option><option
value="7">US-AZ</option><option value="8">US-PST/PDT</option><option value="9">
CST(AWST)</option><option value="10">JST(KST)</option><option value="11">
AEST/AEDT</option><option value="12">NZST/NZDT</option><option value="13">
North Korea</option><option value="14">IST (India)</option><option value="15">
CA-Saskatchewan</option><option value="16">ACST</option><option value="17">
ACST/ACDT</option><option value="18">HST (Hawaii)</option><option value="19">
NOVT (Novosibirsk)</option><option value="20">AKST/AKDT (Anchorage)</option>
<option value="21">MX-CST/CDT</option></select><br>UTC offset: <input name="UO"
type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br>
Current local time is <span class="times">unknown</span>.<br>Latitude (N):
<input name="LT" type="number" min="-66.6" max="66.6" step="0.01">
Longitude (E): <input name="LN" type="number" min="-180" max="180" step="0.01">
<div id="sun" class="times"></div><h3>Clock</h3>Clock Overlay: <select
name="OL" onchange="Cs()"><option value="0" id="cn" selected="selected">None
</option><option value="1" id="ca">Analog Clock</option><option value="2">
Single Digit Clock</option><option value="3" id="cc">Cronixie Clock</option>
</select><br><div id="coc">First LED: <input name="O1" type="number" min="0"
max="255" required> Last LED: <input name="O2" type="number" min="0" max="255"
required><br><div id="cac">12h LED: <input name="OM" type="number" min="0"
max="255" required><br>Show 5min marks: <input type="checkbox" name="O5"><br>
</div>Seconds (as trail): <input type="checkbox" name="OS"><br></div><div
id="ccc">Cronixie Display: <input name="CX" maxlength="6"><br>
Cronixie Backlight: <input type="checkbox" name="CB"><br></div>Countdown Mode:
<input type="checkbox" name="CE"><br>Countdown Goal:<br>Year: 20 <input
name="CY" type="number" min="0" max="99" required> Month: <input name="CI"
type="number" min="1" max="12" required> Day: <input name="CD" type="number"
min="1" max="31" required><br>Hour: <input name="CH" type="number" min="0"
max="23" required> Minute: <input name="CM" type="number" min="0" max="59"
required> Second: <input name="CS" type="number" min="0" max="59" required><br>
<h3>Macro presets</h3><b>Macros have moved!</b><br><i>
Presets now also can be used as macros to save both JSON and HTTP API commands.
<br>Just enter the preset id below!</i> <i>
Use 0 for the default action instead of a preset</i><br>Alexa On/Off Preset:
<input name="A0" type="number" min="0" max="250" required> <input name="A1"
type="number" min="0" max="250" required><br>Countdown-Over Preset: <input
name="MC" type="number" min="0" max="250" required><br>
Timed-Light-Over Presets: <input name="MN" type="number" min="0" max="250"
required><br><h3>Button actions</h3><table style="margin:0 auto" id="macros">
<thead><tr><td>push<br>switch</td><td>short<br>on->off</td><td>long<br>
off->on</td><td>double<br>N/A</td></tr></thead><tbody></tbody></table><a
href="https://github.com/Aircoookie/WLED/wiki/Macros#analog-button"
target="_blank">Analog Button setup</a><h3>Time-controlled presets</h3><div
style="display:inline-block"><table id="TMT"></table></div><hr><button
type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form></body></html>
I am trying to upload images directly from the browser to the Google Cloud bucket. I am using gcs-signed-urls on the backend and followed this example.
Here is my Form upload which is working fine.
<form action={"https://" +this.state.imageForm.bucket +".storage.googleapis.com"} method="post" enctype="multipart/form-data">
<input type="hidden" name="key" value={this.state.imageForm.key}></input>
<input type="hidden" name="bucket" value={this.state.imageForm.bucket}></input>
<input type="hidden" name="GoogleAccessId" value={this.state.imageForm.GoogleAccessId}></input>
<input type="hidden" name="policy" value={this.state.imageForm.policy}></input>
<input type="hidden" name="signature" value={this.state.imageForm.signature}></input>
<input type="hidden" name="Content-Type" value={this.state.imageForm.contenttype}></input>
<input name="file" type="file"></input>
<input type="submit" value="Upload"></input>
Now I am trying to turn this into form post request using Axios.
let data = new FormData();
data.append("key",this.state.imageForm.key+ "." + picture[0].type.replace("image/", ""))
data.append("bucket", this.state.imageForm.bucket);
data.append("GoogleAccessId", this.state.imageForm.GoogleAccessId);
data.append("policy", this.state.imageForm.policy);
data.append("Content-Type", 'application/octet-stream');
data.append("file", picture[0]);
console.log("data", data);
let config = {
headers: {
'Content-Type': 'multipart/form-data',
'Access-Control-Allow-Origin': '*',
}
}
let result = await axios.post("https://" + this.state.imageForm.bucket + ".storage.googleapis.com",
data, config)
I am getting 2 errors:
1 - CROS error
create:1 Access to XMLHttpRequest at 'https://ccc.storage.googleapis.com/' from origin 'http://127.0.0.1:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
2- Post request is failing with 400 error with the response.
Here is a snapshot from the developer console.
I also updated the Cors file for my bucket, here is the config
{
"origin": [*],
"responseHeader": ["Content-Type", "access-control-allow-origin"],
"method": ["GET", "HEAD", "DELETE", "PUT", "POST"],
"maxAgeSeconds": 3600
}
I found the following note in the API POST method docs
Note: The POST object method does not support signed URLs, except in the case of resumable uploads.
So you can try the PUT method or doing a resumable upload as stated at the docs
I'm trying to get form data from my html page on a post request but when I use the getFormAttribute() function, it returns null. Here is the code:
Route postArticleRoute = router
.post("/articlePosted")
.handler(routingContext -> {
HttpServerResponse response = routingContext.response();
HttpServerRequest request = routingContext.request();
String title = request.getFormAttribute("title");
String auth = request.getFormAttribute("author");
String body = request.getFormAttribute("body");
System.out.println(title + auth + body);
});
This code returns: 'nullnullnull'
I've double checked that these are the name attributes in the html form. Just in case, here is the html:
<form action="/articlePosted" method="post">
<label>Title of Article</label><br>
<input type="text" name="title" id="postArticle-title"><br><br>
<label>Author</label><br>
<input type="text" name="author" id="postArticle-auth"><br><br>
<label>Image</label><br>
<input type="file" name="file" id="postArticle-img"><br><br>
<label>Body</label><br>
<textarea style="width: 100%; height: 500px;" name="body" id="postArticle-body"></textarea><br><br>
<input type="submit" value="Post">
</form>
Any help would be appreciated.
I created a form for my customers to submit their comment:
<form role="form" action="comment.php" method="post" class="col-md-6 col-md-offset-3" enctype="multipart/form-data">
<div>
<div class="form-group">
<label>Email<span style="color:gray;"> (optional)</span></label>
<input type="email" name="email" class="form-control">
</div>
<div class="form-group">
<label>Description<span style="color:red;"> *</span></label>
<textarea class="form-control" name="description" rows="3" required></textarea>
</div>
<div class="form-group">
<label>Attach your file</label>
<input type="file" name="file">
</div>
<div style="margin-top: 25px; direction: rtl;">
<div class="form-group">
<button type="submit" name="submit" value="yes" class="btn btn-info">Send Comment</button>
</div>
</div>
</div>
</form>
I used PHPMailer for sending Email in comment.php:
if($g_ok == 1){
$email = new PHPMailer();
$email->From = 'noreply#ideanetwork.co';
$email->FromName = 'Idea Network Ticketing';
$email->Subject = 'New comment from ticketing portal';
$email->Body = $bodytext;
$email->AddAddress( 'submitcomments#ideanetwork.co' );
$email->WordWrap = 70;
if (isset($_FILES['file']) && $_FILES['file']['error'] == UPLOAD_ERR_OK) {
$info = pathinfo($_FILES['file']['name']);
$ext = $info['extension'];
if ($ext == "php" or $ext == "exe" or $ext == "msi"){
header("location:../php/accessdenied.php");
exit;
}
else{
if (filesize($_FILES['file']['tmp_name']) > 4194304){
$filenote .= "Maximum file size must be 4 MB.";
}
else{
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $_FILES['file']['tmp_name']);
$email->AddAttachment($_FILES['file']['tmp_name'], $_FILES['file']['name'], $encoding = 'base64', $mime_type);
}
}
}
if(!$email->Send()){
$filenote .= "<script>alert('Mailer Error: " . $email->ErrorInfo."')</script>";
}
else{
$ok = 1;
}
}
also I tried this code:
$email->AddAttachment($_FILES['file']['tmp_name'], $_FILES['file']['name']);
when I tested my form, every things were OK except attached file! my email size is true, for example when I attach an image with size 150KB, my received email size is 150KB too, but that haven't any attached file. instead of file I received a long unclear text and some information about attached file in header and footer like this:
boundary="b1_2d997b3e49a2cbf59277c329683b668e"
Content-Transfer-Encoding: 8bit
This is a multi-part message in MIME format.
--b1_2d997b3e49a2cbf59277c329683b668e Content-Type: text/plain; charset=us-ascii
in header and:
--b1_2d997b3e49a2cbf59277c329683b668e Content-Type: image/jpeg; name="apple.jpg" Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=apple.jpg
/9j/4AAQSkZJRgABAQEASABIAAD/4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAABtbnRyUkdC
........(hundreds of lines like this).............. JAkwgptNz//Z
--b1_2d997b3e49a2cbf59277c329683b668e--
in footer
my PHP version: 5.5
SOLVED!!!
Problem was using PHPMailer!
PHPMailer classes can't create headers correctly. I create headers using this post and every things were OK!
Thanks #synchro
I am trying submit a form which is dynamically generated.
After form submit i get all the values from Form except one element which is file element.
Even my form enctype is multipart/form-data.
I checked with HTTPfox it is showing below
Content-Disposition: form-data; name="upfile1"; filename=""
Content-Type: application/octet-stream
I don't understand what is going on here.
var str_new='<form id="uploader'+count+'" action="testkal.php" name="frmattach_kal" class="uploader" method="post" enctype="multipart/form-data" target="upload_target'+count+'"><div id="upload_progress_form'+count+'" style="clear:both"> </div>';
str_new +='<div class="upload_form" id="f'+count+'_upload_form"><div class="attch rmailsprite"></div><div class="upld" id="upld"><div id="upl'+count+'" class="zin2" style="width:62px;overflow:hidden;cursor:pointer;float:left;position:absolute;filter: alpha(opacity=0);"><input id="myfile'+count+'" name="upfile1" type="file" class="fileclass" size="30" onchange="javascript:upload_form_attachment('+count+');" /><input type=hidden name="FormName" value="attach" /><input type=hidden name="output" value="xml" /><input type=hidden name="sid" value="5" /></div>';
str_new +='<div class="cornLeftTop" style="float:left;"><a id="anch'+count+'" style="text-decoration: underline;" href="javascript:void(0);" onclick="javascript:test_fn('+count+');">Select file</a><span class="greyI" id="upld_inst'+count+'">(This file will start attaching once the previous file is done)</span></div>';
str_new +='</div><div class="adjustdiv"></div></form><iframe id="upload_target'+count+'" name="upload_target'+count+'" class="upload_target" src=""></iframe>';
document.getElementById("uploadstate").innerHTML=str_new;
This is how i am generating form.It is getting submitted onchange event of file element.
document.getElementById("uploader"+count).setAttribute("enctype","multipart/form-data");
document.getElementById("uploader"+count).setAttribute("encoding","multipart/form-data");
I am also setting enctype here