c# HttpWebRequest POST'ing failing - c#

So i'm trying to POST something to a webserver.
System.Net.HttpWebRequest EventReq = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create("url");
System.String Content = "id=" + Id;
EventReq.ContentLength = System.Text.Encoding.UTF8.GetByteCount(Content);
EventReq.Method = "POST";
EventReq.ContentType = "application/x-www-form-urlencoded";
System.IO.StreamWriter sw = new System.IO.StreamWriter(EventReq.GetRequestStream(), System.Text.Encoding.UTF8);
sw.Write(Content);
sw.Flush();
sw.Close();
Looks alright, i'm setting content-length based on the size of the ENCODED data...
Anyway it fails at sw.flush() with "bytes to be written to the stream exceed the Content-Length size specified"
Is StreamWriter doing some magic behind my back i'm not aware of? Is there a way i can peer into what StreamWriter is doing?

Other answers have explained how to avoid this, but I thought I'd answer why it's happening: you're ending up with a byte order mark before your actual content.
You can avoid this by calling new UTF8Encoding(false) instead of using Encoding.UTF8. Here's a short program to demonstrate the difference:
using System;
using System.Text;
using System.IO;
class Test
{
static void Main()
{
Encoding enc = new UTF8Encoding(false); // Prints 1 1
// Encoding enc = Encoding.UTF8; // Prints 1 4
string content = "x";
Console.WriteLine(enc.GetByteCount("x"));
MemoryStream ms = new MemoryStream();
StreamWriter sw = new StreamWriter(ms, enc);
sw.Write(content);
sw.Flush();
Console.WriteLine(ms.Length);
}
}

Maybe make like easier:
using(WebClient client = new WebClient()) {
NameValueCollection values = new NameValueCollection();
values.Add("id",Id);
byte[] resp = client.UploadValues("url","POST", values);
}
Or see here for a discussion allowing use like:
client.Post(destUri, new {
id = Id // other values here
});

You need not set ContentLength explicitly, since it will be set automatically to the size of data written to request stream when you close it.

Related

Server for displaying pictures in web browser

