ходим на сайты в интернете
This commit is contained in:
parent
f0f1e83c60
commit
3cfa44b99c
8
go.mod
8
go.mod
|
@ -1,3 +1,11 @@
|
||||||
module git.3crabs.ru/VLADIMIR/net
|
module git.3crabs.ru/VLADIMIR/net
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
|
require github.com/stretchr/testify v1.8.4
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -2,15 +2,22 @@ package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Do(method string, url string) (*Response, error) {
|
var (
|
||||||
|
CRLF = []byte("\r\n")
|
||||||
|
SP = []byte(" ")
|
||||||
|
)
|
||||||
|
|
||||||
|
func Do(method string, url string, headers []*Header) (*Response, error) {
|
||||||
|
url = strings.TrimPrefix(url, "http://")
|
||||||
|
url = strings.TrimPrefix(url, "https://")
|
||||||
path := "/"
|
path := "/"
|
||||||
arr := strings.Split(url, "/")
|
arr := strings.Split(url, "/")
|
||||||
if len(arr) == 2 {
|
if len(arr) == 2 {
|
||||||
|
@ -20,56 +27,93 @@ func Do(method string, url string) (*Response, error) {
|
||||||
path = "/" + path
|
path = "/" + path
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", arr[0])
|
connectPath := arr[0]
|
||||||
|
// conn, err := net.Dial("tcp", connectPath)
|
||||||
|
conn, err := tls.Dial("tcp", connectPath, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("connect " + arr[0])
|
return nil, errors.New("connect " + connectPath)
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
WriteRequest(conn, method, path, "HTTP/1.0")
|
WriteRequest(conn, method, arr[0], path, "HTTP/1.0", headers)
|
||||||
return ReadResponse(conn)
|
return ReadResponse(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func WriteRequest(w io.Writer, method string, path string, httpVersion string) {
|
func WriteRequest(
|
||||||
fmt.Fprintf(w, "%s %s %s\r\n\r\n", method, path, httpVersion)
|
w io.Writer,
|
||||||
|
method string,
|
||||||
|
host string,
|
||||||
|
path string,
|
||||||
|
httpVersion string,
|
||||||
|
headers []*Header,
|
||||||
|
) {
|
||||||
|
io.WriteString(w, method)
|
||||||
|
w.Write(SP)
|
||||||
|
io.WriteString(w, path)
|
||||||
|
w.Write(SP)
|
||||||
|
io.WriteString(w, httpVersion)
|
||||||
|
w.Write(CRLF)
|
||||||
|
|
||||||
|
for _, header := range headers {
|
||||||
|
WriteHeader(w, header.Name, header.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// headers end
|
||||||
|
w.Write(CRLF)
|
||||||
|
|
||||||
|
// todo: body
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteHeader(w io.Writer, name string, value string) {
|
||||||
|
fmt.Fprintf(w, "%s:%s", name, value)
|
||||||
|
w.Write(CRLF)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadResponse(r io.Reader) (*Response, error) {
|
func ReadResponse(r io.Reader) (*Response, error) {
|
||||||
response := &Response{}
|
|
||||||
|
|
||||||
b := bufio.NewReader(r)
|
b := bufio.NewReader(r)
|
||||||
|
|
||||||
status, err := b.ReadString('\n')
|
if _, err := b.ReadString(' '); err != nil {
|
||||||
if err != nil {
|
return nil, errors.New("read httpVersion")
|
||||||
return response, errors.New("read status")
|
|
||||||
}
|
}
|
||||||
arr := strings.Split(status, " ")
|
statusCodeStr, err := b.ReadString(' ')
|
||||||
response.StatusCode, err = strconv.Atoi(arr[1])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response, errors.New("read status code")
|
return nil, errors.New("read statusCode")
|
||||||
|
}
|
||||||
|
if _, err := b.ReadString('\r'); err != nil {
|
||||||
|
return nil, errors.New("read statusName")
|
||||||
|
}
|
||||||
|
if _, err := b.ReadString('\n'); err != nil {
|
||||||
|
return nil, errors.New("read LF")
|
||||||
|
}
|
||||||
|
|
||||||
|
statusCode, err := strconv.Atoi(statusCodeStr[:len(statusCodeStr)-1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("read statusCode")
|
||||||
}
|
}
|
||||||
|
|
||||||
headerStr := ""
|
headerStr := ""
|
||||||
|
contentLengthStr := ""
|
||||||
|
headers := []*Header{}
|
||||||
for {
|
for {
|
||||||
headerStr, err = b.ReadString('\n')
|
headerStr, err = b.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response, errors.New("read header")
|
return nil, errors.New("read header")
|
||||||
}
|
}
|
||||||
if headerStr == "\r\n" {
|
if headerStr == "\r\n" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
arr := strings.Split(headerStr, ": ")
|
arr := strings.Split(headerStr, ": ")
|
||||||
value := strings.TrimSpace(arr[1])
|
value := strings.TrimSpace(arr[1])
|
||||||
response.Headers = append(response.Headers, &Header{Name: arr[0], Value: value})
|
header := &Header{Name: arr[0], Value: value}
|
||||||
|
if header.Name == "Content-Length" {
|
||||||
|
contentLengthStr = header.Value
|
||||||
|
}
|
||||||
|
headers = append(headers, header)
|
||||||
}
|
}
|
||||||
|
|
||||||
contentTypeHeader, err := response.GetHeader("Content-Length")
|
length, err := strconv.Atoi(contentLengthStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response, errors.New("get Content-Length")
|
return nil, errors.New("read Content-Length")
|
||||||
}
|
|
||||||
length, err := strconv.Atoi(contentTypeHeader.Value)
|
|
||||||
if err != nil {
|
|
||||||
return response, errors.New("read Content-Length")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body := make([]byte, 0, length)
|
body := make([]byte, 0, length)
|
||||||
|
@ -77,11 +121,14 @@ func ReadResponse(r io.Reader) (*Response, error) {
|
||||||
for i := 0; i < length; i++ {
|
for i := 0; i < length; i++ {
|
||||||
r, err := b.ReadByte()
|
r, err := b.ReadByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response, errors.New("read body")
|
return nil, errors.New("read body")
|
||||||
}
|
}
|
||||||
body = append(body, r)
|
body = append(body, r)
|
||||||
}
|
}
|
||||||
response.Body = body
|
|
||||||
|
|
||||||
return response, nil
|
return &Response{
|
||||||
|
StatusCode: statusCode,
|
||||||
|
Headers: headers,
|
||||||
|
Body: body,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_WriteRequest(t *testing.T) {
|
||||||
|
b := &strings.Builder{}
|
||||||
|
|
||||||
|
WriteRequest(b, "GET", "example.ru", "/", "HTTP/1.0", nil)
|
||||||
|
|
||||||
|
assert.Equal(t, "GET / HTTP/1.0\r\n\r\n", b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ReadResponse(t *testing.T) {
|
||||||
|
b := bytes.NewBuffer(
|
||||||
|
[]byte("HTTP/1.1 200 OK\r\nDate: Sat, 03 Feb 2024 16:40:29 GMT\r\nContent-Length: 12\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nHello World!"),
|
||||||
|
)
|
||||||
|
|
||||||
|
r, err := ReadResponse(b)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 200, r.StatusCode)
|
||||||
|
|
||||||
|
assert.Equal(t, "Date", r.Headers[0].Name)
|
||||||
|
assert.Equal(t, "Sat, 03 Feb 2024 16:40:29 GMT", r.Headers[0].Value)
|
||||||
|
|
||||||
|
assert.Equal(t, "Content-Length", r.Headers[1].Name)
|
||||||
|
assert.Equal(t, "12", r.Headers[1].Value)
|
||||||
|
|
||||||
|
assert.Equal(t, "Content-Type", r.Headers[2].Name)
|
||||||
|
assert.Equal(t, "text/plain; charset=utf-8", r.Headers[2].Value)
|
||||||
|
|
||||||
|
assert.Equal(t, []byte("Hello World!"), r.Body)
|
||||||
|
}
|
|
@ -7,9 +7,21 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
r, err := http.Do("GET", "127.0.0.1:8081")
|
r, err := http.Do(
|
||||||
|
"GET",
|
||||||
|
"https://3crabs.ru:443",
|
||||||
|
[]*http.Header{
|
||||||
|
{Name: "Host", Value: "3crabs.ru"},
|
||||||
|
{Name: "User-Agent", Value: "3crabs/0.0.1"},
|
||||||
|
{Name: "Accept", Value: "*/*"},
|
||||||
|
},
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
fmt.Println(r.StatusCode, string(r.Body))
|
fmt.Println(r.StatusCode)
|
||||||
|
for _, header := range r.Headers {
|
||||||
|
fmt.Println(header)
|
||||||
|
}
|
||||||
|
fmt.Println(string(r.Body))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Println(r.Header)
|
||||||
fmt.Fprintf(w, "Hello World!")
|
fmt.Fprintf(w, "Hello World!")
|
||||||
w.WriteHeader(200)
|
w.WriteHeader(200)
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue