My problem is probably stupid, but I can't figure out the solution by myself, so I hope someone here can help me. I am trying to make a dll file for hosting a server on the localhost. I thought I did everything right, but I receive this error:
System.IndexOutOfRangeException: Index was outside the bounds of the array.
at minihttpc.Requests.HttpRequest.ParseHeaders(String[] requestLine) in C:\Users\deqnb\OneDrive\minihttpc\minihttpc\Requests\HttpRequest.cs:line 87
at minihttpc.Requests.HttpRequest.ParseRequest(String requestString) in C:\Users\deqnb\OneDrive\minihttpc\minihttpc\Requests\HttpRequest.cs:line 44
at minihttpc.Requests.HttpRequest..ctor(String requestString) in C:\Users\deqnb\OneDrive\minihttpc\minihttpc\Requests\HttpRequest.cs:line 21
at MiniServerHTTP.WebServer.ConnectionHandler.ReadRequest() in C:\Users\deqnb\OneDrive\minihttpc\MiniServerHTTP.WebServer\ConnectionHandler.cs:line 80
at MiniServerHTTP.WebServer.ConnectionHandler.ProcessRequest() in C:\Users\deqnb\OneDrive\minihttpc\MiniServerHTTP.WebServer\ConnectionHandler.cs:line 28
Here is my code: HttpRequest.cs:
using minihttpc.Common.CoreValidator;
using minihttpc.Common.GlobalConstants;
using minihttpc.Headers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace minihttpc.Requests
{
public class HttpRequest:IHttpRequest
{
public HttpRequest(string requestString)
{
CoreValidator.ThrowIfNullOrEmpty(requestString, nameof(requestString));
this.FormData = new Dictionary<string, object>();
this.QueryData = new Dictionary<string, object>();
this.Headers = new HttpHeaderCollection();
this.ParseRequest(requestString);
}
public string Path { get; private set; }
public string Url { get; private set; }
public Dictionary<string,object>FormData { get; }
public Dictionary<string, object> QueryData { get; }
public IHttpHeaderCollection Headers { get; private set; }
public HttpRequestMethod RequestMethod { get; private set; }
public void ParseRequest(string requestString)
{
string[] splitRequestContent = requestString.Split(new[] {
GlobalConstants.HttpNewLine }, StringSplitOptions.None);
string[] requestLine = splitRequestContent[0].Trim().Split(new[]
{ ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (!this.IsValidReqiestLine(requestLine))
{
throw new BadRequestException();
}
this.ParseRequestMethod(requestLine);
this.ParseRequestUrl(requestLine);
this.ParseRequestPath();
this.ParseHeaders(splitRequestContent.Skip(1).ToArray());
//this.ParseRequestQuerryParameters();
this.ParseRequestParameters(splitRequestContent[splitRequestContent.Length - 1]);
}
bool IsValidReqiestLine(string[] requestLine)
{
if(requestLine.Count()!=3&&requestLine[2] != "HTTP/1.1")
{
return false;
}
else
{
return true;
}
}
void ParseRequestMethod(string[] requestLine)
{
switch (requestLine[0])
{
case "GET": RequestMethod = HttpRequestMethod.Get; break;
case "POST": RequestMethod = HttpRequestMethod.Post; break;
case "PUT": RequestMethod = HttpRequestMethod.Put; break;
case "DELETE": RequestMethod = HttpRequestMethod.Delete; break;
}
}
void ParseRequestUrl(string [] requestLine)
{
this.Url = requestLine[1];
}
void ParseRequestPath()
{
this.Path = this.Url.Split("?").Take(1).First().ToString();
}
void ParseHeaders(string [] requestLine)
{
foreach(var line in requestLine)
{
Console.WriteLine(line); //a lot of info about the req
if (line == GlobalConstants.HttpNewLine) break;
string[] header = line.Split(' ').ToArray();
//Console.WriteLine(header[1]);
Headers.AddHeader(new HttpHeader(header[0],
header[1]));//seems fine //line 87
}
if (Headers.ContainsHeader("host"))
{
throw new BadRequestException();
}
}
void ParseRequestQuerryParameters()
{
if (!(this.Url.Split('?').Length > 1)) return;
this.Url.Split('?', '#')[1].Split('&').Select(plainQuerryParameter =>
plainQuerryParameter.Split());//not finished !!!!
}
void ParseFormDataParameters(string formData)
{
if (!string.IsNullOrEmpty(formData))
{
formData.Split('&').Select(plainQueryParameter =>
plainQueryParameter.Split('=')).ToList().ForEach(queryParameterKeyValue =>
this.FormData.Add(queryParameterKeyValue[0],
queryParameterKeyValue[1]));
}
}
void ParseRequestParameters(string formData)//not being called
{
ParseRequestQuerryParameters();
ParseFormDataParameters(formData);
}
}
}
ConnectionHandler.cs:
using minihttpc.Common.CoreValidator;
using minihttpc.Requests;
using MiniServerHTTP.WebServer.Results;
using MiniServerHTTP.WebServer.Routing;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace MiniServerHTTP.WebServer
{
public class ConnectionHandler
{
private readonly Socket client;
private readonly IServerRoutingTable table;
public ConnectionHandler(Socket client, IServerRoutingTable table)
{
CoreValidator.ThrowIfNull(client, nameof(client));
CoreValidator.ThrowIfNull(client, nameof(client));
this.client = client;
this.table = table;
}
public async Task ProcessRequest()
{
try
{
var httpRequest = await this.ReadRequest();
if (httpRequest != null)
{
Console.WriteLine($"Processing: {httpRequest.RequestMethod} {httpRequest.Path}...");
var httpResponse = this.HandleRequest((IHttpRequest)httpRequest);
this.ProcessResponse(httpResponse);
}
}
catch (BadRequestException e)//400
{
this.ProcessResponse(new TextResult(e.ToString(),
HttpResponseStatusCode.BadRequest));
}
catch (Exception e)//500
{
this.ProcessResponse(new TextResult(e.ToString(),
HttpResponseStatusCode.InternalServerError));
}
this.client.Shutdown(SocketShutdown.Both);
}
private async Task ProcessResponse(IHttpResponse httpResponse)
{
byte[] byteSegments = httpResponse.GetBytes();
await this.client.SendAsync(byteSegments, SocketFlags.None);
}
private IHttpResponse HandleRequest(IHttpRequest httpRequest)
{
if(!this.table.Contains(httpRequest.RequestMethod,
httpRequest.Path))
{
return new TextResult($"Route with method {httpRequest.RequestMethod} and path \"{httpRequest.Path}\"not found.",
HttpResponseStatusCode.NotFound);
}
return this.table.Get(httpRequest.RequestMethod,
httpRequest.Path).Invoke(httpRequest);
}
private async Task<HttpRequest> ReadRequest()
{
var result = new StringBuilder();
var data = new ArraySegment<byte>(new byte[1024]);
while (true)
{
int numberOfBytes = await this.client.ReceiveAsync(data.Array, SocketFlags.None);
if (numberOfBytes == 0) break;
var bytesAsString = Encoding.UTF8.GetString(data.Array, 0,
numberOfBytes);
result.Append(bytesAsString);
if (numberOfBytes < 1023) break;
}
if (result.Length == 0)
{
return null;
}
return new HttpRequest(result.ToString());
}
}
}
When I check manualy the parameters and variables in minihttpc.Requests.HttpRequest.ParseHeaders() everything seems good. I can't get where is the problem.
CodePudding user response:
string[] header = line.Split(' ').ToArray();
Headers.AddHeader(new HttpHeader(header[0], header[1]));
I can pretty much guarantee you're running into a line that doesn't have a space in it. Maybe there's a line that's empty, for example. That wouldn't be obvious from the Console.WriteLine()
that you're doing, because empty lines are kind of invisible.