Fossil SCM

Add the subpath field to the UrlData object. If that field is non-NULL, then the http_exchange() routine will build the request header using subpath rather than path. This allows the path for ssh: and file: to be distinct from the HTTP request path. Enhance the test-httpmsg command to work with file: and ssh: URLs as long as the new --subpath option is provided.

drh 2025-10-15 15:53 trunk
Commit 9abda297348385e9f6f7e389e4a418e16bb63db2f4e0147b880a0f26e362e16e
2 files changed +19 -4 +3
+19 -4
--- src/http.c
+++ src/http.c
@@ -141,15 +141,22 @@
141141
Blob *pHdr, /* construct the header here */
142142
Blob *pLogin, /* Login card header value or NULL */
143143
const char *zAltMimetype /* Alternative mimetype */
144144
){
145145
int nPayload = pPayload ? blob_size(pPayload) : 0;
146
+ const char *zPath;
146147
147148
blob_zero(pHdr);
149
+ if( g.url.subpath ){
150
+ zPath = g.url.subpath;
151
+ }else if( g.url.path==0 || g.url.path[0]==0 ){
152
+ zPath = "/";
153
+ }else{
154
+ zPath = g.url.path;
155
+ }
148156
blob_appendf(pHdr, "%s %s HTTP/1.0\r\n",
149
- nPayload>0 ? "POST" : "GET",
150
- (g.url.path && g.url.path[0]) ? g.url.path : "/");
157
+ nPayload>0 ? "POST" : "GET", zPath);
151158
if( g.url.proxyAuth ){
152159
blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
153160
}
154161
if( g.zHttpAuth && g.zHttpAuth[0] ){
155162
const char *zCredentials = g.zHttpAuth;
@@ -459,10 +466,11 @@
459466
/* Activate the PATH= auxiliary argument to the ssh command if that
460467
** is called for.
461468
*/
462469
if( g.url.isSsh
463470
&& (g.url.flags & URL_SSH_RETRY)==0
471
+ && g.db!=0
464472
&& ssh_needs_path_argument(g.url.hostname, -1)
465473
){
466474
g.url.flags |= URL_SSH_PATH;
467475
}
468476
@@ -686,11 +694,11 @@
686694
g.url.hostname,
687695
(g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with"
688696
);
689697
g.url.flags ^= URL_SSH_PATH|URL_SSH_RETRY;
690698
rc = http_exchange(pSend,pReply,mHttpFlags,0,zAltMimetype);
691
- if( rc==0 ){
699
+ if( rc==0 && g.db!=0 ){
692700
(void)ssh_needs_path_argument(g.url.hostname,
693701
(g.url.flags & URL_SSH_PATH)!=0);
694702
}
695703
return rc;
696704
}else{
@@ -808,17 +816,19 @@
808816
** Options:
809817
** --compress Use ZLIB compression on the payload
810818
** --mimetype TYPE Mimetype of the payload
811819
** --no-cert-verify Disable TLS cert verification
812820
** --out FILE Store the reply in FILE
821
+** --subpath PATH HTTP request path for ssh: and file: URLs
813822
** -v Verbose output
814823
** --xfer PAYLOAD in a Fossil xfer protocol message
815824
*/
816825
void test_httpmsg_command(void){
817826
const char *zMimetype;
818827
const char *zInFile;
819828
const char *zOutFile;
829
+ const char *zSubpath;
820830
Blob in, out;
821831
unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
822832
823833
zMimetype = find_option("mimetype",0,1);
824834
zOutFile = find_option("out","o",1);
@@ -832,10 +842,11 @@
832842
if( find_option("xfer",0,0)!=0 ){
833843
mHttpFlags |= HTTP_USE_LOGIN;
834844
mHttpFlags &= ~HTTP_GENERIC;
835845
}
836846
if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
847
+ zSubpath = find_option("subpath",0,1);
837848
verify_all_options();
838849
if( g.argc<3 || g.argc>5 ){
839850
usage("URL ?PAYLOAD? ?OUTPUT?");
840851
}
841852
zInFile = g.argc>=4 ? g.argv[3] : 0;
@@ -846,11 +857,15 @@
846857
}
847858
zOutFile = g.argv[4];
848859
}
849860
url_parse(g.argv[2], 0);
850861
if( g.url.protocol[0]!='h' ){
851
- fossil_fatal("the %s command supports only http: and https:", g.argv[1]);
862
+ if( zSubpath==0 ){
863
+ fossil_fatal("the --subpath option is required for %s://",g.url.protocol);
864
+ }else{
865
+ g.url.subpath = fossil_strdup(zSubpath);
866
+ }
852867
}
853868
if( zInFile ){
854869
blob_read_from_file(&in, zInFile, ExtFILE);
855870
if( zMimetype==0 && (mHttpFlags & HTTP_GENERIC)!=0 ){
856871
if( fossil_strcmp(zInFile,"-")==0 ){
857872
--- src/http.c
+++ src/http.c
@@ -141,15 +141,22 @@
141 Blob *pHdr, /* construct the header here */
142 Blob *pLogin, /* Login card header value or NULL */
143 const char *zAltMimetype /* Alternative mimetype */
144 ){
145 int nPayload = pPayload ? blob_size(pPayload) : 0;
 
146
147 blob_zero(pHdr);
 
 
 
 
 
 
 
148 blob_appendf(pHdr, "%s %s HTTP/1.0\r\n",
149 nPayload>0 ? "POST" : "GET",
150 (g.url.path && g.url.path[0]) ? g.url.path : "/");
151 if( g.url.proxyAuth ){
152 blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
153 }
154 if( g.zHttpAuth && g.zHttpAuth[0] ){
155 const char *zCredentials = g.zHttpAuth;
@@ -459,10 +466,11 @@
459 /* Activate the PATH= auxiliary argument to the ssh command if that
460 ** is called for.
461 */
462 if( g.url.isSsh
463 && (g.url.flags & URL_SSH_RETRY)==0
 
464 && ssh_needs_path_argument(g.url.hostname, -1)
465 ){
466 g.url.flags |= URL_SSH_PATH;
467 }
468
@@ -686,11 +694,11 @@
686 g.url.hostname,
687 (g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with"
688 );
689 g.url.flags ^= URL_SSH_PATH|URL_SSH_RETRY;
690 rc = http_exchange(pSend,pReply,mHttpFlags,0,zAltMimetype);
691 if( rc==0 ){
692 (void)ssh_needs_path_argument(g.url.hostname,
693 (g.url.flags & URL_SSH_PATH)!=0);
694 }
695 return rc;
696 }else{
@@ -808,17 +816,19 @@
808 ** Options:
809 ** --compress Use ZLIB compression on the payload
810 ** --mimetype TYPE Mimetype of the payload
811 ** --no-cert-verify Disable TLS cert verification
812 ** --out FILE Store the reply in FILE
 
813 ** -v Verbose output
814 ** --xfer PAYLOAD in a Fossil xfer protocol message
815 */
816 void test_httpmsg_command(void){
817 const char *zMimetype;
818 const char *zInFile;
819 const char *zOutFile;
 
820 Blob in, out;
821 unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
822
823 zMimetype = find_option("mimetype",0,1);
824 zOutFile = find_option("out","o",1);
@@ -832,10 +842,11 @@
832 if( find_option("xfer",0,0)!=0 ){
833 mHttpFlags |= HTTP_USE_LOGIN;
834 mHttpFlags &= ~HTTP_GENERIC;
835 }
836 if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
 
837 verify_all_options();
838 if( g.argc<3 || g.argc>5 ){
839 usage("URL ?PAYLOAD? ?OUTPUT?");
840 }
841 zInFile = g.argc>=4 ? g.argv[3] : 0;
@@ -846,11 +857,15 @@
846 }
847 zOutFile = g.argv[4];
848 }
849 url_parse(g.argv[2], 0);
850 if( g.url.protocol[0]!='h' ){
851 fossil_fatal("the %s command supports only http: and https:", g.argv[1]);
 
 
 
 
852 }
853 if( zInFile ){
854 blob_read_from_file(&in, zInFile, ExtFILE);
855 if( zMimetype==0 && (mHttpFlags & HTTP_GENERIC)!=0 ){
856 if( fossil_strcmp(zInFile,"-")==0 ){
857
--- src/http.c
+++ src/http.c
@@ -141,15 +141,22 @@
141 Blob *pHdr, /* construct the header here */
142 Blob *pLogin, /* Login card header value or NULL */
143 const char *zAltMimetype /* Alternative mimetype */
144 ){
145 int nPayload = pPayload ? blob_size(pPayload) : 0;
146 const char *zPath;
147
148 blob_zero(pHdr);
149 if( g.url.subpath ){
150 zPath = g.url.subpath;
151 }else if( g.url.path==0 || g.url.path[0]==0 ){
152 zPath = "/";
153 }else{
154 zPath = g.url.path;
155 }
156 blob_appendf(pHdr, "%s %s HTTP/1.0\r\n",
157 nPayload>0 ? "POST" : "GET", zPath);
 
158 if( g.url.proxyAuth ){
159 blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
160 }
161 if( g.zHttpAuth && g.zHttpAuth[0] ){
162 const char *zCredentials = g.zHttpAuth;
@@ -459,10 +466,11 @@
466 /* Activate the PATH= auxiliary argument to the ssh command if that
467 ** is called for.
468 */
469 if( g.url.isSsh
470 && (g.url.flags & URL_SSH_RETRY)==0
471 && g.db!=0
472 && ssh_needs_path_argument(g.url.hostname, -1)
473 ){
474 g.url.flags |= URL_SSH_PATH;
475 }
476
@@ -686,11 +694,11 @@
694 g.url.hostname,
695 (g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with"
696 );
697 g.url.flags ^= URL_SSH_PATH|URL_SSH_RETRY;
698 rc = http_exchange(pSend,pReply,mHttpFlags,0,zAltMimetype);
699 if( rc==0 && g.db!=0 ){
700 (void)ssh_needs_path_argument(g.url.hostname,
701 (g.url.flags & URL_SSH_PATH)!=0);
702 }
703 return rc;
704 }else{
@@ -808,17 +816,19 @@
816 ** Options:
817 ** --compress Use ZLIB compression on the payload
818 ** --mimetype TYPE Mimetype of the payload
819 ** --no-cert-verify Disable TLS cert verification
820 ** --out FILE Store the reply in FILE
821 ** --subpath PATH HTTP request path for ssh: and file: URLs
822 ** -v Verbose output
823 ** --xfer PAYLOAD in a Fossil xfer protocol message
824 */
825 void test_httpmsg_command(void){
826 const char *zMimetype;
827 const char *zInFile;
828 const char *zOutFile;
829 const char *zSubpath;
830 Blob in, out;
831 unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
832
833 zMimetype = find_option("mimetype",0,1);
834 zOutFile = find_option("out","o",1);
@@ -832,10 +842,11 @@
842 if( find_option("xfer",0,0)!=0 ){
843 mHttpFlags |= HTTP_USE_LOGIN;
844 mHttpFlags &= ~HTTP_GENERIC;
845 }
846 if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
847 zSubpath = find_option("subpath",0,1);
848 verify_all_options();
849 if( g.argc<3 || g.argc>5 ){
850 usage("URL ?PAYLOAD? ?OUTPUT?");
851 }
852 zInFile = g.argc>=4 ? g.argv[3] : 0;
@@ -846,11 +857,15 @@
857 }
858 zOutFile = g.argv[4];
859 }
860 url_parse(g.argv[2], 0);
861 if( g.url.protocol[0]!='h' ){
862 if( zSubpath==0 ){
863 fossil_fatal("the --subpath option is required for %s://",g.url.protocol);
864 }else{
865 g.url.subpath = fossil_strdup(zSubpath);
866 }
867 }
868 if( zInFile ){
869 blob_read_from_file(&in, zInFile, ExtFILE);
870 if( zMimetype==0 && (mHttpFlags & HTTP_GENERIC)!=0 ){
871 if( fossil_strcmp(zInFile,"-")==0 ){
872
+3
--- src/url.c
+++ src/url.c
@@ -58,10 +58,11 @@
5858
char *user; /* User id for http: */
5959
char *passwd; /* Password for http: */
6060
char *canonical; /* Canonical representation of the URL */
6161
char *proxyAuth; /* Proxy-Authorizer: string */
6262
char *fossil; /* The fossil query parameter on ssh: */
63
+ char *subpath; /* Secondary HTTP request path for ssh: and file: */
6364
char *pwConfig; /* CONFIG table entry that gave us the password */
6465
unsigned flags; /* Boolean flags controlling URL processing */
6566
int useProxy; /* Used to remember that a proxy is in use */
6667
int proxyOrigPort; /* Tunneled port number for https through proxy */
6768
char *proxyUrlPath; /* Remember path when proxy is use */
@@ -406,10 +407,11 @@
406407
fossil_free(p->name);
407408
fossil_free(p->path);
408409
fossil_free(p->user);
409410
fossil_free(p->passwd);
410411
fossil_free(p->fossil);
412
+ fossil_free(p->subpath);
411413
fossil_free(p->pwConfig);
412414
memset(p, 0, sizeof(*p));
413415
}
414416
415417
/*
@@ -483,10 +485,11 @@
483485
fossil_print("g.url.passwd = ************\n");
484486
}
485487
fossil_print("g.url.pwConfig = %s\n", g.url.pwConfig);
486488
fossil_print("g.url.canonical = %s\n", g.url.canonical);
487489
fossil_print("g.url.fossil = %s\n", g.url.fossil);
490
+ fossil_print("g.url.subpath = %s\n", g.url.subpath);
488491
fossil_print("g.url.flags = 0x%04x\n", g.url.flags);
489492
fossil_print("url_full(g.url) = %z\n", url_full(&g.url));
490493
}
491494
492495
/*
493496
--- src/url.c
+++ src/url.c
@@ -58,10 +58,11 @@
58 char *user; /* User id for http: */
59 char *passwd; /* Password for http: */
60 char *canonical; /* Canonical representation of the URL */
61 char *proxyAuth; /* Proxy-Authorizer: string */
62 char *fossil; /* The fossil query parameter on ssh: */
 
63 char *pwConfig; /* CONFIG table entry that gave us the password */
64 unsigned flags; /* Boolean flags controlling URL processing */
65 int useProxy; /* Used to remember that a proxy is in use */
66 int proxyOrigPort; /* Tunneled port number for https through proxy */
67 char *proxyUrlPath; /* Remember path when proxy is use */
@@ -406,10 +407,11 @@
406 fossil_free(p->name);
407 fossil_free(p->path);
408 fossil_free(p->user);
409 fossil_free(p->passwd);
410 fossil_free(p->fossil);
 
411 fossil_free(p->pwConfig);
412 memset(p, 0, sizeof(*p));
413 }
414
415 /*
@@ -483,10 +485,11 @@
483 fossil_print("g.url.passwd = ************\n");
484 }
485 fossil_print("g.url.pwConfig = %s\n", g.url.pwConfig);
486 fossil_print("g.url.canonical = %s\n", g.url.canonical);
487 fossil_print("g.url.fossil = %s\n", g.url.fossil);
 
488 fossil_print("g.url.flags = 0x%04x\n", g.url.flags);
489 fossil_print("url_full(g.url) = %z\n", url_full(&g.url));
490 }
491
492 /*
493
--- src/url.c
+++ src/url.c
@@ -58,10 +58,11 @@
58 char *user; /* User id for http: */
59 char *passwd; /* Password for http: */
60 char *canonical; /* Canonical representation of the URL */
61 char *proxyAuth; /* Proxy-Authorizer: string */
62 char *fossil; /* The fossil query parameter on ssh: */
63 char *subpath; /* Secondary HTTP request path for ssh: and file: */
64 char *pwConfig; /* CONFIG table entry that gave us the password */
65 unsigned flags; /* Boolean flags controlling URL processing */
66 int useProxy; /* Used to remember that a proxy is in use */
67 int proxyOrigPort; /* Tunneled port number for https through proxy */
68 char *proxyUrlPath; /* Remember path when proxy is use */
@@ -406,10 +407,11 @@
407 fossil_free(p->name);
408 fossil_free(p->path);
409 fossil_free(p->user);
410 fossil_free(p->passwd);
411 fossil_free(p->fossil);
412 fossil_free(p->subpath);
413 fossil_free(p->pwConfig);
414 memset(p, 0, sizeof(*p));
415 }
416
417 /*
@@ -483,10 +485,11 @@
485 fossil_print("g.url.passwd = ************\n");
486 }
487 fossil_print("g.url.pwConfig = %s\n", g.url.pwConfig);
488 fossil_print("g.url.canonical = %s\n", g.url.canonical);
489 fossil_print("g.url.fossil = %s\n", g.url.fossil);
490 fossil_print("g.url.subpath = %s\n", g.url.subpath);
491 fossil_print("g.url.flags = 0x%04x\n", g.url.flags);
492 fossil_print("url_full(g.url) = %z\n", url_full(&g.url));
493 }
494
495 /*
496

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button