ходим на сайты в интернете

This commit is contained in:
2024-02-04 15:17:32 +07:00
parent f0f1e83c60
commit 3cfa44b99c
6 changed files with 144 additions and 28 deletions
+73 -26
View File
@@ -2,15 +2,22 @@ package http
import (
"bufio"
"crypto/tls"
"errors"
"fmt"
"io"
"net"
"strconv"
"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 := "/"
arr := strings.Split(url, "/")
if len(arr) == 2 {
@@ -20,56 +27,93 @@ func Do(method string, url string) (*Response, error) {
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 {
return nil, errors.New("connect " + arr[0])
return nil, errors.New("connect " + connectPath)
}
defer conn.Close()
WriteRequest(conn, method, path, "HTTP/1.0")
WriteRequest(conn, method, arr[0], path, "HTTP/1.0", headers)
return ReadResponse(conn)
}
func WriteRequest(w io.Writer, method string, path string, httpVersion string) {
fmt.Fprintf(w, "%s %s %s\r\n\r\n", method, path, httpVersion)
func WriteRequest(
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) {
response := &Response{}
b := bufio.NewReader(r)
status, err := b.ReadString('\n')
if err != nil {
return response, errors.New("read status")
if _, err := b.ReadString(' '); err != nil {
return nil, errors.New("read httpVersion")
}
arr := strings.Split(status, " ")
response.StatusCode, err = strconv.Atoi(arr[1])
statusCodeStr, err := b.ReadString(' ')
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 := ""
contentLengthStr := ""
headers := []*Header{}
for {
headerStr, err = b.ReadString('\n')
if err != nil {
return response, errors.New("read header")
return nil, errors.New("read header")
}
if headerStr == "\r\n" {
break
}
arr := strings.Split(headerStr, ": ")
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 {
return response, errors.New("get Content-Length")
}
length, err := strconv.Atoi(contentTypeHeader.Value)
if err != nil {
return response, errors.New("read Content-Length")
return nil, errors.New("read Content-Length")
}
body := make([]byte, 0, length)
@@ -77,11 +121,14 @@ func ReadResponse(r io.Reader) (*Response, error) {
for i := 0; i < length; i++ {
r, err := b.ReadByte()
if err != nil {
return response, errors.New("read body")
return nil, errors.New("read body")
}
body = append(body, r)
}
response.Body = body
return response, nil
return &Response{
StatusCode: statusCode,
Headers: headers,
Body: body,
}, nil
}
+39
View File
@@ -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)
}