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)
Example #3
0
// 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)