1+ import * as _ from 'lodash' ;
2+ import * as React from 'react' ;
3+ import { action , computed } from 'mobx' ;
4+ import { inject , observer } from 'mobx-react' ;
5+ import {
6+ MOCKTTP_PARAM_REF ,
7+ ProxySetting ,
8+ ProxySettingSource ,
9+ RuleParameterReference
10+ } from 'mockttp' ;
11+
12+ import { RawHeaders } from '../../types' ;
13+ import { styled } from '../../styles' ;
14+ import { bufferToString , isProbablyUtf8 , stringToBuffer } from '../../util' ;
15+
16+ import { sendRequest } from '../../services/server-api' ;
17+ import { RulesStore } from '../../model/rules/rules-store' ;
18+ import { SendStore } from '../../model/send/send-store' ;
19+ import { ClientProxyConfig , RULE_PARAM_REF_KEY } from '../../model/send/send-data-model' ;
20+
21+ import { EditableRawHeaders } from '../common/editable-headers' ;
22+ import { ThemedSelfSizedEditor } from '../editor/base-editor' ;
23+ import { Button , TextInput } from '../common/inputs' ;
24+
25+ const RequestPaneContainer = styled . section `
26+ display: flex;
27+ flex-direction: column;
28+ ` ;
29+
30+ const UrlInput = styled ( TextInput ) `
31+ ` ;
32+
33+ export const getEffectivePort = ( url : { protocol : string | null , port : string | null } ) => {
34+ if ( url . port ) {
35+ return parseInt ( url . port , 10 ) ;
36+ } else if ( url . protocol === 'https:' || url . protocol === 'wss:' ) {
37+ return 443 ;
38+ } else {
39+ return 80 ;
40+ }
41+ }
42+
43+ @inject ( 'rulesStore' )
44+ @inject ( 'sendStore' )
45+ @observer
46+ export class RequestPane extends React . Component < {
47+ rulesStore ?: RulesStore ,
48+ sendStore ?: SendStore
49+ } > {
50+
51+ @computed
52+ get method ( ) {
53+ return this . props . sendStore ! . requestInput . method ;
54+ }
55+
56+ @computed
57+ get url ( ) {
58+ return this . props . sendStore ! . requestInput . url ;
59+ }
60+
61+
62+ @computed
63+ get headers ( ) {
64+ return this . props . sendStore ! . requestInput . headers ;
65+ }
66+
67+ @computed
68+ get body ( ) {
69+ return this . props . sendStore ! . requestInput . rawBody ;
70+ }
71+
72+ @computed
73+ private get bodyTextEncoding ( ) {
74+ // If we're handling text data, we want to show & edit it as UTF8.
75+ // If it's binary, that's a lossy operation, so we use binary (latin1) instead.
76+ return isProbablyUtf8 ( this . body )
77+ ? 'utf8'
78+ : 'binary' ;
79+ }
80+
81+ render ( ) {
82+ const bodyString = bufferToString ( this . body , this . bodyTextEncoding ) ;
83+
84+ return < RequestPaneContainer >
85+ < UrlInput
86+ placeholder = 'https://example.com/hello?name=world'
87+ value = { this . url }
88+ onChange = { this . updateUrl }
89+ />
90+ < EditableRawHeaders
91+ headers = { this . headers }
92+ onChange = { this . updateHeaders }
93+ />
94+ < ThemedSelfSizedEditor
95+ contentId = 'request'
96+ language = { 'text' }
97+ value = { bodyString }
98+ onChange = { this . updateBody }
99+ />
100+ < Button
101+ onClick = { this . sendRequest }
102+ />
103+ </ RequestPaneContainer > ;
104+ }
105+
106+ @action . bound
107+ updateUrl ( changeEvent : React . ChangeEvent < HTMLInputElement > ) {
108+ const { requestInput } = this . props . sendStore ! ;
109+ requestInput . url = changeEvent . target . value ;
110+ }
111+
112+ @action . bound
113+ updateHeaders ( headers : RawHeaders ) {
114+ const { requestInput } = this . props . sendStore ! ;
115+ requestInput . headers = headers ;
116+ }
117+
118+ @action . bound
119+ updateBody ( input : string ) {
120+ const { requestInput } = this . props . sendStore ! ;
121+ requestInput . rawBody = stringToBuffer ( input , this . bodyTextEncoding ) ;
122+ }
123+
124+ @action . bound
125+ async sendRequest ( ) {
126+ const rulesStore = this . props . rulesStore ! ;
127+ const passthroughOptions = rulesStore . activePassthroughOptions ;
128+
129+ const url = new URL ( this . url ) ;
130+ const effectivePort = getEffectivePort ( url ) ;
131+ const hostWithPort = `${ url . hostname } :${ effectivePort } ` ;
132+ const clientCertificate = passthroughOptions . clientCertificateHostMap ?. [ hostWithPort ] ||
133+ passthroughOptions . clientCertificateHostMap ?. [ url . hostname ! ] ||
134+ undefined ;
135+
136+ const responseStream = await sendRequest ( {
137+ url : this . url ,
138+ method : this . method ,
139+ headers : this . headers ,
140+ rawBody : this . body
141+ } , {
142+ ignoreHostHttpsErrors : passthroughOptions . ignoreHostHttpsErrors ,
143+ trustAdditionalCAs : rulesStore . additionalCaCertificates . map ( ( cert ) => ( { cert : cert . rawPEM } ) ) ,
144+ clientCertificate,
145+ proxyConfig : getProxyConfig ( rulesStore . proxyConfig ) ,
146+ lookupOptions : passthroughOptions . lookupOptions
147+ } ) ;
148+
149+ const reader = responseStream . getReader ( ) ;
150+ while ( true ) {
151+ const { done, value } = await reader . read ( ) ;
152+ if ( done ) return ;
153+ else console . log ( value ) ;
154+ }
155+ }
156+
157+ }
158+
159+ function getProxyConfig ( proxyConfig : RulesStore [ 'proxyConfig' ] ) : ClientProxyConfig {
160+ if ( ! proxyConfig ) return undefined ;
161+
162+ if ( _ . isArray ( proxyConfig ) ) {
163+ return proxyConfig . map ( ( config ) => getProxyConfig ( config ) ) as ClientProxyConfig ;
164+ }
165+
166+ if ( MOCKTTP_PARAM_REF in proxyConfig ) {
167+ return {
168+ [ RULE_PARAM_REF_KEY ] : ( proxyConfig as RuleParameterReference < ProxySettingSource > ) [ MOCKTTP_PARAM_REF ]
169+ } ;
170+ }
171+
172+ return proxyConfig as ProxySetting ;
173+ }
0 commit comments