// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package httptest

import (
	"io"
	"io/ioutil"
	"net/url"
	"reflect"
	"strings"
	"testing"

	"github.com/zmap/zcrypto/tls"
	"github.com/zmap/zgrab2/lib/http"
)

func TestNewRequest(t *testing.T) {
	tests := [...]struct {
		method, uri string
		body        io.Reader

		want     *http.Request
		wantBody string
	}{
		// Empty method means GET:
		0: {
			method: "",
			uri:    "/",
			body:   nil,
			want: &http.Request{
				Method: "GET",
				Host:   "example.com",
				URL:    &url.URL{Path: "/"},
				Header: http.Header{},
				Protocol: http.Protocol{
					Name:  "HTTP/1.1",
					Major: 1,
					Minor: 1,
				},
				RemoteAddr: "192.0.2.1:1234",
				RequestURI: "/",
			},
			wantBody: "",
		},

		// GET with full URL:
		1: {
			method: "GET",
			uri:    "http://foo.com/path/%2f/bar/",
			body:   nil,
			want: &http.Request{
				Method: "GET",
				Host:   "foo.com",
				URL: &url.URL{
					Scheme:  "http",
					Path:    "/path///bar/",
					RawPath: "/path/%2f/bar/",
					Host:    "foo.com",
				},
				Header: http.Header{},
				Protocol: http.Protocol{
					Name:  "HTTP/1.1",
					Major: 1,
					Minor: 1,
				},
				RemoteAddr: "192.0.2.1:1234",
				RequestURI: "http://foo.com/path/%2f/bar/",
			},
			wantBody: "",
		},

		// GET with full https URL:
		2: {
			method: "GET",
			uri:    "https://foo.com/path/",
			body:   nil,
			want: &http.Request{
				Method: "GET",
				Host:   "foo.com",
				URL: &url.URL{
					Scheme: "https",
					Path:   "/path/",
					Host:   "foo.com",
				},
				Header: http.Header{},
				Protocol: http.Protocol{
					Name:  "HTTP/1.1",
					Major: 1,
					Minor: 1,
				},
				RemoteAddr: "192.0.2.1:1234",
				RequestURI: "https://foo.com/path/",
				TLS: &tls.ConnectionState{
					Version:           tls.VersionTLS12,
					HandshakeComplete: true,
					ServerName:        "foo.com",
				},
			},
			wantBody: "",
		},

		// Post with known length
		3: {
			method: "POST",
			uri:    "/",
			body:   strings.NewReader("foo"),
			want: &http.Request{
				Method: "POST",
				Host:   "example.com",
				URL:    &url.URL{Path: "/"},
				Header: http.Header{},
				Protocol: http.Protocol{
					Name:  "HTTP/1.1",
					Major: 1,
					Minor: 1,
				},
				ContentLength: 3,
				RemoteAddr:    "192.0.2.1:1234",
				RequestURI:    "/",
			},
			wantBody: "foo",
		},

		// Post with unknown length
		4: {
			method: "POST",
			uri:    "/",
			body:   struct{ io.Reader }{strings.NewReader("foo")},
			want: &http.Request{
				Method: "POST",
				Host:   "example.com",
				URL:    &url.URL{Path: "/"},
				Header: http.Header{},
				Protocol: http.Protocol{
					Name:  "HTTP/1.1",
					Major: 1,
					Minor: 1,
				},
				ContentLength: -1,
				RemoteAddr:    "192.0.2.1:1234",
				RequestURI:    "/",
			},
			wantBody: "foo",
		},

		// OPTIONS *
		5: {
			method: "OPTIONS",
			uri:    "*",
			want: &http.Request{
				Method: "OPTIONS",
				Host:   "example.com",
				URL:    &url.URL{Path: "*"},
				Header: http.Header{},
				Protocol: http.Protocol{
					Name:  "HTTP/1.1",
					Major: 1,
					Minor: 1,
				},
				RemoteAddr: "192.0.2.1:1234",
				RequestURI: "*",
			},
		},
	}
	for i, tt := range tests {
		got := NewRequest(tt.method, tt.uri, tt.body)
		slurp, err := ioutil.ReadAll(got.Body)
		if err != nil {
			t.Errorf("%d. ReadAll: %v", i, err)
		}
		if string(slurp) != tt.wantBody {
			t.Errorf("%d. Body = %q; want %q", i, slurp, tt.wantBody)
		}
		got.Body = nil // before DeepEqual
		if !reflect.DeepEqual(got.URL, tt.want.URL) {
			t.Errorf("%d. Request.URL mismatch:\n got: %#v\nwant: %#v", i, got.URL, tt.want.URL)
		}
		if !reflect.DeepEqual(got.Header, tt.want.Header) {
			t.Errorf("%d. Request.Header mismatch:\n got: %#v\nwant: %#v", i, got.Header, tt.want.Header)
		}
		if !reflect.DeepEqual(got.TLS, tt.want.TLS) {
			t.Errorf("%d. Request.TLS mismatch:\n got: %#v\nwant: %#v", i, got.TLS, tt.want.TLS)
		}
		if !reflect.DeepEqual(got, tt.want) {
			t.Errorf("%d. Request mismatch:\n got: %#v\nwant: %#v", i, got, tt.want)
		}
	}
}
