super tiny web server in c# - c#

I am writing a super tiny web server for educational purposes.
For the following code, if I request a html page containing an image, I cannot see the image in the browser. What am I doing wrong?
static void Main(string[] args)
{
TcpListener listener = new TcpListener(9999);
listener.Start();
while (true)
{
TcpClient client = listener.AcceptTcpClient();
string request = GetRequest(client.GetStream(),
client.ReceiveBufferSize);
WriteOutput(request, client.GetStream());
client.Close();
}
}
static void WriteOutput(string request, NetworkStream output)
{
try
{
string[] reqs = request.Split(' ');
WriteOutputHelper(output, reqs[1].Substring(1));
}
catch (Exception)
{
WriteOutputHelper(output, "404.html");
}
}
private static void WriteOutputHelper(NetworkStream output, string file)
{
byte[] statusLine = (new System.Text.ASCIIEncoding()).
GetBytes(GetStatusLine(file) + "\r\n\r\n");
output.Write(statusLine, 0, statusLine.Length);
byte[] ContentType =
(new System.Text.ASCIIEncoding()).GetBytes(GetContentType(file) +
"\r\n\r\n");
output.Write(ContentType, 0, ContentType.Length);
byte[] response = System.IO.File.ReadAllBytes("C:\\" + file);
output.Write(response, 0, response.Length);
output.Flush();
}
static string GetContentType(string fileName)
{
string i = "<META http-equiv=\"Content-Type\" content=\"";
if ((fileName.IndexOf(".htm") > -1) || (fileName.IndexOf(".html") > -1))
i = i + "text/html";
else if (fileName.IndexOf(".jpg") > -1)
i = i + "image/jpeg";
i = i + ";\">";
return i;
}
static string GetStatusLine(string fileName)
{
string i = "HTTP/1.0 ";
if (fileName.IndexOf("404") > -1)
return i + "404 Not Found";
else if (fileName.IndexOf("jpg") > -1)
return i + "302 Found";
return i + "200 OK";
}
static string GetRequest(NetworkStream reqStream,int bufSize)
{
byte[] bytesFrom = new byte[10025];
reqStream.Read(bytesFrom, 0, bufSize);
string request = System.Text.Encoding.ASCII.GetString(bytesFrom);
return request;
}
Edited:
static void imageTest(NetworkStream output)
{
byte[] fileContent = System.IO.File.ReadAllBytes("C:\\sachin.jpg");
string statusLine = "HTTP/1.0 200 OK" + System.Environment.NewLine;
string contentType = "Content-type: image/jpeg" + System.Environment.NewLine;
string contentLength = "Content-length: " + fileContent.Length + System.Environment.NewLine;
System.Text.UnicodeEncoding coding = new UnicodeEncoding();
byte[] headers = coding.GetBytes(statusLine + contentType + contentLength);
output.Write(headers, 0, headers.Length);
output.Write(fileContent, 0, fileContent.Length);
output.Flush();
}
For the code above, I am getting this error in fiddler.
The Server did not return properly formatted HTTP Headers. HTTP headers
should be terminated with CRLFCRLF. These were terminated with LFLF.
I am using Unicode encoding because I want to convert string to bytes and I only know to use encoding.

The response for a JPG must be just the HTTP header and then the contents of the JPEG, not any HTML around it.
Something like
HTTP/1.0 200 OK
Content-type: image/jpeg
Content-length: XXXXX
RAWJPEGDATA
Fill in XXXXX with the number of bytes in the Jpeg, and just output the raw JPEG data directly, without any encoding.
Use Fiddler or Firebug to help debug -- they show the exact requests/responses being sent.

It looks like you are sending a 302 Found status for jpeg files, which is meant for redirects. You need to send 200 OK like you do for the HTML file.

I think the problem is in the WriteOutputHelper and the GetContentTypeHelper methods.
The headers should not have \r\n\r\n, one should suffice, also, the GetContentTypeHelper method should return a header like:
Content-type: image/jpeg
Not a html <meta> element which is meant for (X)HTML content, not a HTTP header.

Have you thought about using cassini, It is open source under the project title of utildev. You can definately write your own but you can never cover all your bases. An off the hip guess of what the issue is that all the mime types are not supported by your lite web server.

Related

Cannot read data from HttpListener .NET