What I am trying to do is send a picture from the server to the client (web browser). So when I open the link in the browser, for example https://localhost:8080/geoserver/ (I set the port to 8080 at the beginning) it will display the message "hello world" which is fine but now I am trying to send image with the StreamWriter and all I got was some text like System.Drawing.Bitmap and there was no picture displayed in the browser. Im working with c# console application.
My code:
static void Main(string[] args)
{
HttpListener listen = new HttpListener();
string url = "http://localhost";
string port = "";
Console.Write("Nastavite port:");
port = Console.ReadLine();
url = url + ":" + port + "/geoserver/";
listen.Prefixes.Add(url);
listen.Start();
while (true)
{
Console.WriteLine("Cakam...");
HttpListenerContext kontekst = listen.GetContext();
string msg = "hello world";
kontekst.Response.ContentLength64 = Encoding.UTF8.GetByteCount(msg);
kontekst.Response.StatusCode = (int)HttpStatusCode.OK;
using(Stream stream = kontekst.Response.OutputStream)
{
using(StreamWriter writer = new StreamWriter(stream))
{
writer.Write(msg);
}
}
Console.WriteLine("Sporočilo poslano");
}
}
You can do it, by converting the image to base64 and display it in a html img tag.
1) Use the System.Drawing library to get the picture as a byte array
Image image = Image.FromFile("test-img.jpg");
MemoryStream ms = new MemoryStream();
image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] imgBytes = ms.ToArray();
2) After you need to convert it to a base64 string
string base64 = Convert.ToBase64String(imgBytes);
3) Then you create the html response text
string html = $"<html><img src=\"data: image / png; base64, {base64} \"></html>";
4) Now you can write this text to the output stream
Stream stream = kontekst.Response.OutputStream;
StreamWriter writer = new StreamWriter(stream);
writer.WriteLine(html);
So the full working code looks like this
static void Main(string[] args)
{
HttpListener listen = new HttpListener();
string url = "http://localhost";
string port = "";
Console.Write("Nastavite port:");
port = Console.ReadLine();
url = url + ":" + port + "/geoserver/";
listen.Prefixes.Add(url);
listen.Start();
while (true)
{
Console.WriteLine("Cakam...");
HttpListenerContext kontekst = listen.GetContext();
kontekst.Response.StatusCode = (int)HttpStatusCode.OK;
using (Stream stream = kontekst.Response.OutputStream)
using (Image image = Image.FromFile("test-img.jpg"))
using (MemoryStream ms = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
{
image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
string base64 = Convert.ToBase64String(ms.ToArray());
writer.WriteLine($"<html><img src=\"data: image / png; base64, {base64} \"></html>");
}
Console.WriteLine("Sporočilo poslano");
}
}
The minimal changes required to make your code work is to use the Save method to write the image to the stream, instead of Writer.Write(), which will call the ToString() of the object, (if it's not already a character array) resulting in sending the class name that you experienced.
//Can't set the response length upfront, if you really need to set it you need to
//calculate it from the size of the image.
//kontekst.Response.ContentLength64 = Encoding.UTF8.GetByteCount(msg);
//Most browsers figure it out without this, but good practice to set the type:
kontekst.Response.ContentType = "image/bmp";
kontekst.Response.StatusCode = (int)HttpStatusCode.OK;
var img = Image.FromFile(#"some.bmp");
using (Stream stream = kontekst.Response.OutputStream)
{
img.Save(stream, ImageFormat.Bmp);
}
I would also consider changing some other things:
Instead of System.Drawing.Bitmap, use System.Drawing.Image as I did if you don't need to edit the image on the server. Or even better, if you don't need to treat it as an image on the server side, just read it in with a FileStream and write it out to the Output stream.
Do you really want to implement your on server and deal with the low level HttpContext / listeners? Maybe you could use ASP.Net Core with Kestrel.

REstful POST : Bytes to be written to the stream exceed the Content-Length bytes size specified

This error gets thrown
Bytes to be written to the stream exceed
the Content-Length bytes size specified.
when I run the following code:
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/json";
request.ContentLength = Encoding.UTF8.GetByteCount(json);
using (var webStream = request.GetRequestStream())
using (var requestWriter = new StreamWriter(webStream, System.Text.Encoding.UTF8))
{
requestWriter.Write(json);
}
I read, that error could occurs when Method was HEAD or GET, but here it's POST.
Any idea what's wrong there?
The problem is that you're writing the UTF-8 BOM first, because Encoding.UTF8 does that by default. Short but complete example:
using System;
using System.IO;
using System.Text;
class Test
{
static void Main()
{
string text = "text";
var encoding = Encoding.UTF8;
Console.WriteLine(encoding.GetByteCount(text));
using (var stream = new MemoryStream())
{
using (var writer = new StreamWriter(stream, encoding))
{
writer.Write(text);
}
Console.WriteLine(BitConverter.ToString(stream.ToArray()));
}
}
}
Output:
4
EF-BB-BF-74-65-78-74
The simplest fix is either to add the preamble size to the content length, or to use an encoding which doesn't have a BOM:
Encoding utf8NoBom = new UTF8Encoding(false);
Use that instead of Encoding.UTF8, and all should be well.

How do I correctly prepare an 'HTTP Redirect Binding' SAML Request using C#

I need to create an SP initiated SAML 2.0 Authentication transaction using HTTP Redirect Binding method. It turns out this is quite easy. Just get the IdP URI and concatenate a single query-string param SAMLRequest. The param is an encoded block of xml that describes the SAML request. So far so good.
The problem comes when converting the SAML into the query string param. I believe this process of preparation should be:
Build a SAML string
Compress this string
Base64 encode the string
UrlEncode the string.
The SAML Request
<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="{0}"
Version="2.0"
AssertionConsumerServiceIndex="0"
AttributeConsumingServiceIndex="0">
<saml:Issuer>URN:xx-xx-xx</saml:Issuer>
<samlp:NameIDPolicy
AllowCreate="true"
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"/>
</samlp:AuthnRequest>
The Code
private string GetSAMLHttpRedirectUri(string idpUri)
{
var saml = string.Format(SAMLRequest, Guid.NewGuid());
var bytes = Encoding.UTF8.GetBytes(saml);
using (var output = new MemoryStream())
{
using (var zip = new DeflaterOutputStream(output))
{
zip.Write(bytes, 0, bytes.Length);
}
var base64 = Convert.ToBase64String(output.ToArray());
var urlEncode = HttpUtility.UrlEncode(base64);
return string.Concat(idpUri, "?SAMLRequest=", urlEncode);
}
}
I suspect the compression is somehow to blame. I am using the DeflaterOutputStream class from SharpZipLib which is supposed to implement an industry standard deflate-algorithm so perhaps there are some settings here I have wrong?
The encoded output can be tested using this SAML2.0 Debugger (its a useful online conversion tool). When I decode my output using this tool it comes out as nonsense.
The question therefore is: Do you know how to convert a SAML string into the correctly deflated and encoded SAMLRequest query-param?
Thank you
EDIT 1
The accepted answer below gives the answer to the problem. Here is final code as corrected by all subsequent comments and answers.
Encode SAMLRequest - Working Code
private string GenerateSAMLRequestParam()
{
var saml = string.Format(SAMLRequest, Guid.NewGuid());
var bytes = Encoding.UTF8.GetBytes(saml);
using (var output = new MemoryStream())
{
using (var zip = new DeflateStream(output, CompressionMode.Compress))
{
zip.Write(bytes, 0, bytes.Length);
}
var base64 = Convert.ToBase64String(output.ToArray());
return HttpUtility.UrlEncode(base64);
}
}
The SAMLRequest variable contains the SAML shown at the top of this question.
Decode SAMLResponse - Working Code
private string DecodeSAMLResponse(string response)
{
var utf8 = Encoding.UTF8;
var bytes = utf8.GetBytes(response);
using (var output = new MemoryStream())
{
using (new DeflateStream(output, CompressionMode.Decompress))
{
output.Write(bytes, 0, bytes.Length);
}
var base64 = utf8.GetString(output.ToArray());
return utf8.GetString(Convert.FromBase64String(base64));
}
}
I've just run the following code with your example SAML:
var saml = string.Format(sample, Guid.NewGuid());
var bytes = Encoding.UTF8.GetBytes(saml);
string middle;
using (var output = new MemoryStream())
{
using (var zip = new DeflaterOutputStream(output))
zip.Write(bytes, 0, bytes.Length);
middle = Convert.ToBase64String(output.ToArray());
}
string decoded;
using (var input = new MemoryStream(Convert.FromBase64String(middle)))
using (var unzip = new InflaterInputStream(input))
using (var reader = new StreamReader(unzip, Encoding.UTF8))
decoded = reader.ReadToEnd();
bool test = decoded == saml;
The test variable is true. This means that the zip/base64/unbase64/unzip roundtrip performs correctly. The error must occur later. Maybe the URLEncoder destroys them? Could you try similar urlencode/decode test? Also, check how long the result is. It may be possible that the resulting URL is truncated due to its length.
(edit: I've added a StreamReader instead of reading to arrays. Earlier my sample used bytes.Length to prepare the buffer and that could damage the test. Now the reading uses only the information from the compressed stream)
edit:
var saml = string.Format(sample, Guid.NewGuid());
var bytes = Encoding.UTF8.GetBytes(saml);
string middle;
using (var output = new MemoryStream())
{
using (var zip = new DeflateStream(output, CompressionMode.Compress))
zip.Write(bytes, 0, bytes.Length);
middle = Convert.ToBase64String(output.ToArray());
}
// MIDDLE is the thing that should be now UrlEncode'd
string decoded;
using (var input = new MemoryStream(Convert.FromBase64String(middle)))
using (var unzip = new DeflateStream(input, CompressionMode.Decompress))
using (var reader = new StreamReader(unzip, Encoding.UTF8))
decoded = reader.ReadToEnd();
bool test = decoded == saml;
this code produces a middle variable, that once is UrlEncoded, passes through the debugger properly. DeflateStream comes from the standard .Net's System.IO.Compression namespace. I don't have the slightest idea why the SharpZip's Deflate is not accepted by the 'debugger' site. It is undeniable that the compression works, as it manages to decompress the data properly.. it just has to be some difference in the algorithms, but I cannot tell what is the difference between this deflate and that deflate, d'oh.
The question at the top contains a "Decode SAMLResponse - Working Code" section, but that code seemed broken. After trying a few things, I discovered that it was trying to read and write to the same stream at the same time. I reworked it by separating the read and write streams and here is my solution (I am providing the request section for convenience and clarity):
Encode SAML Authentication Request:
public static string EncodeSamlAuthnRequest(this string authnRequest) {
var bytes = Encoding.UTF8.GetBytes(authnRequest);
using (var output = new MemoryStream()) {
using (var zip = new DeflateStream(output, CompressionMode.Compress)) {
zip.Write(bytes, 0, bytes.Length);
}
var base64 = Convert.ToBase64String(output.ToArray());
return HttpUtility.UrlEncode(base64);
}
}
Decode SAML Authentication Response:
public static string DecodeSamlAuthnRequest(this string encodedAuthnRequest) {
var utf8 = Encoding.UTF8;
var bytes = Convert.FromBase64String(HttpUtility.UrlDecode(encodedAuthnRequest));
using (var output = new MemoryStream()) {
using (var input = new MemoryStream(bytes)) {
using (var unzip = new DeflateStream(input, CompressionMode.Decompress)) {
unzip.CopyTo(output, bytes.Length);
unzip.Close();
}
return utf8.GetString(output.ToArray());
}
}
}

How to Read a Local MJPEG File

I'm writing an App for Windows Phone in XNA... and I'd like to read a MJPEG stream stored in the App's resources. I've found many examples of how to get a MJPEG from a website via WebHttpRequest like this:
// get the response
HttpWebRequest request = (HttpWebRequest) asyncResult.AsyncState;
HttpWebResponse response = (HttpWebResponse) request.EndGetResponse(asyncResult);
try {
// find boundary value
string contentType = response.Headers["Content-Type"];
if (!String.IsNullOrEmpty(contentType) && !contentType.Contains("=")) {
throw new FormatException("Invalid content-type header. The source is likely not returning a proper MJPEG stream.");
}
string boundary = response.Headers["Content-Type"].Split('=')[1].Replace("\"", "");
byte[] boundaryBytes = Encoding.UTF8.GetBytes(boundary.StartsWith("--") ? boundary : "--" + boundary);
using (Stream stream = response.GetResponseStream()) {
using (BinaryReader binaryReader = new BinaryReader(stream)) {
// code to parse the MJPEG stream
}
}
} finally {
response.Close();
}
... but this is not exactly what I'm looking for. Here below is my code for reading the MJPEG as a binary stream from the App's resources:
private void ParseMjpeg(object uri)
{
// what the corresponding code for determining the boundary bytes in my local MJPEG?
byte[] boundaryBytes = ???
using (Stream stream = TitleContainer.OpenStream(uri.ToString())) {
using (BinaryReader binaryReader = new BinaryReader(stream)) {
// code to parse the MJPEG stream as before: OK
}
}
}
How do I determine the boundary bytes in my code here above? Any help would be REALLY appreciated.
Thanks,
j3d
this should make your life a lot easier. As far as I understand the DLL should provide you with most of what you need.
http://channel9.msdn.com/coding4fun/articles/MJPEG-Decoder

Convert to Stream from a Url

I was trying to convert an Url to Stream but I am not sure whether I am right or wrong.
protected Stream GetStream(String gazouUrl)
{
Stream rtn = null;
HttpWebRequest aRequest = (HttpWebRequest)WebRequest.Create(gazouUrl);
HttpWebResponse aResponse = (HttpWebResponse)aRequest.GetResponse();
using (StreamReader sReader = new StreamReader(aResponse.GetResponseStream(), System.Text.Encoding.Default))
{
rtn = sReader.BaseStream;
}
return rtn;
}
Am I on the right track?
I ended up doing a smaller version and using WebClient instead the old Http Request code:
private static Stream GetStreamFromUrl(string url)
{
byte[] imageData = null;
using (var wc = new System.Net.WebClient())
imageData = wc.DownloadData(url);
return new MemoryStream(imageData);
}
You don't need to create a StreamReader there. Just return aResponse.GetResponseStream();. The caller of that method will also need to call Dispose on the stream when it's done.
The current answer is missing an example in how to use GetResponseStream()
Here is an example
// Creates an HttpWebRequest with the specified URL.
HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);
// Sends the HttpWebRequest and waits for the response.
HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
// Gets the stream associated with the response.
Stream receiveStream = myHttpWebResponse.GetResponseStream();
Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
// Pipes the stream to a higher level stream reader with the required encoding format.
StreamReader readStream = new StreamReader( receiveStream, encode );
Console.WriteLine("\r\nResponse stream received.");
Char[] read = new Char[256];
// Reads 256 characters at a time.
int count = readStream.Read( read, 0, 256 );
Console.WriteLine("HTML...\r\n");
while (count > 0)
{
// Dumps the 256 characters on a string and displays the string to the console.
String str = new String(read, 0, count);
Console.Write(str);
count = readStream.Read(read, 0, 256);
}
Console.WriteLine("");
// Releases the resources of the response.
myHttpWebResponse.Close();
// Releases the resources of the Stream.
readStream.Close();
For more details see - https://learn.microsoft.com/en-us/dotnet/api/system.net.httpwebresponse.getresponsestream?view=net-5.0

Categories