⚠WARNING
ASP.NET에 대한 포스팅이 아닙니다
나가실 문은 오른쪽 하단입니다
소스가 커질수록 점점... 머리가 복잡해진다.
시간이 지나면 나조차도 모르게 되어버릴 거 같다.
그러나 계속 나아가야 한다.
멈추지 말고 나아가야 한다.
한번 멈추면 또 멈추게 되고, 흐지부지 되고,
결국 안한것만도 못하는 시간이 된다.
항상 그랬다.
어떤 일이든 깔짝 만지다가 손을 빼면,
마음과는 다르게 그것을 다시 볼일이 없을 거라는 듯이 점점 잊어버린다.
그렇다
중도에 다시 시작을 하려해도 이어서 하기가 안된다.
그러니 나아가야 한다.
웹에서 기본이 되는 GET과 POST방식의 파라미터를 받아오기 위하여 다음과 같이 html 파일을 작성하였다.
<body>
<form action="/test/param.html" method="get" style="width:fit-content;">
<fieldset>
<legend>GET</legend>
<input name="name" type="text" value="이치고" placeholder="이름을 입력하세요" />
<input name="age" type="text" value="1" placeholder="나이를 입력하세요" />
<button type="submit">전송</button>
</fieldset>
</form>
<form action="/test/param.html" method="post" style="width:fit-content;">
<fieldset>
<legend>POST</legend>
<input name="name" type="text" value="이치예" placeholder="이름을 입력하세요" />
<input name="age" type="text" value="2" placeholder="나이를 입력하세요" />
<button type="submit">전송</button>
</fieldset>
</form>
</body>
하기와 같이 화면이 나온다.
POST man을 이용하여 html을 작성하지 않고도 테스트를 할 수 있지만, 그런 도구는 사람을 너무 편하게 만들기에 타이핑 감각을 잃게 한다.
fieldset도 legend도 바로 생각이 나지 않았다...
그리고 하는김에 request를 받는 부분을 파일로 옮기고, 강한 리펙토링을 강행하였다.
public enum Method
{
GET, POST, UNKNOW
}
public class Request : Dictionary<string, string>
{
public readonly Method method = Method.UNKNOW;
public string url;
public NameValueCollection param = new();
private readonly string body;
public readonly Dictionary<string, string> cookie;
private readonly string sessionID;
public readonly Socket client;
public readonly string err = null;
public Request(Socket client)
{
this.client = client;
byte[] data = new byte[8192];
int cnt = client.Receive(data);
if (cnt == 0) return;
var strs = Encoding.UTF8.GetString(data, 0, cnt);
String[] split = strs.Split("\r\n");
var top = split.FirstOrDefault();
var dic = "method,url,http".Split(',')
.Zip(top.Split(' '), (a, b) => new { a, b })
.ToDictionary(x => x.a, x => x.b.Trim());
foreach (Method m in Enum.GetValues(typeof(Method)))
{
if (dic["method"] != m.ToString()) continue;
this.method = m;
break;
}
foreach (var item in dic) this.Add(item.Key, item.Value);
if (this.method == Method.UNKNOW)
{
this.err = "Bad request: Invaild method";
return;
}
if (!this.ContainsKey("url"))
{
this.err = "Bad request: Can't find url";
return;
}
url = this["url"];
var startIdx = url.IndexOf('?');
if (startIdx > -1) //GET 파라미터
{
this.param = HttpUtility.ParseQueryString(url.Substring(startIdx));
url = url.Substring(0, startIdx);
}
var infos = split.Skip(1).GetEnumerator();
while (infos.MoveNext())
{
var val = infos.Current.Trim();
if (val.Length == 0) break;
var sp = val.Split(':', 2);
this.Add(sp[0].Trim(), sp.LastOrDefault()?.Trim());
}
infos.MoveNext();
body = infos.Current;
var bodyLen = int.Parse("0"+this.GetValueOrDefault("Content-Length"));
if(bodyLen > body.Length){ //재차 요청
cnt = client.Receive(data);
body+=Encoding.UTF8.GetString(data, 0, cnt);
}
this["body"] = body;
WriteLine($"[req]-------------[{DateTime.Now}]--------------");
foreach (var t in this) WriteLine($"{t.Key}: {t.Value}");
if (this.GetValueOrDefault("Content-Type") == "application/x-www-form-urlencoded")
{
this.param = HttpUtility.ParseQueryString(this.body); //POST 파라미터
};
foreach (var t in this.param.Keys) WriteLine($"{t}: {param.Get(t.ToString())}");
this.cookie = getCookie(this.GetValueOrDefault("Cookie"));
this.sessionID = this.cookie?.GetValueOrDefault("sessionID");
}
public static Dictionary<string, string> getCookie(string value)
{
if(value == null) return new Dictionary<string, string>();
var dic = value.Split(';').Select(x => x.Split('='))
.ToDictionary(x => x[0].Trim(), x => x.LastOrDefault()?.Trim());
return dic;
}
}
알다시피 GET방식은 url에 파라미터가 붙어있고 POST방식은 본문(바디)에 붙어있다.
그리고 둘 다 url 디코딩을 때려줘야 원문을 제대로 확인할 수 있는데, HttpUtility.ParseQueryString를 사용하면 알아서 해준다.
그리고 특이한 점은 한 번의 Recieve로 모든 데이터를 받아오지 않는다는 것을 발견했다.
로컬로 할 때는 몰랐는데, 구름 ide에서 실행해보니, 약 절반의 확률로 body 내용을 가져오지 않았다.
그래서 다시 Recieve 메서드를 호출하지 않을 수가 없었다.
아래의 캡쳐는 전송버튼을 클릭 했을 때의 콘솔창의 내용이다.
つづく