I am stuck with reading data from HttpListener. Data arrives, I verify it with request.ContentLength64 that is usually over 8000 and it increases as the server generates more and more data.
The server sends data as HTTP post and the content type is text/plain.
When I try to check whether streamreader got some data via its length attribute I get 0.
The code is a little bit messy as I was trying different ways to make it work but unfortunatelly I had no luck.
Does anyone got an idea what I'm doing wrong?
Thanks!
HttpListener listener2 = new HttpListener();
listener2.Prefixes.Clear();
listener2.Prefixes.Add("http://+:4200/");
listener2.Prefixes.Add("http://XXX.XXX.eu/");
listener2.Start();
LogWriteLine("http listener started listening to: " +listener2.Prefixes);
try
{
while (true)//change to match end check
{
LogWriteLine("http listener waiting");
HttpListenerContext context = listener2.GetContext();
LogWriteLine("http request arrived");
HttpListenerRequest request = context.Request;
// Obtain a response object.
HttpListenerResponse response = context.Response;
System.IO.Stream body = request.InputStream;
System.Text.Encoding encoding = request.ContentEncoding;
System.IO.StreamReader reader = new System.IO.StreamReader(body, encoding);
if (!request.HasEntityBody)
{
LogWriteLine("No client data was sent with the request.");
Thread.Sleep(300);
//return;
}
if (request.ContentType != null)
{
LogWriteLine("Client data content type " + request.ContentType);
}
LogWriteLine("Client data content length " + request.ContentLength64); //Works fine
LogWriteLine("Start of client data:");
// Convert the data to a string and display it on the console.
Console.WriteLine(body.CanSeek);
string s = reader.ReadToEnd();
var ahoj = new StreamReader(context.Request.InputStream).ReadToEnd();
Console.WriteLine("ahoj length " + ahoj.Length); //0
Console.WriteLine(s); //nothing
string text;
var bytes = default(byte[]);
using (var reader1 = new StreamReader(request.InputStream,
request.ContentEncoding))
{
text = reader1.ReadToEnd();
Console.WriteLine(text + text.Length); //output: 0
using (var memstream = new MemoryStream())
{
reader1.BaseStream.CopyTo(memstream);
bytes = memstream.ToArray();
}
Console.WriteLine("bytes:" + bytes.Length); //output: bytes: 0
}
LogWriteLine("End of client data:");
//write to console file
sw.Write(s);
body.Close();
reader.Close();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}

c# Web Server - How to send images

I'm creating a server using C# and I'm able to receive requests and return HTML, but I'm not sure what I have to do to send image files so they will display on the page.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;
namespace Ambar
{
class Program
{
static void Main(string[] args)
{
TcpListener listener = new TcpListener(80);
listener.Start();
while (true)
{
Console.WriteLine("waiting for a connection");
TcpClient client = listener.AcceptTcpClient();
StreamReader sr = new StreamReader(client.GetStream());
StreamWriter sw = new StreamWriter(client.GetStream());
Console.WriteLine(client.GetStream().ToString());
try
{
string request = sr.ReadLine();
Console.WriteLine(request);
string[] tokens = request.Split(' ');
string page = tokens[1];
if (page == "/")
{
page = "/default.htm";
}
StreamReader file = new StreamReader("../../web" + page);
sw.WriteLine("HTTP/1.0 200 OK\n");
string data = file.ReadLine();
while (data != null)
{
sw.WriteLine(data);
sw.Flush();
data = file.ReadLine();
}
}
catch (Exception e)
{
sw.WriteLine("HTTP/1.0 404 OK\n");
sw.WriteLine("<H1> Future Site of Ambar Remake </H!>");
sw.Flush();
}
client.Close();
}
}
}
I'm able to host whatever HTML I want, but if I try to display an image like
<img src="picture.gif" alt="a picture" height="42" width="42">
I'm not sure how to host that image and have it displayed there.
I assume you are using web sockets.
You'll need to return the image as a base 64 encoded string, then use the
<img src ="data:image/png;base64," + base64ImageHere
Format
HTTP request and response header have two line breaks(\n) to divide Header part and content part.
for example:
(Request that requesting an image(image.gif) from server)
GET /image.gif HTTP/1.1
User-Agent: WebBrowser
Accept-Encoding: deflate,gzip
Connection: keep-alive
... and more headers if present ...
(Response for image request)
HTTP/1.1 200 OK
Content-Length: <Length_Of_Content>
Content-Type: image/gif
... and more headers if present ...
<Data_Of_Image>
As you can see, there's two line breaks(\n) between response header and contents.
So, you have to read files as byte array(byte[]). In this situation, you can easily read files using System.IO.File.ReadAllBytes(string).
Now, one more left. I told you read files as byte array, but there's no way to join string and byte[]. So you have to encode string to byte[] and join header bytes and content bytes.
+ We can't send byte array via StreamWriter so, we'll use NetworkStream.
Here's explanation:
NetworkStream ns = client.GetStream();
...
string[] tokens = request.Split(' ');
string page = tokens[1];
if (page == "/")
{
page = "/default.htm";
}
//StreamReader file = new StreamReader("../../web" + page);
byte[] file = null;
try { file = File.ReadAllBytes(#"../../web" + page); }
// In this 'catch' block, you can't read requested file.
catch {
// do something (like printing error message)
}
// We are not going to use StreamWriter, we'll use StringBuilder
StringBuilder sbHeader = new StringBuilder();
// STATUS CODE
sbHeader.AppendLine("HTTP/1.1 200 OK");
// CONTENT-LENGTH
sbHeader.AppendLine("Content-Length: " + file.Length);
// Append one more line breaks to seperate header and content.
sbHeader.AppendLine();
// List for join two byte arrays.
List<byte> response = new List<byte>();
// First, add header.
response.AddRange(Encoding.ASCII.GetBytes(sbHeader.ToString()));
// Last, add content.
response.AddRange(file);
// Make byte array from List<byte>
byte[] responseByte = response.ToArray();
// Send entire response via NetworkStream
ns.Write(responseByte, 0, responseByte.Length);
This is it. I hope you can understand my bad english :O.
Hope this help to you!

C# HttpWebRequest Content-type not Changing

In C# i need to POST some data to a web server using HTTP. I keep getting errors returned by the web server and after sniffing throught the data I dound that the problem is that thew Content-type header is still set to "text/html" and isn't getting changed to "application/json; Charset=UTF-8" as in my program. I've tried everything I can think of that might stop it getting changed, but am out of ideas.
Here is the function that is causing problems:
private string post(string uri, Dictionary<string, dynamic> parameters)
{
//Put parameters into long JSON string
string data = "{";
foreach (KeyValuePair<string, dynamic> item in parameters)
{
if (item.Value.GetType() == typeof(string))
{
data += "\r\n" + item.Key + ": " + "\"" + item.Value + "\"" + ",";
}
else if (item.Value.GetType() == typeof(int))
{
data += "\r\n" + item.Key + ": " + item.Value + ",";
}
}
data = data.TrimEnd(',');
data += "\r\n}";
//Setup web request
HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(Url + uri);
wr.KeepAlive = true;
wr.ContentType = "application/json; charset=UTF-8";
wr.Method = "POST";
wr.ContentLength = data.Length;
//Ignore false certificates for testing/sniffing
wr.ServerCertificateValidationCallback = delegate { return true; };
try
{
using (Stream dataStream = wr.GetRequestStream())
{
//Send request to server
dataStream.Write(Encoding.UTF8.GetBytes(data), 0, data.Length);
}
//Get response from server
WebResponse response = wr.GetResponse();
response.Close();
}
catch (WebException e)
{
MessageBox.Show(e.Message);
}
return "";
}
The reason i'm getting problems is because the content-type stays as "text/html" regardless of what I set it as.
Thanks in advence.
As odd as this might sound, but this worked for me:
((WebRequest)httpWebRequest).ContentType = "application/json";
this changes the internal ContentType which updates the inherited one.
I am not sure why this works but I would guess it has something to do with the ContentType being an abstract property in WebRequest and there is some bug or issue in the overridden one in HttpWebRequest
A potential problem is that you're setting the content length based on the length of the string, but that's not necessarily the correct length to send. That is, you have in essence:
string data = "whatever goes here."
request.ContentLength = data.Length;
using (var s = request.GetRequestStream())
{
byte[] byteData = Encoding.UTF8.GetBytes(data);
s.Write(byteData, 0, data.Length);
}
This is going to cause a problem if encoding your string to UTF-8 results in more than data.Length bytes. That can happen if you have non-ASCII characters (i.e. accented characters, symbols from non-English languages, etc.). So what happens is your entire string isn't sent.
You need to write:
string data = "whatever goes here."
byte[] byteData = Encoding.UTF8.GetBytes(data);
request.ContentLength = byteData.Length; // this is the number of bytes you want to send
using (var s = request.GetRequestStream())
{
s.Write(byteData, 0, byteData.Length);
}
That said, I don't understand why your ContentType property isn't being set correctly. I can't say that I've ever seen that happen.

UTF8 Encoded post Data causing problems windows phone

I am developing windows phone app. In that i need to send a json string to the server in UTF8 encoded format. I follow the below mentioned method.
private void RequestStreamCallBack(IAsyncResult ar)
{
try
{
HttpWebRequest request = (HttpWebRequest)ar.AsyncState;
Stream postStream = request.EndGetRequestStream(ar);
string postData = "OPERATION_NAME=" + operationName + "&INPUT_DATA=" + inputData ;
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
postStream.Write(byteArray, 0, postData.Length);
postStream.Close();
request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
}
catch (Exception ex)
{
}
}
The inputData contains the JSON string. So far it worked perfectly. But now the json string has " " character or "+" character. When these characters are present the server is not giving expected response. i don't know what I'm missing.
Please help.
Thank you.
PostData when submitted in application/x-www-urlencoded format must be URL Encoded.
It's in the name.
string postData = "OPERATION_NAME=" + URLEncode(operationName)
+ "&" + "INPUT_DATA=" + URLEncode(inputData);

Ensuring File Upload from Windows Phone to ASP.NET MVC 3

I am working on a Windows Phone app. This app will let users take pictures and upload them to my server. I have successfully implemented this. However, I've noticed that sometimes the picture does not get uploaded properly. I suspect this happens when someone puts the app to sleep before its done uploading. Or the connectivity gets interrupted. I'm really not sure how to do address this. I've included my code here. I'm hoping someone can provide some insight.
Client Side - Windows Phone App - Picture.cs
--------------------------------------------
public event EventHandler Upload_Succeeded;
public event EventHandler Upload_Failed;
public void Upload()
{
try {
WebRequest request = HttpWebRequest.Create(GetBackendPictureUploadUrl());
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.BeginGetRequestStream(new AsyncCallback(UploadBeginGetRequestStreamCallBack), request);
} catch (Exception ex)
{
Upload_Failed(this, EventArgs.Empty);
}
}
private void UploadBeginGetRequestStreamCallBack(IAsyncResult ar)
{
try {
StringBuilder sb = new StringBuilder();
this.ImageBytes.ToList<byte>().ForEach(x => sb.AppendFormat("{0}.", Convert.ToUInt32(x)));
Dictionary<string, string> parameters = new Dictionary<string, string>();
parameters.Add("username", GetUsername());
parameters.Add("pictureByts", sb.ToString());
string data = string.Empty;
foreach (string key in parameters.Keys)
{
data += key;
data += "=";
data += parameters[key];
data += "&";
}
data = data.Substring(0, data.Length - 1);
HttpWebRequest webRequest = (HttpWebRequest)(ar.AsyncState);
using (Stream postStream = webRequest.EndGetRequestStream(ar))
{
byte[] byteArray = Encoding.UTF8.GetBytes(data);
postStream.Write(byteArray, 0, byteArray.Length);
postStream.Close();
}
webRequest.BeginGetResponse(new AsyncCallback(Upload_Completed), webRequest);
} catch (Exception ex)
{
Upload_Failed(this, EventArgs.Empty);
}
}
private void Upload_Completed(IAsyncResult result)
{
Upload_Succeeded(this, EventArgs.Empty);
}
Server Size - ASP.NET MVC - MyController.cs
-------------------------------------------
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddPicture(string username, string pictureBytes)
{
string path = ConfigurationManager.AppSettings["pictureDirectory"] + "/" + username + "/";
if (Directory.Exists(path) == false)
Directory.CreateDirectory(path);
string filePath = path + "/" + Guid.NewGuid().ToString() + ".png";
// Save the picture to the file system
string[] bytesToConvert = pictureBytes.Split('.');
List<byte> fileBytes = new List<byte>();
bytesToConvert.ToList<string>().Where(x => !string.IsNullOrEmpty(x)).ToList<string>().ForEach(x => fileBytes.Add(Convert.ToByte(x)));
using (FileStream fileStream = new FileStream(filePath, FileMode.Create))
{
byte[] bytes = fileBytes.ToArray();
fileStream.Write(bytes, 0, bytes.Length);
fileStream.Close();
}
}
I'm so confused. This code looks right in my opinion. But I've noticed that files are being written on my server. Sometimes they are just grey images. Sometimes it looks like part of the image was written but not the whole thing. What am I doing wrong? How do I ensure that the file is completely uploaded / written properly? There has to be some way of doing this. Clearly Facebook etc. are doing this. What am I doing wrong?
You need to set ContentLength in your Upload method. To do that, you should compute your finished data buffer before you make the async request. Then you will have the length you need (after UTF8 encode). Then supply the data to the stream when the callback occurs.

Categories