C# How do you send a file via TCP for HTTP?
Asked Answered
R

1

7

The issue I'm currently facing is that I'm unable to get my C# program to write the file I want to send in the response (I'm writing the bytes of the file) and have a valid HTTP response, the browser I'm testing it with is saying ERR_INVALID_HTTP_RESPONSE if I have appended the file to the response.

Originally I was using a HttpListener but this was requiring me to run my IDE as admin every time so I decided to move away from it and start using a TcpListener.

This is just a simple web server that is running entirely off of C# and utilizes the TcpListener from System.Net. So far I've tried using the BaseStream from the StreamWriter to write the bytes of the file into the response.

I have been searching around to look for other ways to send a file via TCP / HTTP however it seems two things are apparent, its not brilliantly documented on how to properly do it and nobody seems to have asked the same question for this specific scenario.

So far the code that handles the response to the incoming connection is as follows.

      StreamReader sr = new StreamReader(client.GetStream());
      StreamWriter sw = new StreamWriter(client.GetStream());
      string request = sr.ReadLine();
      if (request != null) {
        //byte[] testData = Encoding.UTF8.GetBytes("Test");
        string[] tokens = request.Split(" ");
        try {
          FileInfo file = files[tokens[1]];
          byte[] fileBytes = File.ReadAllBytes(file.FullName);
          sw.WriteLine("HTTP/1.0 200 OK\n");
          sw.BaseStream.Write(fileBytes, 0, fileBytes.Length);
          sw.Flush();
        } catch {
          sw.WriteLine("HTTP/1.0 404 OK\n");
          sw.Write("<h1>Error 404</h1><p>The file you were looking for does not exist</p>");
          sw.Flush();
        }
      }
      client.Close();

The expected result is that the browser will show the image much like you'd see from an imgur link, basically the same format as I will use to show the screenshot of the current result is the expected result.

image taken from https://static.mcmap.net/file/mcmap/ZG-AbGLDKwfpKnMxcF_AZVLQamyA/0wnrt.png

Robrobaina answered 14/5, 2019 at 13:47 Comment(7)
404 OK?!?! That seems a bit off.Selfcentered
It might well be but it worked just fine when I tested trying to open a file that doesn't existRobrobaina
Well, browsers only look at the 404 part; the OK is more for humans. But technically speaking it should be Not Found for a 404.Selfcentered
The reason "because it requires admin right" why you switched from HTTP Listener to TCP listener is not enough to implement your own webserver. Unless you want to spend days implementing http standard request and responses, you better stick to http listener. Much easier to solve is the problem with admin rights and http listener: https://mcmap.net/q/117871/-httplistener-access-deniedSelenium
That is subject to opinion @Harry. I literally only need one function for it, its not going to take me days to implement standard requests and responses because they simply don't need to be there for what I'm using it for.Robrobaina
@AlexJBallz, i believe you are doing this the first time. As long as you don't write the client yourself but need to support different clients like standard browsers, you will spend lots of time supporting all the unexpected behaviour.Selenium
@Selenium For the use this webserver will get, that support seriously just isn't needed.Robrobaina
P
6

HTTP dictates that initial lines and headers should end in CRLF (not LF), and that a double CRLF should separate initial lines and headers from the response body.

Here's a super-handy quick-guide to the HTTP protocol:

https://www.jmarshall.com/easy/http/

You'll also probably need to flush your StreamWriter before you switch from operating over the StreamWriter to operating over it's base stream.

sw.Write("HTTP/1.0 200 OK\r\n\r\n");
sw.Flush(); //  <-- HERE
sw.BaseStream.Write(fileBytes, 0, fileBytes.Length);

I'd also avoid WriteLine as the line endings are not guaranteed to be the same across all platforms.

Phlogistic answered 14/5, 2019 at 13:50 Comment(4)
Thank you! Made progress, it now downloads the file rather than displaying it, I should be able to work out the rest from here.Robrobaina
@Robrobaina Glad to help. I've added a short para about the wisdom of using sw.Write over sw.WriteLinePhlogistic
@Robrobaina You also need to be very careful to stick to ASCII in the initial/header portions of the transaction.Phlogistic
Thanks for all your help, I've now reached my desired result :)Robrobaina

© 2022 - 2025 — McMap. All rights reserved.