func OneWebsockConnectionCheck(addr string, N uint64, t *testing.T) { conn, err := net.Dial("tcp", addr) if err != nil { t.Errorf("Ошибка подключения: %s\n", err.Error()) return } defer conn.Close() // Переходим на протокол веб-сокетов ws_key := "Iv8io/9s+lYFgZWcXczP8Q==" head_params := []HttpUtils.HeaderParam{{"Upgrade", "websocket"}, {"Connection", "Upgrade"}, {"Sec-WebSocket-Key", ws_key}} req := HttpUtils.MakeRequest("GET", "/", head_params, nil) data := req.Serialize() var sz int var e error for sz, e = conn.Write(data); (sz > 0) && (len(data) > 0); sz, e = conn.Write(data) { data = data[sz:] } if len(data) > 0 { if e == nil { t.Fatal("Запрос отправлен не полностью, ошибка - nil") } else { t.Fatalf("Запрос отправлен не полностью, ошибка - %s\n", e.Error()) } } // Ждём ответ buf := make([]byte, 100) var unworked_data []byte var resp *HttpUtils.HttpResponse for sz, _ := conn.Read(buf); sz > 0; sz, _ = conn.Read(buf) { unworked_data = append(unworked_data, buf[:sz]...) r, uw, succ := HttpUtils.ParseOneResp(unworked_data) resp = r unworked_data = uw if !succ || (resp != nil) { break } } if resp == nil { t.Fatal("Подтверждение не было получено") } if len(unworked_data) > 0 { t.Error("Остались необработанные данные") } // Проверяем ответ if resp.Code != 101 { t.Fatalf("Ошибка HTTP, код %d\n, ожидали 101", resp.Code) } var chks [3]bool for _, param := range resp.HeaderParams { if param.Name == "Upgrade" { if param.Value != "websocket" { t.Fatalf("Ошибка подтверждения: поле %s равно %s\n", param.Name, param.Value) } chks[0] = true } else if param.Name == "Connection" { if param.Value != "Upgrade" { t.Fatalf("Ошибка подтверждения: поле %s равно %s\n", param.Name, param.Value) } chks[1] = true } else if param.Name == "Sec-WebSocket-Accept" { expected_key := WebsocketUtils.GetAcceptKey(ws_key) if param.Value != expected_key { t.Fatalf("Ошибка подтверждения: поле %s равно %s вместо %s\n", param.Name, param.Value, expected_key) } chks[2] = true } } // for _, param := range resp.HeaderParams if !chks[0] { t.Fatal("Подтверждение не содержит поля Upgrade") } else if !chks[1] { t.Fatal("Подтверждение не содержит поля Connection") } else if !chks[2] { t.Fatal("Подтверждение не содержит поля Sec-WebSocket-Accept") } // Проверка отправки/получения пакетов var reader WebsocketUtils.FrameParser for num := 0; num < 100; num++ { v := (num % 2) == 0 var mask_key [4]byte for j := 0; j < 4; j++ { mask_key[j] = byte((j*num + int(N)) % 0xFF) } f := WebsocketUtils.Frame{Fin: v, Rsvs: [3]bool{v, !v, v}, Type: byte(num) % 0xF, Mask: v, MaskKey: mask_key} f.Data = make([]byte, 0, 1000) for j := 0; j < 1000; j++ { f.Data = append(f.Data, byte((j*num+int(N))%0xFF)) } f_ser := f.Serialize() for s, e := conn.Write(f_ser); (len(f_ser) > 0) && (s > 0); s, e = conn.Write(f_ser) { if e != nil { t.Fatalf("Ошибка отправки %s\n", e.Error()) } f_ser = f_ser[s:] } if len(f_ser) > 0 { t.Fatal("Не весь пакет был отправлен") } var f_rcv *WebsocketUtils.Frame for s, _ := conn.Read(buf); s > 0; s, _ = conn.Read(buf) { reader.Write(buf[:s]) if rcv_f := reader.Parse(); len(rcv_f) > 0 { // Получили пакет f_rcv = &(rcv_f[0]) break } else if s < len(buf) { break } } // Сравниваем отправленный пакет и полученный if f_rcv == nil { t.Fatal("Ответный пакет не был получен") } else if f.Type != f_rcv.Type { t.Fatalf("Неверный тип полученного пакета: ожидали %d, получили %d\n", f.Type, f_rcv.Type) } else if bytes.Compare(f.Data, f_rcv.Data) != 0 { t.Fatal("Неверные данные в полученном пакете") } } // for num := 0; num < 100; num++ } // func OneWebsockConnectionCheck(addr string, num byte, t *testing.T)
func TestHttpWebsockServer(t *testing.T) { http_req_worker := func(req *HttpUtils.HttpRequest) (*HttpUtils.HttpResponse, WsWorker) { if req != nil { var ws_worker WsWorker var resp HttpUtils.HttpResponse hparams_map := make(map[string]string) for _, p := range req.HeaderParams { hparams_map[p.Name] = p.Value } check := false for _, p := range strings.Split(hparams_map["Upgrade"], ", ") { if p == "websocket" { check = true break } } if check { check = false for _, p := range strings.Split(hparams_map["Connection"], ", ") { if p == "Upgrade" { check = true break } } if check { key, check := hparams_map["Sec-WebSocket-Key"] if check { // Переходим на протокол веб-сокетов confirm_key := WebsocketUtils.GetAcceptKey(key) resp_hparams := make([]HttpUtils.HeaderParam, 3) resp_hparams[0] = HttpUtils.HeaderParam{Name: "Upgrade", Value: "websocket"} resp_hparams[1] = HttpUtils.HeaderParam{Name: "Connection", Value: "Upgrade"} resp_hparams[2] = HttpUtils.HeaderParam{Name: "Sec-WebSocket-Accept", Value: confirm_key} resp = HttpUtils.MakeResponse(101, "Switching protocols", resp_hparams, nil) } } ws_worker = &echo_ws_worker{} } else { t.Error("Некорректный запрос (ожидаем только запрос переключения на вебсокет)") resp = HttpUtils.MakeResponse(200, "OK", req.HeaderParams, req.Body) } return &resp, ws_worker } // if req != nil resp := HttpUtils.MakeResponse(400, "Bad request", nil, nil) return &resp, nil } // http_req_worker := func(req *HttpUtils.HttpRequest) (*HttpUtils.HttpResponse, WsWorker) srv, err := RunNewServer("127.0.0.1", 45000, http_req_worker) if srv != nil { defer srv.Close() if err != nil { t.Fatalf("Ошибка запуска сервера %s, но возвращён ненулевой интерфейс\n", err.Error()) } } else { if err != nil { t.Fatalf("Ошибка запуска сервера %s\n", err.Error()) } else { t.Fatal("Ошибка запуска сервера: результат nil, nil") } } for n := uint64(0); n < 100; n++ { OneWebsockConnectionCheck("127.0.0.1:45000", n, t) } for num := 0; num < 10; num++ { var wg sync.WaitGroup for step := 0; step < 1000; step++ { wg.Add(1) N := uint64(step) go func() { defer wg.Done() OneWebsockConnectionCheck("127.0.0.1:45000", N, t) }() } wg.Wait() } } // func TestHttpWebsockServer(t *testing.T)
// type HttpWorker func(*HttpUtils.HttpRequest) (*HttpUtils.HttpResponse, WsWorker) func make_handler(root_dir string) func(req *HttpUtils.HttpRequest) (*HttpUtils.HttpResponse, CommonWebServer.WsWorker) { resp_bad_req := HttpUtils.MakeResponse(400, "Bad request", nil, nil) return func(req *HttpUtils.HttpRequest) (*HttpUtils.HttpResponse, CommonWebServer.WsWorker) { if req == nil { return &resp_bad_req, nil } hparams_map := make(map[string]string) for _, p := range req.HeaderParams { hparams_map[p.Name] = p.Value } check := false for _, p := range strings.Split(hparams_map["Upgrade"], ", ") { if p == "websocket" { check = true break } } if check { check = false for _, p := range strings.Split(hparams_map["Connection"], ", ") { if p == "Upgrade" { check = true break } } if check { key, check := hparams_map["Sec-WebSocket-Key"] if check { // Переходим на протокол веб-сокетов confirm_key := WebsocketUtils.GetAcceptKey(key) resp_hparams := make([]HttpUtils.HeaderParam, 3) resp_hparams[0] = HttpUtils.HeaderParam{Name: "Upgrade", Value: "websocket"} resp_hparams[1] = HttpUtils.HeaderParam{Name: "Connection", Value: "Upgrade"} resp_hparams[2] = HttpUtils.HeaderParam{Name: "Sec-WebSocket-Accept", Value: confirm_key} resp := HttpUtils.MakeResponse(101, "Switching protocols", resp_hparams, nil) return &resp, &EchoWsWorker{} } } } if req.Host == "/" { h_params := []HttpUtils.HeaderParam{HttpUtils.HeaderParam{Name: "Location", Value: "/index.html"}} resp := HttpUtils.MakeResponse(301, "Moved permanently", h_params, nil) return &resp, nil } var ftype string = "" switch path.Ext(req.Host) { case "html": ftype = "text/html" case "txt": ftype = "text/plain" case "png": ftype = "image/png" case "ico": ftype = "image/x-icon" } f, e := os.Open(root_dir + req.Host) if e != nil { resp := HttpUtils.MakeResponse(404, "Нету!", nil, nil) return &resp, nil } else { defer f.Close() } var resp HttpUtils.HttpResponse hexademical := "0123456789ABCDEF" switch req.Type { case "POST": s_body := string(req.Body) var req_body string for p := strings.Index(s_body, "%"); p >= 0; p = strings.Index(s_body, "%") { v := make([]byte, 1) v[0] = byte(strings.Index(hexademical, string(s_body[p+1]))) << 4 v[0] |= byte(strings.Index(hexademical, string(s_body[p+2]))) req_body = strings.Join([]string{req_body, s_body[:p], string(v)}, "") s_body = s_body[p+3:] } req_body = strings.Join([]string{req_body, s_body}, "") params := strings.Split(string(req_body), "&") resp_body := `<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <title>Введённые данные</title> <link rel="icon" href="favicon.ico" type="image/x-icon"> </head> <body> <p><h1>Вы ввели:</h1></p>` for _, p := range params { resp_body += strings.Join([]string{"<p>", p, "</p>"}, "") } resp_body += `<p><a href="index.html">Перейти на главную страницу</a></p> <p><a href="form.html">Перейти на страницу с формой</a></p> </body> </html>` body := []byte(resp_body) header_params := make([]HttpUtils.HeaderParam, 2) header_params[0] = HttpUtils.HeaderParam{Name: HttpUtils.BodySizeParamName, Value: strconv.Itoa(len(body))} header_params[1] = HttpUtils.HeaderParam{Name: "Content-Type", Value: "text/html"} resp = HttpUtils.MakeResponse(200, "OK", header_params, body) case "GET": body := make([]byte, 0, 100) buf := make([]byte, 100) for e == nil { sz, e := f.Read(buf) if sz == 0 { break } else if (e == nil) || (e == io.EOF) { body = append(body, buf[:sz]...) } else { resp = HttpUtils.MakeResponse(500, e.Error(), nil, nil) } } if (e != nil) && (e != io.EOF) { panic("Ошибка, которой быть не должно: " + e.Error()) } header_params := make([]HttpUtils.HeaderParam, 1, 2) header_params[0] = HttpUtils.HeaderParam{Name: HttpUtils.BodySizeParamName, Value: strconv.Itoa(len(body))} if ftype != "" { header_params = append(header_params, HttpUtils.HeaderParam{Name: "Content-Type", Value: ftype}) } resp = HttpUtils.MakeResponse(200, "OK", header_params, body) default: resp = HttpUtils.MakeResponse(501, "Not supported", nil, nil) } return &resp, nil } // return func(req *HttpUtils.HttpRequest) (*HttpUtils.HttpResponse, WsWorker) } // func make_handler(root_dir string) func(req *HttpUtils.HttpRequest) (*HttpUtils.HttpResponse, WsWorker)